/** * Copyright (c) Nicolas Gallagher. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * */ import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'; import { MONOSPACE_FONT_STACK, SYSTEM_FONT_STACK, STYLE_SHORT_FORM_EXPANSIONS } from './constants'; import normalizeValueWithProperty from './normalizeValueWithProperty'; /** * The browser implements the CSS cascade, where the order of properties is a * factor in determining which styles to paint. React Native is different. It * gives giving precedence to the more specific style property. For example, * the value of `paddingTop` takes precedence over that of `padding`. * * This module creates mutally exclusive style declarations by expanding all of * React Native's supported shortform properties (e.g. `padding`) to their * longfrom equivalents. */ var emptyObject = {}; var supportsCSS3TextDecoration = !canUseDOM || window.CSS != null && window.CSS.supports != null && (window.CSS.supports('text-decoration-line', 'none') || window.CSS.supports('-webkit-text-decoration-line', 'none')); /** * Transform */ // { scale: 2 } => 'scale(2)' // { translateX: 20 } => 'translateX(20px)' var mapTransform = function mapTransform(transform) { var type = Object.keys(transform)[0]; var value = normalizeValueWithProperty(transform[type], type); return type + "(" + value + ")"; }; // [1,2,3,4,5,6] => 'matrix3d(1,2,3,4,5,6)' var convertTransformMatrix = function convertTransformMatrix(transformMatrix) { var matrix = transformMatrix.join(','); return "matrix3d(" + matrix + ")"; }; var resolveTransform = function resolveTransform(resolvedStyle, style) { var transform = style.transform; if (Array.isArray(style.transform)) { transform = style.transform.map(mapTransform).join(' '); } else if (style.transformMatrix) { transform = convertTransformMatrix(style.transformMatrix); } resolvedStyle.transform = transform; }; /** * Reducer */ var createReactDOMStyle = function createReactDOMStyle(style) { if (!style) { return emptyObject; } var resolvedStyle = {}; Object.keys(style).sort().forEach(function (prop) { var value = normalizeValueWithProperty(style[prop], prop); // Ignore everything else with a null value if (value == null) { return; } switch (prop) { // Ignore some React Native styles case 'aspectRatio': case 'elevation': case 'overlayColor': case 'resizeMode': case 'tintColor': { break; } // TODO: remove once this issue is fixed // https://github.com/rofrischmann/inline-style-prefixer/issues/159 case 'backgroundClip': { if (value === 'text') { resolvedStyle.backgroundClip = value; resolvedStyle.WebkitBackgroundClip = value; } break; } // The 'flex' property value in React Native must be a positive integer, // 0, or -1. case 'flex': { if (value > 0) { resolvedStyle.flexGrow = value; resolvedStyle.flexShrink = 1; resolvedStyle.flexBasis = '0%'; } else if (value === 0) { resolvedStyle.flexGrow = 0; resolvedStyle.flexShrink = 0; resolvedStyle.flexBasis = '0%'; } else if (value === -1) { resolvedStyle.flexGrow = 0; resolvedStyle.flexShrink = 1; resolvedStyle.flexBasis = 'auto'; } break; } case 'font': { resolvedStyle[prop] = value.replace('System', SYSTEM_FONT_STACK); break; } case 'fontFamily': { if (value.indexOf('System') > -1) { var stack = value.split(/,\s*/); stack[stack.indexOf('System')] = SYSTEM_FONT_STACK; resolvedStyle[prop] = stack.join(','); } else if (value === 'monospace') { resolvedStyle[prop] = MONOSPACE_FONT_STACK; } else { resolvedStyle[prop] = value; } break; } case 'fontVariant': { if (Array.isArray(value) && value.length > 0) { resolvedStyle.fontVariant = value.join(' '); } break; } case 'textAlignVertical': { resolvedStyle.verticalAlign = value === 'center' ? 'middle' : value; break; } case 'textDecorationLine': { // use 'text-decoration' for browsers that only support CSS2 // text-decoration (e.g., IE, Edge) if (!supportsCSS3TextDecoration) { resolvedStyle.textDecoration = value; } else { resolvedStyle.textDecorationLine = value; } break; } case 'transform': case 'transformMatrix': { resolveTransform(resolvedStyle, style); break; } case 'writingDirection': { resolvedStyle.direction = value; break; } default: { var longFormProperties = STYLE_SHORT_FORM_EXPANSIONS[prop]; if (longFormProperties) { longFormProperties.forEach(function (longForm, i) { // The value of any longform property in the original styles takes // precedence over the shortform's value. if (typeof style[longForm] === 'undefined') { resolvedStyle[longForm] = value; } }); } else { resolvedStyle[prop] = Array.isArray(value) ? value.join(',') : value; } } } }); return resolvedStyle; }; export default createReactDOMStyle;