From ebb0bc5ff05fa82866531bf7897e2c0c2751a310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jir=CC=8Ci=CC=81=20Ota=CC=81hal?= Date: Wed, 27 Jun 2018 11:12:48 +1200 Subject: [PATCH] [Breaking Change] Use React.createContext --- package.json | 3 +- src/ActionButton/ActionButton.react.js | 17 ++++---- src/Avatar/Avatar.react.js | 24 ++++++------ src/Badge/Badge.react.js | 15 +++---- .../BottomNavigation.react.js | 14 +++---- .../BottomNavigationAction.react.js | 11 ++---- src/Button/Button.react.js | 24 +++++------- src/Card/Card.react.js | 15 +++---- src/Checkbox/Checkbox.react.js | 18 ++++----- src/Dialog/Actions.react.js | 11 ++---- src/Dialog/Content.react.js | 11 ++---- src/Dialog/Dialog.react.js | 13 +++---- src/Dialog/DialogDefaultActions.react.js | 13 +++---- src/Dialog/DialogStackedActions.react.js | 13 +++---- src/Dialog/Title.react.js | 13 +++---- src/Divider/Divider.react.js | 11 ++---- src/Drawer/Drawer.react.js | 13 +++---- src/Drawer/Header.react.js | 15 +++---- src/Drawer/HeaderAccount.react.js | 22 +++++------ src/Drawer/Section.react.js | 24 ++++++------ src/Icon/index.js | 16 ++++---- src/IconToggle/IconToggle.react.js | 21 +++++----- src/ListItem/ListItem.react.js | 15 +++---- src/Snackbar/Snackbar.react.js | 24 ++++++------ src/Subheader/Subheader.react.js | 13 +++---- src/Toolbar/CenterElement.react.js | 15 +++---- src/Toolbar/LeftElement.react.js | 15 +++---- src/Toolbar/RightElement.react.js | 13 +++---- src/Toolbar/Toolbar.react.js | 33 ++++++++-------- src/index.js | 4 +- src/styles/ThemeProvider.react.js | 39 ------------------- src/styles/themeContext.js | 4 ++ src/styles/withTheme.js | 23 +++++++++++ yarn.lock | 4 ++ 34 files changed, 235 insertions(+), 304 deletions(-) delete mode 100644 src/styles/ThemeProvider.react.js create mode 100644 src/styles/themeContext.js create mode 100644 src/styles/withTheme.js diff --git a/package.json b/package.json index b22edc89..98f7d06d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-material-ui", - "version": "1.22.4", + "version": "1.30.0", "description": "React Native Material Design Components", "main": "./index.js", "scripts": { @@ -35,6 +35,7 @@ }, "dependencies": { "color": "3.0.0", + "hoist-non-react-statics": "^2.5.5", "lodash.merge": "^4.0.0", "prop-types": "^15.5.10", "react-native-material-design-styles": "^0.2.6" diff --git a/src/ActionButton/ActionButton.react.js b/src/ActionButton/ActionButton.react.js index 21641d99..6a8a1186 100755 --- a/src/ActionButton/ActionButton.react.js +++ b/src/ActionButton/ActionButton.react.js @@ -18,6 +18,7 @@ import RippleFeedback from '../RippleFeedback'; /* eslint-disable import/no-unresolved, import/extensions */ import getPlatformElevation from '../styles/getPlatformElevation'; /* eslint-enable import/no-unresolved, import/extensions */ +import withTheme from '../styles/withTheme'; function darkenOrLighten(color, ratio = 0.5) { const c = Color(color); @@ -83,13 +84,10 @@ const defaultProps = { hidden: false, rippleColor: null, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context, state) { - const { actionButton } = context.uiTheme; - const { size } = props; +function getStyles(props, state) { + const { size, theme } = props; + const { actionButton } = theme; const local = { container: {}, @@ -230,7 +228,7 @@ class ActionButton extends PureComponent { return rippleColor; } - const styles = getStyles(this.props, this.context, this.state); + const styles = getStyles(this.props, this.state); const { backgroundColor } = StyleSheet.flatten(styles.container); @@ -471,7 +469,7 @@ class ActionButton extends PureComponent { render() { const { render } = this.state; - const styles = getStyles(this.props, this.context, this.state); + const styles = getStyles(this.props, this.state); if (render === 'toolbar') { return this.renderToolbarTransition(styles); @@ -486,6 +484,5 @@ class ActionButton extends PureComponent { ActionButton.propTypes = propTypes; ActionButton.defaultProps = defaultProps; -ActionButton.contextTypes = contextTypes; -export default ActionButton; +export default withTheme(ActionButton); diff --git a/src/Avatar/Avatar.react.js b/src/Avatar/Avatar.react.js index e7779547..322f236c 100644 --- a/src/Avatar/Avatar.react.js +++ b/src/Avatar/Avatar.react.js @@ -5,6 +5,7 @@ import { View, Image, Text, StyleSheet } from 'react-native'; import { ViewPropTypes } from '../utils'; /* eslint-enable import/no-unresolved, import/extensions */ import Icon from '../Icon'; +import withTheme from '../styles/withTheme'; const propTypes = { /** @@ -38,6 +39,10 @@ const propTypes = { container: ViewPropTypes.style, content: Text.propTypes.style, // eslint-disable-line }), + /** + * Theme + */ + theme: PropTypes.any, // eslint-disable-line }; const defaultProps = { image: null, @@ -48,13 +53,10 @@ const defaultProps = { size: 48, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { avatar } = context.uiTheme; - const { size } = props; +function getStyles(props) { + const { size, theme } = props; + const { avatar } = theme; const local = {}; @@ -74,13 +76,12 @@ function getStyles(props, context) { class Avatar extends PureComponent { render() { - const { image, icon, iconSize, iconColor, text } = this.props; - const { uiTheme } = this.context; - const { avatar, spacing } = uiTheme; + const { image, icon, iconSize, iconColor, text, theme } = this.props; + const { avatar, spacing } = theme; let content = null; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); if (icon) { const color = iconColor || StyleSheet.flatten(avatar.content).color; @@ -102,6 +103,5 @@ class Avatar extends PureComponent { Avatar.propTypes = propTypes; Avatar.defaultProps = defaultProps; -Avatar.contextTypes = contextTypes; -export default Avatar; +export default withTheme(Avatar); diff --git a/src/Badge/Badge.react.js b/src/Badge/Badge.react.js index eb1c8f6b..05a9ae59 100644 --- a/src/Badge/Badge.react.js +++ b/src/Badge/Badge.react.js @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import Icon from '../Icon'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { /** @@ -59,13 +60,10 @@ const defaultProps = { }, }, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { badge, palette } = context.uiTheme; - const { accent, size, stroke } = props; +function getStyles(props) { + const { accent, size, stroke, theme } = props; + const { badge, palette } = theme; const local = { container: {}, @@ -160,7 +158,7 @@ class Badge extends PureComponent { } render() { - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); return ( @@ -173,6 +171,5 @@ class Badge extends PureComponent { Badge.propTypes = propTypes; Badge.defaultProps = defaultProps; -Badge.contextTypes = contextTypes; -export default Badge; +export default withTheme(Badge); diff --git a/src/BottomNavigation/BottomNavigation.react.js b/src/BottomNavigation/BottomNavigation.react.js index 08e2b470..d41c53b0 100644 --- a/src/BottomNavigation/BottomNavigation.react.js +++ b/src/BottomNavigation/BottomNavigation.react.js @@ -5,6 +5,8 @@ import { View, Animated, Easing, StyleSheet } from 'react-native'; import { ViewPropTypes } from '../utils'; /* eslint-enable import/no-unresolved, import/extensions */ +import withTheme from '../styles/withTheme'; + import BottomNavigationAction from './BottomNavigationAction.react'; const propTypes = { @@ -32,12 +34,9 @@ const defaultProps = { hidden: false, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { bottomNavigation } = context.uiTheme; +function getStyles(props) { + const { bottomNavigation } = props.theme; const local = {}; return { @@ -131,8 +130,9 @@ class BottomNavigation extends PureComponent { BottomNavigation.propTypes = propTypes; BottomNavigation.defaultProps = defaultProps; -BottomNavigation.contextTypes = contextTypes; BottomNavigation.Action = BottomNavigationAction; -export default BottomNavigation; +const ThemedBottomNavigation = withTheme(BottomNavigation); + +export default ThemedBottomNavigation; diff --git a/src/BottomNavigation/BottomNavigationAction.react.js b/src/BottomNavigation/BottomNavigationAction.react.js index 76ca08d4..d75e59c3 100644 --- a/src/BottomNavigation/BottomNavigationAction.react.js +++ b/src/BottomNavigation/BottomNavigationAction.react.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import { StyleSheet, View, Text } from 'react-native'; import { ViewPropTypes } from '../utils'; /* eslint-enable import/no-unresolved, import/extensions */ +import withTheme from '../styles/withTheme'; import RippleFeedback from '../RippleFeedback'; import Icon from '../Icon'; @@ -46,12 +47,9 @@ const defaultProps = { disabled: false, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { bottomNavigationAction } = context.uiTheme; +function getStyles(props) { + const { bottomNavigationAction } = props.theme; const local = {}; @@ -120,6 +118,5 @@ class BottomNavigationAction extends PureComponent { BottomNavigationAction.propTypes = propTypes; BottomNavigationAction.defaultProps = defaultProps; -BottomNavigationAction.contextTypes = contextTypes; -export default BottomNavigationAction; +export default withTheme(BottomNavigationAction); diff --git a/src/Button/Button.react.js b/src/Button/Button.react.js index 15a43e3c..13fd34c1 100755 --- a/src/Button/Button.react.js +++ b/src/Button/Button.react.js @@ -9,6 +9,7 @@ import RippleFeedback from '../RippleFeedback'; /* eslint-disable import/no-unresolved, import/extensions */ import getPlatformElevation from '../styles/getPlatformElevation'; /* eslint-enable import/no-unresolved, import/extensions */ +import withTheme from '../styles/withTheme'; const propTypes = { testID: PropTypes.string, @@ -62,21 +63,17 @@ const defaultProps = { upperCase: true, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context, state) { +function getStyles(props, state) { + const { primary, accent, disabled, raised, theme } = props; const { button, buttonFlat, buttonRaised, buttonDisabled, buttonRaisedDisabled, - } = context.uiTheme; - - const { primary, accent, disabled, raised } = props; - const { palette } = context.uiTheme; + palette, + } = theme; const local = { container: {}, @@ -139,7 +136,7 @@ class Button extends PureComponent { constructor(props) { super(props); this.state = { - elevation: 2, // eslint-disable-line + elevation: 2, // eslint-disable-line }; } @@ -153,13 +150,13 @@ class Button extends PureComponent { setElevation = () => { this.setState({ - elevation: 4, // eslint-disable-line + elevation: 4, // eslint-disable-line }); }; removeElevation = () => { this.setState({ - elevation: 2, // eslint-disable-line + elevation: 2, // eslint-disable-line }); }; @@ -199,7 +196,7 @@ class Button extends PureComponent { testID, } = this.props; - const styles = getStyles(this.props, this.context, this.state); + const styles = getStyles(this.props, this.state); const content = ( @@ -229,6 +226,5 @@ class Button extends PureComponent { Button.propTypes = propTypes; Button.defaultProps = defaultProps; -Button.contextTypes = contextTypes; -export default Button; +export default withTheme(Button); diff --git a/src/Card/Card.react.js b/src/Card/Card.react.js index 0e2119b6..ce18ee34 100644 --- a/src/Card/Card.react.js +++ b/src/Card/Card.react.js @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import RippleFeedback from '../RippleFeedback'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { children: PropTypes.node, @@ -18,12 +19,9 @@ const defaultProps = { onPress: null, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { card } = context.uiTheme; +function getStyles(props) { + const { card } = props.theme; const local = {}; @@ -43,12 +41,12 @@ class Card extends PureComponent { super(props, context); this.state = { - styles: getStyles(props, context), + styles: getStyles(props), }; } componentWillReceiveProps(nextProps) { - this.setState({ styles: getStyles(nextProps, this.context) }); + this.setState({ styles: getStyles(nextProps) }); } renderContent = () => { @@ -79,6 +77,5 @@ class Card extends PureComponent { Card.propTypes = propTypes; Card.defaultProps = defaultProps; -Card.contextTypes = contextTypes; -export default Card; +export default withTheme(Card); diff --git a/src/Checkbox/Checkbox.react.js b/src/Checkbox/Checkbox.react.js index 4548a377..c62911b7 100644 --- a/src/Checkbox/Checkbox.react.js +++ b/src/Checkbox/Checkbox.react.js @@ -6,6 +6,7 @@ import PropTypes from 'prop-types'; import IconToggle from '../IconToggle'; import RippleFeedback from '../RippleFeedback'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { /** @@ -38,7 +39,8 @@ const propTypes = { onCheck: PropTypes.func.isRequired, style: PropTypes.shape({ container: ViewPropTypes.style, - icon: IconToggle.propTypes.style, // eslint-disable-line + // FIXME: + icon: PropTypes.any, // eslint-disable-line label: Text.propTypes.style, // eslint-disable-line }), /** @@ -54,13 +56,10 @@ const defaultProps = { style: {}, size: 24, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { checkbox, palette } = context.uiTheme; - const { disabled } = props; +function getStyles(props) { + const { disabled, theme } = props; + const { checkbox, palette } = theme; const local = {}; @@ -97,7 +96,7 @@ class Checkbox extends PureComponent { label, } = this.props; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); const labelColor = StyleSheet.flatten(styles.label).color; const iconColor = StyleSheet.flatten(styles.icon).color; @@ -126,6 +125,5 @@ class Checkbox extends PureComponent { Checkbox.propTypes = propTypes; Checkbox.defaultProps = defaultProps; -Checkbox.contextTypes = contextTypes; -export default Checkbox; +export default withTheme(Checkbox); diff --git a/src/Dialog/Actions.react.js b/src/Dialog/Actions.react.js index f3ef2050..b22fa5ad 100644 --- a/src/Dialog/Actions.react.js +++ b/src/Dialog/Actions.react.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { children: PropTypes.node.isRequired, @@ -14,12 +15,9 @@ const propTypes = { const defaultProps = { style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { dialog } = context.uiTheme; +function getStyles(props) { + const { dialog } = props.theme; return { actionsContainer: [dialog.actionsContainer, props.style.actionsContainer], @@ -38,6 +36,5 @@ class DialogFooter extends PureComponent { DialogFooter.propTypes = propTypes; DialogFooter.defaultProps = defaultProps; -DialogFooter.contextTypes = contextTypes; -export default DialogFooter; +export default withTheme(DialogFooter); diff --git a/src/Dialog/Content.react.js b/src/Dialog/Content.react.js index 8c595ca8..67372234 100644 --- a/src/Dialog/Content.react.js +++ b/src/Dialog/Content.react.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { children: PropTypes.node.isRequired, @@ -14,12 +15,9 @@ const propTypes = { const defaultProps = { style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { dialog } = context.uiTheme; +function getStyles(props) { + const { dialog } = props.theme; return { contentContainer: [dialog.contentContainer, props.style.contentContainer], @@ -38,6 +36,5 @@ class DialogContent extends PureComponent { DialogContent.propTypes = propTypes; DialogContent.defaultProps = defaultProps; -DialogContent.contextTypes = contextTypes; -export default DialogContent; +export default withTheme(DialogContent); diff --git a/src/Dialog/Dialog.react.js b/src/Dialog/Dialog.react.js index d4bb8d54..76bcc67f 100644 --- a/src/Dialog/Dialog.react.js +++ b/src/Dialog/Dialog.react.js @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import RippleFeedback from '../RippleFeedback'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; import Title from './Title.react'; import Content from './Content.react'; @@ -21,12 +22,9 @@ const defaultProps = { onPress: null, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { dialog } = context.uiTheme; +function getStyles(props) { + const { dialog } = props.theme; return { container: [dialog.container, props.style.container], @@ -36,7 +34,7 @@ function getStyles(props, context) { class Dialog extends PureComponent { renderContent = () => { const { children } = this.props; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); return ( @@ -62,10 +60,9 @@ class Dialog extends PureComponent { Dialog.propTypes = propTypes; Dialog.defaultProps = defaultProps; -Dialog.contextTypes = contextTypes; Dialog.Title = Title; Dialog.Content = Content; Dialog.Actions = Actions; -export default Dialog; +export default withTheme(Dialog); diff --git a/src/Dialog/DialogDefaultActions.react.js b/src/Dialog/DialogDefaultActions.react.js index 96585ec1..7b69b828 100644 --- a/src/Dialog/DialogDefaultActions.react.js +++ b/src/Dialog/DialogDefaultActions.react.js @@ -5,6 +5,7 @@ import { View } from 'react-native'; /* eslint-enable import/no-unresolved, import/extensions */ import Button from '../Button'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { actions: PropTypes.arrayOf(PropTypes.string).isRequired, @@ -18,12 +19,9 @@ const defaultProps = { style: {}, options: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { dialog } = context.uiTheme; +function getStyles(props) { + const { dialog } = props.theme; return { defaultActionsContainer: [ @@ -72,7 +70,7 @@ class DialogDefaultActions extends PureComponent { render() { const { actions } = this.props; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); return ( @@ -84,6 +82,5 @@ class DialogDefaultActions extends PureComponent { DialogDefaultActions.propTypes = propTypes; DialogDefaultActions.defaultProps = defaultProps; -DialogDefaultActions.contextTypes = contextTypes; -export default DialogDefaultActions; +export default withTheme(DialogDefaultActions); diff --git a/src/Dialog/DialogStackedActions.react.js b/src/Dialog/DialogStackedActions.react.js index 952b67d0..084630f3 100644 --- a/src/Dialog/DialogStackedActions.react.js +++ b/src/Dialog/DialogStackedActions.react.js @@ -5,6 +5,7 @@ import { View } from 'react-native'; /* eslint-enable import/no-unresolved, import/extensions */ import Button from '../Button'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { actions: PropTypes.arrayOf(PropTypes.string).isRequired, @@ -18,12 +19,9 @@ const defaultProps = { style: {}, options: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { dialog } = context.uiTheme; +function getStyles(props) { + const { dialog } = props.theme; return { stackedActionsContainer: [ @@ -58,7 +56,7 @@ class DialogStackedActions extends PureComponent { render() { const { actions } = this.props; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); return ( @@ -70,6 +68,5 @@ class DialogStackedActions extends PureComponent { DialogStackedActions.propTypes = propTypes; DialogStackedActions.defaultProps = defaultProps; -DialogStackedActions.contextTypes = contextTypes; -export default DialogStackedActions; +export default withTheme(DialogStackedActions); diff --git a/src/Dialog/Title.react.js b/src/Dialog/Title.react.js index 8854828c..1c9076a0 100644 --- a/src/Dialog/Title.react.js +++ b/src/Dialog/Title.react.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { children: PropTypes.node.isRequired, @@ -15,12 +16,9 @@ const propTypes = { const defaultProps = { style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { dialog } = context.uiTheme; +function getStyles(props) { + const { dialog } = props.theme; return { titleContainer: [dialog.titleContainer, props.style.titleContainer], @@ -32,7 +30,7 @@ class DialogHeader extends PureComponent { render() { const { children } = this.props; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); return ( @@ -44,6 +42,5 @@ class DialogHeader extends PureComponent { DialogHeader.propTypes = propTypes; DialogHeader.defaultProps = defaultProps; -DialogHeader.contextTypes = contextTypes; -export default DialogHeader; +export default withTheme(DialogHeader); diff --git a/src/Divider/Divider.react.js b/src/Divider/Divider.react.js index 5403ad36..908530fa 100644 --- a/src/Divider/Divider.react.js +++ b/src/Divider/Divider.react.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { inset: PropTypes.bool, @@ -15,12 +16,9 @@ const defaultProps = { inset: false, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { divider } = context.uiTheme; +function getStyles(props) { + const { divider } = props.theme; const local = { container: props.inset ? { marginLeft: 72 } : null, @@ -41,6 +39,5 @@ class Divider extends PureComponent { Divider.propTypes = propTypes; Divider.defaultProps = defaultProps; -Divider.contextTypes = contextTypes; -export default Divider; +export default withTheme(Divider); diff --git a/src/Drawer/Drawer.react.js b/src/Drawer/Drawer.react.js index 962a7e33..d8a9b295 100644 --- a/src/Drawer/Drawer.react.js +++ b/src/Drawer/Drawer.react.js @@ -5,6 +5,7 @@ import { ScrollView } from 'react-native'; import { ViewPropTypes } from '../utils'; /* eslint-enable import/no-unresolved, import/extensions */ import Container from '../Container'; +import withTheme from '../styles/withTheme'; import Header from './Header.react'; import Section from './Section.react'; @@ -18,12 +19,9 @@ const propTypes = { const defaultProps = { style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { drawer } = context.uiTheme; +function getStyles(props) { + const { drawer } = props.theme; return { container: [drawer.container, props.style.container], @@ -34,7 +32,7 @@ class Drawer extends PureComponent { render() { const { children } = this.props; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); return ( @@ -46,9 +44,8 @@ class Drawer extends PureComponent { Drawer.propTypes = propTypes; Drawer.defaultProps = defaultProps; -Drawer.contextTypes = contextTypes; Drawer.Header = Header; Drawer.Section = Section; -export default Drawer; +export default withTheme(Drawer); diff --git a/src/Drawer/Header.react.js b/src/Drawer/Header.react.js index e48c7b8c..ad1559c3 100644 --- a/src/Drawer/Header.react.js +++ b/src/Drawer/Header.react.js @@ -5,6 +5,7 @@ import { View, Image, StyleSheet } from 'react-native'; /* eslint-enable import/no-unresolved, import/extensions */ import HeaderAccount from './HeaderAccount.react'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { image: PropTypes.shape({ type: PropTypes.oneOf([Image]) }), @@ -21,13 +22,10 @@ const defaultProps = { children: null, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { drawerHeader } = context.uiTheme; - const { image } = props; +function getStyles(props) { + const { image, theme } = props; + const { drawerHeader } = theme; const local = {}; @@ -51,7 +49,7 @@ class Header extends PureComponent { render() { const { image, children } = this.props; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); const flatten = StyleSheet.flatten(styles.contentContainer); const content = {children}; @@ -73,8 +71,7 @@ class Header extends PureComponent { Header.propTypes = propTypes; Header.defaultProps = defaultProps; -Header.contextTypes = contextTypes; Header.Account = HeaderAccount; -export default Header; +export default withTheme(Header); diff --git a/src/Drawer/HeaderAccount.react.js b/src/Drawer/HeaderAccount.react.js index 6be9894d..070a7ad5 100644 --- a/src/Drawer/HeaderAccount.react.js +++ b/src/Drawer/HeaderAccount.react.js @@ -5,6 +5,7 @@ import { View, TouchableWithoutFeedback } from 'react-native'; /* eslint-enable import/no-unresolved, import/extensions */ import ListItem from '../ListItem'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { avatar: PropTypes.element, @@ -23,6 +24,10 @@ const propTypes = { activeAvatarContainer: ViewPropTypes.style, inactiveAvatarContainer: ViewPropTypes.style, }), + /** + * Theme + */ + theme: PropTypes.any, // eslint-disable-line }; const defaultProps = { avatar: null, @@ -30,12 +35,9 @@ const defaultProps = { footer: null, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { drawerHeaderAccount } = context.uiTheme; +function getStyles(props) { + const { drawerHeaderAccount } = props.theme; return { container: [drawerHeaderAccount.container, props.style.container], @@ -63,13 +65,12 @@ class HeaderAcount extends PureComponent { componentWillMount = () => { // We need to change state if relevant props are changed this.setState({ - styles: getStyles(this.props, this.context), + styles: getStyles(this.props), }); }; renderFooter = () => { - const { footer } = this.props; - const { uiTheme } = this.context; + const { footer, theme } = this.props; if (!footer) { return null; @@ -77,7 +78,7 @@ class HeaderAcount extends PureComponent { const props = { ...footer, - style: uiTheme.drawerHeaderListItem, + style: theme.drawerHeaderListItem, }; return ; @@ -131,6 +132,5 @@ class HeaderAcount extends PureComponent { HeaderAcount.propTypes = propTypes; HeaderAcount.defaultProps = defaultProps; -HeaderAcount.contextTypes = contextTypes; -export default HeaderAcount; +export default withTheme(HeaderAcount); diff --git a/src/Drawer/Section.react.js b/src/Drawer/Section.react.js index 539acad6..d2956eaa 100644 --- a/src/Drawer/Section.react.js +++ b/src/Drawer/Section.react.js @@ -7,6 +7,7 @@ import Subheader from '../Subheader'; import Divider from '../Divider'; import ListItem from '../ListItem'; import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { title: PropTypes.string, @@ -32,6 +33,10 @@ const propTypes = { label: Text.propTypes.style, // eslint-disable-line }), key: PropTypes.string, + /** + * Theme + */ + theme: PropTypes.any, // eslint-disable-line }; const defaultProps = { title: null, @@ -40,12 +45,9 @@ const defaultProps = { style: {}, key: '', }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { drawerSection } = context.uiTheme; +function getStyles(props) { + const { drawerSection } = props.theme; return { container: [drawerSection.container, props.style.container], @@ -69,11 +71,10 @@ class Section extends PureComponent { }; render() { - const { items, divider, key } = this.props; - const { uiTheme } = this.context; - const { typography } = uiTheme; + const { items, divider, key, theme } = this.props; + const { typography } = theme; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); return ( @@ -83,7 +84,7 @@ class Section extends PureComponent { let style = { primaryText: typography.buttons }; if (item.active) { - style = uiTheme.drawerSectionActiveItem; + style = theme.drawerSectionActiveItem; } return ( @@ -106,6 +107,5 @@ class Section extends PureComponent { Section.propTypes = propTypes; Section.defaultProps = defaultProps; -Section.contextTypes = contextTypes; -export default Section; +export default withTheme(Section); diff --git a/src/Icon/index.js b/src/Icon/index.js index 9f2718fc..94d8f321 100644 --- a/src/Icon/index.js +++ b/src/Icon/index.js @@ -3,27 +3,28 @@ import VectorIcon from 'react-native-vector-icons/MaterialIcons'; import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ +import withTheme from '../styles/withTheme'; const propTypes = { name: PropTypes.string.isRequired, style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), size: PropTypes.number, color: PropTypes.string, + /** + * Theme + */ + theme: PropTypes.any, // eslint-disable-line }; const defaultProps = { size: null, color: null, style: null, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; class Icon extends PureComponent { render() { - const { name, style, size, color } = this.props; - const { uiTheme } = this.context; - const { palette, spacing } = uiTheme; + const { name, style, size, color, theme } = this.props; + const { palette, spacing } = theme; const iconColor = color || palette.secondaryTextColor; const iconSize = size || spacing.iconSize; @@ -36,6 +37,5 @@ class Icon extends PureComponent { Icon.propTypes = propTypes; Icon.defaultProps = defaultProps; -Icon.contextTypes = contextTypes; -export default Icon; +export default withTheme(Icon); diff --git a/src/IconToggle/IconToggle.react.js b/src/IconToggle/IconToggle.react.js index d03b3ed6..b2f6f9ad 100644 --- a/src/IconToggle/IconToggle.react.js +++ b/src/IconToggle/IconToggle.react.js @@ -13,6 +13,7 @@ import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import Color from 'color'; +import withTheme from '../styles/withTheme'; import { ViewPropTypes } from '../utils'; import { ELEVATION_ZINDEX } from '../styles/constants'; import Icon from '../Icon'; @@ -73,12 +74,9 @@ const defaultProps = { maxOpacity: 0.16, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context, state) { - const { iconToggle, palette } = context.uiTheme; +function getStyles(props, state) { + const { iconToggle, palette } = props.theme; const local = {}; @@ -110,8 +108,8 @@ function getStyles(props, context, state) { /** * Returns size of icon. Priority order: style prop, size prop, spacing.iconSize. */ -function getIconSize(props, context) { - const { spacing } = context.uiTheme; +function getIconSize(props) { + const { spacing } = props.theme; const { icon } = props.style; if (icon && icon.width) { @@ -134,7 +132,7 @@ class IconToggle extends PureComponent { constructor(props, context) { super(props, context); - const iconSize = getIconSize(props, context); + const iconSize = getIconSize(props); const containerSize = getContainerSize(iconSize); this.state = { @@ -153,7 +151,7 @@ class IconToggle extends PureComponent { const { iconSize } = this.state; const { percent } = this.props; - const nextIconSize = getIconSize(nextProps, this.context); + const nextIconSize = getIconSize(nextProps); if (iconSize !== nextIconSize || nextProps.percent !== percent) { const containerSize = getContainerSize(iconSize); @@ -247,7 +245,7 @@ class IconToggle extends PureComponent { render() { const { testID } = this.props; - const styles = getStyles(this.props, this.context, this.state); + const styles = getStyles(this.props, this.state); return ( { - const { uiTheme } = this.context; - const { container } = uiTheme.bottomNavigation; + const { theme } = this.props; + const { container } = theme.bottomNavigation; const toValue = bottomNavigation ? StyleSheet.flatten(container).height : 0; @@ -181,9 +183,8 @@ class Snackbar extends PureComponent { }; renderAction = () => { - const { uiTheme } = this.context; - const { snackbar } = uiTheme; - const { button, actionText, onActionPress } = this.props; + const { button, actionText, onActionPress, theme } = this.props; + const { snackbar } = theme; const styles = {}; if (actionText && typeof onActionPress === 'function') { @@ -251,6 +252,5 @@ class Snackbar extends PureComponent { Snackbar.propTypes = propTypes; Snackbar.defaultProps = defaultProps; -Snackbar.contextTypes = contextTypes; -export default Snackbar; +export default withTheme(Snackbar); diff --git a/src/Subheader/Subheader.react.js b/src/Subheader/Subheader.react.js index db536f77..d2465631 100644 --- a/src/Subheader/Subheader.react.js +++ b/src/Subheader/Subheader.react.js @@ -4,6 +4,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; /* eslint-enable import/no-unresolved, import/extensions */ import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { text: PropTypes.string.isRequired, @@ -19,12 +20,9 @@ const defaultProps = { inset: false, lines: 1, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { subheader } = context.uiTheme; +function getStyles(props) { + const { subheader } = props.theme; return { container: [ @@ -40,7 +38,7 @@ class Subheader extends PureComponent { render() { const { text, lines } = this.props; - const styles = getStyles(this.props, this.context); + const styles = getStyles(this.props); return ( @@ -54,6 +52,5 @@ class Subheader extends PureComponent { Subheader.propTypes = propTypes; Subheader.defaultProps = defaultProps; -Subheader.contextTypes = contextTypes; -export default Subheader; +export default withTheme(Subheader); diff --git a/src/Toolbar/CenterElement.react.js b/src/Toolbar/CenterElement.react.js index 3353144b..c2a28790 100644 --- a/src/Toolbar/CenterElement.react.js +++ b/src/Toolbar/CenterElement.react.js @@ -10,6 +10,7 @@ import { } from 'react-native'; /* eslint-enable import/no-unresolved, import/extensions */ import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; const propTypes = { isSearchActive: PropTypes.bool.isRequired, @@ -36,13 +37,10 @@ const defaultProps = { searchable: null, style: {}, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context, state = {}) { - const { leftElement } = props; - const { toolbar, toolbarSearchActive } = context.uiTheme; +function getStyles(props, state = {}) { + const { leftElement, theme } = props; + const { toolbar, toolbarSearchActive } = theme; const { isSearchActive } = state; const local = {}; @@ -123,7 +121,7 @@ class CenterElement extends PureComponent { searchValue, } = this.props; const { opacityValue, isSearchActive } = this.state; - const styles = getStyles(this.props, this.context, this.state); + const styles = getStyles(this.props, this.state); // there can be situastion like this: // 1. Given toolbar with title and searchable feature @@ -177,6 +175,5 @@ class CenterElement extends PureComponent { CenterElement.propTypes = propTypes; CenterElement.defaultProps = defaultProps; -CenterElement.contextTypes = contextTypes; -export default CenterElement; +export default withTheme(CenterElement); diff --git a/src/Toolbar/LeftElement.react.js b/src/Toolbar/LeftElement.react.js index a8f88745..228b0429 100644 --- a/src/Toolbar/LeftElement.react.js +++ b/src/Toolbar/LeftElement.react.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import { Animated, Easing, StyleSheet, Text } from 'react-native'; /* eslint-enable import/no-unresolved, import/extensions */ import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; import IconToggle from '../IconToggle'; @@ -27,9 +28,6 @@ const defaultProps = { style: {}, size: 24, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; const SEARCH_FORWARD_ICON = 'arrow-forward'; @@ -43,9 +41,9 @@ function shouldUpdateStyles(props, nextProps) { return false; } -function getStyles(props, context) { - const { isSearchActive } = props; - const { toolbar, toolbarSearchActive } = context.uiTheme; +function getStyles(props) { + const { isSearchActive, theme } = props; + const { toolbar, toolbarSearchActive } = theme; return { leftElementContainer: [ @@ -66,7 +64,7 @@ class LeftElement extends PureComponent { super(props, context); this.state = { - styles: getStyles(this.props, this.context), + styles: getStyles(this.props), leftElement: props.isSearchActive ? SEARCH_FORWARD_ICON : props.leftElement, @@ -171,6 +169,5 @@ class LeftElement extends PureComponent { LeftElement.propTypes = propTypes; LeftElement.defaultProps = defaultProps; -LeftElement.contextTypes = contextTypes; -export default LeftElement; +export default withTheme(LeftElement); diff --git a/src/Toolbar/RightElement.react.js b/src/Toolbar/RightElement.react.js index 132a65b8..1a64e841 100644 --- a/src/Toolbar/RightElement.react.js +++ b/src/Toolbar/RightElement.react.js @@ -5,6 +5,7 @@ import { View, StyleSheet, NativeModules, findNodeHandle } from 'react-native'; /* eslint-enable import/no-unresolved, import/extensions */ import { ViewPropTypes } from '../utils'; +import withTheme from '../styles/withTheme'; import IconToggle from '../IconToggle'; import isFunction from '../utils/isFunction'; @@ -36,13 +37,10 @@ const defaultProps = { style: {}, searchable: null, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; -function getStyles(props, context) { - const { isSearchActive } = props; - const { toolbar, toolbarSearchActive } = context.uiTheme; +function getStyles(props) { + const { isSearchActive, theme } = props; + const { toolbar, toolbarSearchActive } = theme; return { rightElementContainer: [ @@ -214,6 +212,5 @@ class RightElement extends PureComponent { RightElement.propTypes = propTypes; RightElement.defaultProps = defaultProps; -RightElement.contextTypes = contextTypes; -export default RightElement; +export default withTheme(RightElement); diff --git a/src/Toolbar/Toolbar.react.js b/src/Toolbar/Toolbar.react.js index 4e8902b7..83a7d81e 100644 --- a/src/Toolbar/Toolbar.react.js +++ b/src/Toolbar/Toolbar.react.js @@ -7,8 +7,8 @@ import { ViewPropTypes, BackAndroid } from '../utils'; import LeftElement from './LeftElement.react'; import CenterElement from './CenterElement.react'; import RightElement from './RightElement.react'; -import IconToggle from '../IconToggle'; import isFunction from '../utils/isFunction'; +import withTheme from '../styles/withTheme'; const propTypes = { /** @@ -66,11 +66,12 @@ const propTypes = { style: PropTypes.shape({ container: ViewPropTypes.style, leftElementContainer: ViewPropTypes.style, - leftElement: IconToggle.propTypes.style, // eslint-disable-line + // FIXME + leftElement: PropTypes.any, // eslint-disable-line centerElementContainer: ViewPropTypes.style, titleText: Text.propTypes.style, // eslint-disable-line rightElementContainer: ViewPropTypes.style, - rightElement: IconToggle.propTypes.style, // eslint-disable-line + rightElement: PropTypes.any, // eslint-disable-line }), /** * This size is used for each icon on the toolbar @@ -130,6 +131,10 @@ const propTypes = { * Called when rightElement was pressed. */ onRightElementPress: PropTypes.func, + /** + * Theme + */ + theme: PropTypes.any, // eslint-disable-line }; const defaultProps = { style: {}, @@ -144,9 +149,6 @@ const defaultProps = { onLeftElementPress: null, size: 24, }; -const contextTypes = { - uiTheme: PropTypes.object.isRequired, // eslint-disable-line -}; const getBackButtonListener = callback => BackAndroid.addEventListener('hardwareBackPress', callback); @@ -154,8 +156,8 @@ const getBackButtonListener = callback => // const isSearchable = props => (props.searchable && props.isSearchActive) || false; // const getIsSearchActive = (props, state) => (props.searchable && state.isSearchActive) || false; -function getStyles(props, context) { - const { toolbar } = context.uiTheme; +function getStyles(props) { + const { toolbar } = props.theme; return { container: [toolbar.container, props.style.container], @@ -225,7 +227,7 @@ class Toolbar extends PureComponent { onSearchOpenRequested = () => { this.setState({ - isSearchActive: true, + isSearchActiveInternal: true, searchValue: '', // zIndex: 'toDefaultNext', }); @@ -279,7 +281,7 @@ class Toolbar extends PureComponent { } this.setState({ - isSearchActive: false, + isSearchActiveInternal: false, searchValue: '', }); @@ -362,7 +364,7 @@ class Toolbar extends PureComponent { hide = () => { const { moveAnimated } = this.state; - const styles = getStyles(this.props, this.context, this.state); + const styles = getStyles(this.props); Animated.timing(moveAnimated, { toValue: -1 * StyleSheet.flatten(styles.container).height, duration: 195, @@ -385,8 +387,6 @@ class Toolbar extends PureComponent { order, } = this.state; - const { uiTheme } = this.context; - const bgStyle = { position: 'absolute', top: -radius, @@ -395,7 +395,7 @@ class Toolbar extends PureComponent { borderRadius: radius, }; - const { toolbarSearchActive } = uiTheme; + const { toolbarSearchActive } = this.props.theme; const container = StyleSheet.flatten(styles.container); const searchActive = StyleSheet.flatten(toolbarSearchActive.container); @@ -443,7 +443,7 @@ class Toolbar extends PureComponent { const { isSearchActiveInternal, searchValue, positionValue } = this.state; // TODO: move out from render method - const styles = getStyles(this.props, this.context, this.state); + const styles = getStyles(this.props); return ( { + // ...and returns another component... + class ThemedComponent extends React.PureComponent { + render() { + return ( + + {theme => } + + ); + } + } + + hoistNonReactStatics(ThemedComponent, WrappedComponent); + + return ThemedComponent; +}; + +export default withTheme; diff --git a/yarn.lock b/yarn.lock index fb174972..1d12f03b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2870,6 +2870,10 @@ has@^1.0.1, has@^1.0.3: dependencies: function-bind "^1.1.1" +hoist-non-react-statics@^2.5.5: + version "2.5.5" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"