UNPKG

kitchensink

Version:

Dispatch's awesome components and style guide

357 lines (279 loc) 12.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; var _appendImportantToEachValue = require('./append-important-to-each-value'); var _appendImportantToEachValue2 = _interopRequireDefault(_appendImportantToEachValue); var _cssRuleSetToString = require('./css-rule-set-to-string'); var _cssRuleSetToString2 = _interopRequireDefault(_cssRuleSetToString); var _getState = require('./get-state'); var _getState2 = _interopRequireDefault(_getState); var _getStateKey = require('./get-state-key'); var _getStateKey2 = _interopRequireDefault(_getStateKey); var _hash = require('./hash'); var _hash2 = _interopRequireDefault(_hash); var _mergeStyles = require('./merge-styles'); var _plugins = require('./plugins/'); var _plugins2 = _interopRequireDefault(_plugins); var _exenv = require('exenv'); var _exenv2 = _interopRequireDefault(_exenv); var _react = require('react'); var _react2 = _interopRequireDefault(_react); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var DEFAULT_CONFIG = { plugins: [_plugins2.default.mergeStyleArray, _plugins2.default.checkProps, _plugins2.default.resolveMediaQueries, _plugins2.default.resolveInteractionStyles, _plugins2.default.keyframes, _plugins2.default.visited, _plugins2.default.removeNestedStyles, _plugins2.default.prefix, _plugins2.default.checkProps] }; // Gross var globalState = {}; // Declare early for recursive helpers. var resolveStyles = null; var _shouldResolveStyles = function _shouldResolveStyles(component) { return component.type && !component.type._isRadiumEnhanced; }; var _resolveChildren = function _resolveChildren(_ref) { var children = _ref.children; var component = _ref.component; var config = _ref.config; var existingKeyMap = _ref.existingKeyMap; if (!children) { return children; } var childrenType = typeof children === 'undefined' ? 'undefined' : _typeof(children); if (childrenType === 'string' || childrenType === 'number') { // Don't do anything with a single primitive child return children; } if (childrenType === 'function') { // Wrap the function, resolving styles on the result return function () { var result = children.apply(this, arguments); if (_react2.default.isValidElement(result)) { return resolveStyles(component, result, config, existingKeyMap, true); } return result; }; } if (_react2.default.Children.count(children) === 1 && children.type) { // If a React Element is an only child, don't wrap it in an array for // React.Children.map() for React.Children.only() compatibility. var onlyChild = _react2.default.Children.only(children); return resolveStyles(component, onlyChild, config, existingKeyMap, true); } return _react2.default.Children.map(children, function (child) { if (_react2.default.isValidElement(child)) { return resolveStyles(component, child, config, existingKeyMap, true); } return child; }); }; // Recurse over props, just like children var _resolveProps = function _resolveProps(_ref2) { var component = _ref2.component; var config = _ref2.config; var existingKeyMap = _ref2.existingKeyMap; var props = _ref2.props; var newProps = props; Object.keys(props).forEach(function (prop) { // We already recurse over children above if (prop === 'children') { return; } var propValue = props[prop]; if (_react2.default.isValidElement(propValue)) { newProps = _extends({}, newProps); newProps[prop] = resolveStyles(component, propValue, config, existingKeyMap, true); } }); return newProps; }; var _buildGetKey = function _buildGetKey(_ref3) { var componentName = _ref3.componentName; var existingKeyMap = _ref3.existingKeyMap; var renderedElement = _ref3.renderedElement; // We need a unique key to correlate state changes due to user interaction // with the rendered element, so we know to apply the proper interactive // styles. var originalKey = typeof renderedElement.ref === 'string' ? renderedElement.ref : renderedElement.key; var key = (0, _getStateKey2.default)(originalKey); var alreadyGotKey = false; var getKey = function getKey() { if (alreadyGotKey) { return key; } alreadyGotKey = true; if (existingKeyMap[key]) { var elementName = void 0; if (typeof renderedElement.type === 'string') { elementName = renderedElement.type; } else if (renderedElement.type.constructor) { elementName = renderedElement.type.constructor.displayName || renderedElement.type.constructor.name; } throw new Error('Radium requires each element with interactive styles to have a unique ' + 'key, set using either the ref or key prop. ' + (originalKey ? 'Key "' + originalKey + '" is a duplicate.' : 'Multiple elements have no key specified.') + ' ' + 'Component: "' + componentName + '". ' + (elementName ? 'Element: "' + elementName + '".' : '')); } existingKeyMap[key] = true; return key; }; return getKey; }; var _setStyleState = function _setStyleState(component, key, stateKey, value) { if (!component._radiumIsMounted) { return; } var existing = component._lastRadiumState || component.state && component.state._radiumStyleState || {}; var state = { _radiumStyleState: _extends({}, existing) }; state._radiumStyleState[key] = _extends({}, state._radiumStyleState[key]); state._radiumStyleState[key][stateKey] = value; component._lastRadiumState = state._radiumStyleState; component.setState(state); }; var _runPlugins = function _runPlugins(_ref4) { var component = _ref4.component; var config = _ref4.config; var existingKeyMap = _ref4.existingKeyMap; var props = _ref4.props; var renderedElement = _ref4.renderedElement; // Don't run plugins if renderedElement is not a simple ReactDOMElement or has // no style. if (!_react2.default.isValidElement(renderedElement) || typeof renderedElement.type !== 'string' || !props.style) { return props; } var newProps = props; var plugins = config.plugins || DEFAULT_CONFIG.plugins; var componentName = component.constructor.displayName || component.constructor.name; var getKey = _buildGetKey({ renderedElement: renderedElement, existingKeyMap: existingKeyMap, componentName: componentName }); var getComponentField = function getComponentField(key) { return component[key]; }; var getGlobalState = function getGlobalState(key) { return globalState[key]; }; var componentGetState = function componentGetState(stateKey, elementKey) { return (0, _getState2.default)(component.state, elementKey || getKey(), stateKey); }; var setState = function setState(stateKey, value, elementKey) { return _setStyleState(component, elementKey || getKey(), stateKey, value); }; var addCSS = function addCSS(css) { var styleKeeper = component._radiumStyleKeeper || component.context._radiumStyleKeeper; if (!styleKeeper) { if (__isTestModeEnabled) { return { remove: function remove() {} }; } throw new Error('To use plugins requiring `addCSS` (e.g. keyframes, media queries), ' + 'please wrap your application in the StyleRoot component. Component ' + 'name: `' + componentName + '`.'); } return styleKeeper.addCSS(css); }; var newStyle = props.style; plugins.forEach(function (plugin) { var result = plugin({ ExecutionEnvironment: _exenv2.default, addCSS: addCSS, appendImportantToEachValue: _appendImportantToEachValue2.default, componentName: componentName, config: config, cssRuleSetToString: _cssRuleSetToString2.default, getComponentField: getComponentField, getGlobalState: getGlobalState, getState: componentGetState, hash: _hash2.default, mergeStyles: _mergeStyles.mergeStyles, props: newProps, setState: setState, isNestedStyle: _mergeStyles.isNestedStyle, style: newStyle }) || {}; newStyle = result.style || newStyle; newProps = result.props && Object.keys(result.props).length ? _extends({}, newProps, result.props) : newProps; var newComponentFields = result.componentFields || {}; Object.keys(newComponentFields).forEach(function (fieldName) { component[fieldName] = newComponentFields[fieldName]; }); var newGlobalState = result.globalState || {}; Object.keys(newGlobalState).forEach(function (key) { globalState[key] = newGlobalState[key]; }); }); if (newStyle !== props.style) { newProps = _extends({}, newProps, { style: newStyle }); } return newProps; }; // Wrapper around React.cloneElement. To avoid processing the same element // twice, whenever we clone an element add a special prop to make sure we don't // process this element again. var _cloneElement = function _cloneElement(renderedElement, newProps, newChildren) { // Only add flag if this is a normal DOM element if (typeof renderedElement.type === 'string') { newProps = _extends({}, newProps, { 'data-radium': true }); } return _react2.default.cloneElement(renderedElement, newProps, newChildren); }; // // The nucleus of Radium. resolveStyles is called on the rendered elements // before they are returned in render. It iterates over the elements and // children, rewriting props to add event handlers required to capture user // interactions (e.g. mouse over). It also replaces the style prop because it // adds in the various interaction styles (e.g. :hover). // resolveStyles = function resolveStyles(component, // ReactComponent, flow+eslint complaining renderedElement) { var // ReactElement config = arguments.length <= 2 || arguments[2] === undefined ? DEFAULT_CONFIG : arguments[2]; var existingKeyMap = arguments[3]; var shouldCheckBeforeResolve = arguments.length <= 4 || arguments[4] === undefined ? false : arguments[4]; // ReactElement existingKeyMap = existingKeyMap || {}; if (!renderedElement || // Bail if we've already processed this element. This ensures that only the // owner of an element processes that element, since the owner's render // function will be called first (which will always be the case, since you // can't know what else to render until you render the parent component). renderedElement.props && renderedElement.props['data-radium'] || // Bail if this element is a radium enhanced element, because if it is, // then it will take care of resolving its own styles. shouldCheckBeforeResolve && !_shouldResolveStyles(renderedElement)) { return renderedElement; } var newChildren = _resolveChildren({ children: renderedElement.props.children, component: component, config: config, existingKeyMap: existingKeyMap }); var newProps = _resolveProps({ component: component, config: config, existingKeyMap: existingKeyMap, props: renderedElement.props }); newProps = _runPlugins({ component: component, config: config, existingKeyMap: existingKeyMap, props: newProps, renderedElement: renderedElement }); // If nothing changed, don't bother cloning the element. Might be a bit // wasteful, as we add the sentinal to stop double-processing when we clone. // Assume benign double-processing is better than unneeded cloning. if (newChildren === renderedElement.props.children && newProps === renderedElement.props) { return renderedElement; } return _cloneElement(renderedElement, newProps !== renderedElement.props ? newProps : {}, newChildren); }; // Only for use by tests var __isTestModeEnabled = false; if (process.env.NODE_ENV !== 'production') { resolveStyles.__clearStateForTests = function () { globalState = {}; }; resolveStyles.__setTestMode = function (isEnabled) { __isTestModeEnabled = isEnabled; }; } exports.default = resolveStyles; module.exports = exports['default'];