From dbb7bb59b6d64e19514a39bf74c9d9959ead0125 Mon Sep 17 00:00:00 2001 From: Neil Gabbadon Date: Sat, 23 Jan 2016 23:16:15 -0500 Subject: [PATCH] Implements prepareStyles as composition of functions in muiTheme --- .babelrc | 1 + package.json | 2 + src/mixins/style-propable.js | 11 ++-- src/styles/getMuiTheme.js | 26 +++++++++ src/styles/transformers/autoprefixer.js | 3 + src/styles/transformers/index.js | 7 +++ src/styles/transformers/rtl.js | 78 +++++++++++++++++++++++++ src/utils/styles.js | 16 +---- 8 files changed, 125 insertions(+), 19 deletions(-) create mode 100644 src/styles/transformers/autoprefixer.js create mode 100644 src/styles/transformers/index.js create mode 100644 src/styles/transformers/rtl.js diff --git a/.babelrc b/.babelrc index 938a0ab51a1ac0..1fcbf0ea274f98 100644 --- a/.babelrc +++ b/.babelrc @@ -1,6 +1,7 @@ { "presets": ["es2015", "stage-1", "react"], "plugins": [ + "transform-object-assign", "transform-decorators-legacy", "transform-dev-warning", "add-module-exports" diff --git a/package.json b/package.json index 61d29947e7b282..2f04fb79a32396 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "homepage": "http://material-ui.com/", "dependencies": { "inline-style-prefixer": "^0.6.6", + "lodash.flowright": "^3.2.1", "lodash.merge": "^3.3.2", "lodash.throttle": "~3.0.4", "react-addons-create-fragment": "^0.14.0", @@ -52,6 +53,7 @@ "babel-plugin-add-module-exports": "^0.1.2", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-dev-warning": "^0.1.0", + "babel-plugin-transform-object-assign": "^6.3.13", "babel-plugin-transform-react-constant-elements": "^6.3.13", "babel-plugin-transform-react-inline-elements": "^6.3.13", "babel-plugin-transform-react-remove-prop-types": "^0.1.0", diff --git a/src/mixins/style-propable.js b/src/mixins/style-propable.js index e64ba9ed83cf2d..3d899697b402e4 100644 --- a/src/mixins/style-propable.js +++ b/src/mixins/style-propable.js @@ -1,5 +1,5 @@ import React from 'react'; -import {mergeStyles, mergeAndPrefix, prepareStyles as prepare} from '../utils/styles'; +import {mergeStyles, mergeAndPrefix} from '../utils/styles'; /** * This mixin isn't necessary and will be removed soon. DO NOT USE! @@ -22,8 +22,11 @@ export default { mergeAndPrefix, prepareStyles(...args) { - return prepare((this.state && this.state.muiTheme) || - this.context.muiTheme || - (this.props && this.props.muiTheme), ...args); + const { + prepareStyles = (style) => (style), + } = (this.state && this.state.muiTheme) || (this.context && this.context.muiTheme) || + (this.props && this.props.muiTheme) || {}; + + return prepareStyles(mergeStyles(...args)); }, }; diff --git a/src/styles/getMuiTheme.js b/src/styles/getMuiTheme.js index bc2464543476bc..8b18036bf80e48 100644 --- a/src/styles/getMuiTheme.js +++ b/src/styles/getMuiTheme.js @@ -4,6 +4,21 @@ import ColorManipulator from '../utils/color-manipulator'; import autoPrefix from './auto-prefix'; import lightBaseTheme from './baseThemes/lightBaseTheme'; import zIndex from './zIndex'; +import {autoprefixer, rtl} from './transformers'; +import compose from 'lodash.flowright'; +import warning from 'warning'; + +const CALLED_ONCE = Symbol('prepareStyles/calledOnce'); + +const callOnce = (transformer) => (style) => { + if (style[CALLED_ONCE]) { + warning(false, 'You cannot call prepareStyles() on the same style object more than once.'); + return style; + } else { + style[CALLED_ONCE] = true; + return transformer(style); + } +}; /** * Get the MUI theme corresponding to a base theme. @@ -230,7 +245,18 @@ export default function getMuiTheme(baseTheme, muiTheme) { }, }, muiTheme); + const transformers = []; + + if (muiTheme.userAgent !== false) { + transformers.push(autoprefixer); + } + + if (muiTheme.isRtl) { + transformers.push(rtl); + } + muiTheme.prefix = autoPrefix.getTransform(muiTheme.userAgent); + muiTheme.prepareStyles = callOnce(compose(...transformers.map(t => t(muiTheme)))); return muiTheme; } diff --git a/src/styles/transformers/autoprefixer.js b/src/styles/transformers/autoprefixer.js new file mode 100644 index 00000000000000..928633b7fe3529 --- /dev/null +++ b/src/styles/transformers/autoprefixer.js @@ -0,0 +1,3 @@ +export default (muiTheme) => (style) => { + return muiTheme.prefix(style); +}; diff --git a/src/styles/transformers/index.js b/src/styles/transformers/index.js new file mode 100644 index 00000000000000..60175bf6710eb0 --- /dev/null +++ b/src/styles/transformers/index.js @@ -0,0 +1,7 @@ +import autoprefixer from './autoprefixer'; +import rtl from './rtl'; + +export { + autoprefixer, + rtl, +}; diff --git a/src/styles/transformers/rtl.js b/src/styles/transformers/rtl.js new file mode 100644 index 00000000000000..530857689af692 --- /dev/null +++ b/src/styles/transformers/rtl.js @@ -0,0 +1,78 @@ +const reTranslate = /((^|\s)translate(3d|X)?\()(\-?[\d]+)/; +const reSkew = /((^|\s)skew(x|y)?\()\s*(\-?[\d]+)(deg|rad|grad)(,\s*(\-?[\d]+)(deg|rad|grad))?/; + +/** + * This function ensures that `style` supports both ltr and rtl directions by + * checking `styleConstants` in `muiTheme` and replacing attribute keys if + * necessary. + */ +export default (muiTheme) => (style) => { + // Left to right is the default. No need to flip anything. + if (!muiTheme || !muiTheme.isRtl) return style; + + const flippedAttributes = { + // Keys and their replacements. + right: 'left', + left: 'right', + marginRight: 'marginLeft', + marginLeft: 'marginRight', + paddingRight: 'paddingLeft', + paddingLeft: 'paddingRight', + borderRight: 'borderLeft', + borderLeft: 'borderRight', + }; + + let newStyle = {}; + + Object.keys(style).forEach(function(attribute) { + let value = style[attribute]; + let key = attribute; + + if (flippedAttributes.hasOwnProperty(attribute)) { + key = flippedAttributes[attribute]; + } + + switch (attribute) { + case 'float': + case 'textAlign': + if (value === 'right') { + value = 'left'; + } else if (value === 'left') { + value = 'right'; + } + break; + + case 'direction': + if (value === 'ltr') { + value = 'rtl'; + } else if (value === 'rtl') { + value = 'ltr'; + } + break; + + case 'transform': + let matches; + if ((matches = value.match(reTranslate))) { + value = value.replace(matches[0], matches[1] + (-parseFloat(matches[4])) ); + } + if ((matches = value.match(reSkew))) { + value = value.replace(matches[0], matches[1] + (-parseFloat(matches[4])) + matches[5] + + matches[6] ? ',' + (-parseFloat(matches[7])) + matches[8] : '' + ); + } + break; + + case 'transformOrigin': + if (value.indexOf('right') > -1) { + value = value.replace('right', 'left'); + } else if (value.indexOf('left') > -1) { + value = value.replace('left', 'right'); + } + break; + } + + newStyle[key] = value; + }); + + return newStyle; +}; diff --git a/src/utils/styles.js b/src/utils/styles.js index fecdc5dc72e1d3..cb6dad77e38fbe 100644 --- a/src/utils/styles.js +++ b/src/utils/styles.js @@ -1,16 +1,11 @@ import autoPrefix from '../styles/auto-prefix'; -import update from 'react-addons-update'; import warning from 'warning'; const reTranslate = /((^|\s)translate(3d|X)?\()(\-?[\d]+)/; const reSkew = /((^|\s)skew(x|y)?\()\s*(\-?[\d]+)(deg|rad|grad)(,\s*(\-?[\d]+)(deg|rad|grad))?/; -function mergeSingle(objA, objB) { - if (!objA) return objB; - if (!objB) return objA; - return update(objA, {$merge: objB}); -} +export const mergeStyles = (...args) => Object.assign({}, ...args); /** * This function ensures that `style` supports both ltr and rtl directions by @@ -97,15 +92,6 @@ function ensureDirection(muiTheme, style) { return newStyle; } -export function mergeStyles(base, ...args) { - for (let i = 0; i < args.length; i++) { - if (args[i]) { - base = mergeSingle(base, args[i]); - } - } - return base; -} - /** * `mergeAndPrefix` is used to merge styles and autoprefix them. It has has been deprecated * and should no longer be used.