UNPKG

react-jsx-style

Version:

create dynamic injected styles with react component or hooks

125 lines (124 loc) 4.23 kB
// To simulate the behavior of react inline style, reuse some code from react internal. // reference: https://github.com/facebook/react/blob/master/packages/react-dom/src/shared/CSSProperty.js /** * CSS properties which accept numbers but are not in units of "px". */ const unitlessPropertyDict = { animationIterationCount: true, borderImageOutset: true, borderImageSlice: true, borderImageWidth: true, boxFlex: true, boxFlexGroup: true, boxOrdinalGroup: true, columnCount: true, columns: true, flex: true, flexGrow: true, flexPositive: true, flexShrink: true, flexNegative: true, flexOrder: true, gridArea: true, gridRow: true, gridRowEnd: true, gridRowSpan: true, gridRowStart: true, gridColumn: true, gridColumnEnd: true, gridColumnSpan: true, gridColumnStart: true, fontWeight: true, lineClamp: true, lineHeight: true, opacity: true, order: true, orphans: true, tabSize: true, widows: true, zIndex: true, zoom: true, // SVG-related properties fillOpacity: true, floodOpacity: true, stopOpacity: true, strokeDasharray: true, strokeDashoffset: true, strokeMiterlimit: true, strokeOpacity: true, strokeWidth: true, }; /** * @param {string} prefix vendor-specific prefix, eg: Webkit * @param {string} key style name, eg: transitionDuration * @return {string} style name prefixed with `prefix`, properly camelCased, eg: * WebkitTransitionDuration */ function prefixKey(prefix, key) { return prefix + key.charAt(0).toUpperCase() + key.substring(1); } /** * Support style names that may come passed in prefixed by adding permutations * of vendor prefixes. */ const prefixes = ['Webkit', 'ms', 'Moz', 'O']; // Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an // infinite loop, because it iterates over the newly added props too. Object.keys(unitlessPropertyDict).forEach(function (prop) { prefixes.forEach(function (prefix) { unitlessPropertyDict[prefixKey(prefix, prop)] = unitlessPropertyDict[prop]; }); }); function isUnitlessNumber(name) { return !!(unitlessPropertyDict.hasOwnProperty(name) && unitlessPropertyDict[name]); } function dangerousStyleValue(name, value, isCustomProperty) { // Note that we've removed escapeTextForBrowser() calls here since the // whole string will be escaped when the attribute is injected into // the markup. If you provide unsafe user data here they can inject // arbitrary CSS which may be problematic (I couldn't repro this): // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/ // This is not an XSS hole but instead a potential CSS injection issue // which has lead to a greater discussion about how we're going to // trust URLs moving forward. See #2115901 const isEmpty = value == null || typeof value === 'boolean' || value === ''; if (isEmpty) { return ''; } if (!isCustomProperty && typeof value === 'number' && value !== 0 && !isUnitlessNumber(name)) { return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers } return ('' + value).trim(); } const uppercasePattern = /([A-Z])/g; const msPattern = /^ms-/; function hyphenateStyleName(name) { return name .replace(uppercasePattern, '-$1') .toLowerCase() .replace(msPattern, '-ms-'); } function createCssRules(styles) { const rules = []; for (const styleName in styles) { if (!styles.hasOwnProperty(styleName)) { continue; } const isCustomProperty = styleName.indexOf('--') === 0; const styleValue = styles[styleName]; if (styleValue != null) { const propertyName = isCustomProperty ? styleName : hyphenateStyleName(styleName); const propertyValue = dangerousStyleValue(styleName, styleValue, isCustomProperty); rules.push({ name: propertyName, value: propertyValue, }); } } return rules; } export { createCssRules };