From c6fae596aaf76e77a8969cdabd853ca11469ffb5 Mon Sep 17 00:00:00 2001 From: Roland Warmerdam Date: Sat, 17 Feb 2018 19:28:46 -0800 Subject: [PATCH] Add comments and improve performance --- src/box.js | 13 ++- src/enhance-props.js | 6 +- src/expand-aliases.js | 21 ++-- src/get-class-name.js | 5 +- src/get-css.js | 4 + src/get-safe-value.js | 3 + src/prefixer.js | 3 + src/styles.js | 1 + src/value-to-string.js | 3 + test/enhance-props.js | 2 +- test/expand-aliases.js | 28 ++--- test/snapshots/box.js.md | 230 ++++++++++++++++++------------------- test/snapshots/box.js.snap | Bin 3762 -> 3771 bytes 13 files changed, 170 insertions(+), 149 deletions(-) diff --git a/src/box.js b/src/box.js index fc1e635..e68c728 100644 --- a/src/box.js +++ b/src/box.js @@ -27,11 +27,14 @@ export default class Box extends React.PureComponent { render() { const {is, css, innerRef, children, ...props} = this.props + // Convert the CSS props to class names (and inject the styles) const [className, parsedProps] = enhanceProps(props) - const finalProps = parsedProps + // Add glamor class if (css) { + // Warn that it's deprecated in the development if (process.env.NODE_ENV !== 'production') { + // Don't spam the warning if (!cssWarned) { cssWarned = true console.warn( @@ -39,17 +42,17 @@ export default class Box extends React.PureComponent { ) } } - finalProps.className = cx(className, gcss(css).toString()) + parsedProps.className = cx(className, gcss(css).toString()) } else { - finalProps.className = className + parsedProps.className = className } if (innerRef) { - finalProps.ref = node => { + parsedProps.ref = node => { innerRef(node) } } - return React.createElement(is, finalProps, children) + return React.createElement(is, parsedProps, children) } } diff --git a/src/enhance-props.js b/src/enhance-props.js index af3a32a..9c66eaf 100644 --- a/src/enhance-props.js +++ b/src/enhance-props.js @@ -4,7 +4,9 @@ import expandAliases from './expand-aliases' import * as styles from './styles' import * as cache from './cache' -// This is optimized for performance. It gets called a lot of times +/** + * Converts the CSS props to class names and inserts the styles. + */ export default function enhanceProps(rawProps) { const propsMap = expandAliases(rawProps) const enhancedProps = {} @@ -23,7 +25,7 @@ export default function enhanceProps(rawProps) { if (enhancer && !propValue) { continue } else if (!enhancer) { - // Native prop. e.g: disabled, value, type + // Pass through native props. e.g: disabled, value, type enhancedProps[propName] = propValue continue } diff --git a/src/expand-aliases.js b/src/expand-aliases.js index 677a5eb..eba7017 100644 --- a/src/expand-aliases.js +++ b/src/expand-aliases.js @@ -1,15 +1,22 @@ import {propAliases, propValidators} from './enhancers' +/** + * Expands aliases like `margin` to `marginTop`, `marginBottom`, `marginLeft` and `marginRight`. + * + * This prevents edge cases where longhand properties can't override shorthand + * properties due to the style insertion order. + */ export default function expandAliases(props) { const propNames = Object.keys(props) + // Use a Map because it's faster for setting values and looping over than an Object const newProps = new Map() - // Loop in reverse order so that the last props override the previous props - for (let i = propNames.length - 1; i >= 0; i--) { + for (let i = 0; i < propNames.length; i++) { const propName = propNames[i] const propValue = props[propName] const aliases = propAliases[propName] || [propName] + // Check that the alias has a valid value in development if (process.env.NODE_ENV !== 'production') { const validator = propValidators[propName] if (validator) { @@ -22,15 +29,9 @@ export default function expandAliases(props) { // Expand aliases for (let i = 0; i < aliases.length; i++) { - const enhancerName = aliases[i] - // Don't return duplicate props - if (!newProps.has(enhancerName)) { - newProps.set(enhancerName, propValue) - } + newProps.set(aliases[i], propValue) } } - // Return props in the order they defined so that the css gets injected in a - // predictable order - return Array.from(newProps).reverse() + return newProps } diff --git a/src/get-class-name.js b/src/get-class-name.js index 0dacf76..8d45ecf 100644 --- a/src/get-class-name.js +++ b/src/get-class-name.js @@ -1,8 +1,9 @@ import hash from './utils/hash' import getSafeValue from './get-safe-value' -// This is only used for hash based caching -// Array/index based caching doesn't reach this code +/** + * Generates the class name. + */ export default function getClassName(propertyInfo, value) { const { className, diff --git a/src/get-css.js b/src/get-css.js index dcd9cad..90a5788 100644 --- a/src/get-css.js +++ b/src/get-css.js @@ -2,6 +2,9 @@ import prefixer from './prefixer' import valueToString from './value-to-string' import getClassName from './get-class-name' +/** + * Generates the class name and styles. + */ export default function getCss(propertyInfo, value) { let rules @@ -9,6 +12,7 @@ export default function getCss(propertyInfo, value) { const className = getClassName(propertyInfo, valueString) + // Avoid running the prefixer when possible because it's slow if (propertyInfo.isPrefixed) { rules = prefixer(propertyInfo.jsName, valueString) } else { diff --git a/src/get-safe-value.js b/src/get-safe-value.js index d171d8b..3e53313 100644 --- a/src/get-safe-value.js +++ b/src/get-safe-value.js @@ -3,6 +3,9 @@ import {unsafeClassNameCharacters} from './utils/regex' const dashRegex = /[ .]/g const percentRegex = /%/g +/** + * Makes the value safe for use in a class name. + */ export default function getSafeValue(value) { return value .replace(dashRegex, '-') diff --git a/src/prefixer.js b/src/prefixer.js index 5b196a4..e85c559 100644 --- a/src/prefixer.js +++ b/src/prefixer.js @@ -3,6 +3,9 @@ import decamelize from './utils/decamelize' const prefixRegex = /^(Webkit|ms|Moz|O)/ +/** + * Adds vendor prefixes to properties and values. + */ export default function prefixer(property, value) { const rules = prefixAll({[property]: value}) const rulesArray = [] diff --git a/src/styles.js b/src/styles.js index daa3f87..8e66039 100644 --- a/src/styles.js +++ b/src/styles.js @@ -8,6 +8,7 @@ export function add(styles) { } export function getAll() { + // Convert rules array to a string return styleSheet .rules() .reduce((combinedRules, rule) => combinedRules + rule.cssText, '') diff --git a/src/value-to-string.js b/src/value-to-string.js index d0680d6..83080f8 100644 --- a/src/value-to-string.js +++ b/src/value-to-string.js @@ -1,3 +1,6 @@ +/** + * Converts number values to a string with a unit. + */ export default function valueToString(value, unit = 'px') { return typeof value === 'number' ? `${value}${unit}` : value } diff --git a/test/enhance-props.js b/test/enhance-props.js index 41cd6af..513f10c 100644 --- a/test/enhance-props.js +++ b/test/enhance-props.js @@ -16,7 +16,7 @@ test.serial('enhances a prop', t => { test.serial('expands aliases', t => { const [className, enhancedProps] = enhanceProps({margin: 11}) - t.is(className, '📦mt_11px 📦mr_11px 📦ml_11px 📦mb_11px') + t.is(className, '📦mb_11px 📦ml_11px 📦mr_11px 📦mt_11px') t.deepEqual(enhancedProps, {}) }) diff --git a/test/expand-aliases.js b/test/expand-aliases.js index 6dd9362..d41b704 100644 --- a/test/expand-aliases.js +++ b/test/expand-aliases.js @@ -6,12 +6,12 @@ test('expands an alias', t => { expandAliases({ margin: '10px', }), - [ - ['marginTop', '10px'], - ['marginRight', '10px'], - ['marginLeft', '10px'], + new Map([ ['marginBottom', '10px'], - ] + ['marginLeft', '10px'], + ['marginRight', '10px'], + ['marginTop', '10px'], + ]) ) }) @@ -21,12 +21,12 @@ test('aliases override earlier props', t => { marginTop: '20px', margin: '10px', }), - [ + new Map([ ['marginTop', '10px'], - ['marginRight', '10px'], - ['marginLeft', '10px'], ['marginBottom', '10px'], - ] + ['marginLeft', '10px'], + ['marginRight', '10px'], + ]) ) }) @@ -36,12 +36,12 @@ test('props override earlier aliases', t => { margin: '10px', marginTop: '20px', }), - [ - ['marginRight', '10px'], - ['marginLeft', '10px'], + new Map([ ['marginBottom', '10px'], + ['marginLeft', '10px'], + ['marginRight', '10px'], ['marginTop', '20px'], - ] + ]) ) }) @@ -52,6 +52,6 @@ test('maintains original prop order', t => { height: '10px', marginTop: '10px', }), - [['width', '10px'], ['height', '10px'], ['marginTop', '10px']] + new Map([['width', '10px'], ['height', '10px'], ['marginTop', '10px']]) ) }) diff --git a/test/snapshots/box.js.md b/test/snapshots/box.js.md index 6fff3fb..77508b3 100644 --- a/test/snapshots/box.js.md +++ b/test/snapshots/box.js.md @@ -9,7 +9,7 @@ Generated by [AVA](https://ava.li). > DOM
@@ -63,65 +63,65 @@ Generated by [AVA](https://ava.li). .📦max-h_100prcnt {␊ max-height: 100%;␊ }␊ - .📦btlr_5px {␊ - border-top-left-radius: 5px;␊ - }␊ - .📦btrr_5px {␊ - border-top-right-radius: 5px;␊ - }␊ .📦bblr_5px {␊ border-bottom-left-radius: 5px;␊ }␊ .📦bbrr_5px {␊ border-bottom-right-radius: 5px;␊ }␊ + .📦btlr_5px {␊ + border-top-left-radius: 5px;␊ + }␊ + .📦btrr_5px {␊ + border-top-right-radius: 5px;␊ + }␊ .📦bs_1yidkis {␊ box-shadow: 0 10px 40px black;␊ }␊ + .📦b-btm_1px-solid-black {␊ + border-bottom: 1px solid black;␊ + }␊ .📦b-lft_1px-solid-black {␊ border-left: 1px solid black;␊ }␊ - .📦b-lft-clr_red {␊ - border-left-color: red;␊ + .📦b-rgt_1px-solid-black {␊ + border-right: 1px solid black;␊ }␊ - .📦b-lft-stl_dashed {␊ - border-left-style: dashed;␊ + .📦b-top_1px-solid-black {␊ + border-top: 1px solid black;␊ }␊ - .📦b-lft-wdt_2px {␊ - border-left-width: 2px;␊ + .📦b-btm-clr_red {␊ + border-bottom-color: red;␊ }␊ - .📦b-rgt_1px-solid-black {␊ - border-right: 1px solid black;␊ + .📦b-lft-clr_red {␊ + border-left-color: red;␊ }␊ .📦b-rgt-clr_red {␊ border-right-color: red;␊ }␊ - .📦b-rgt-stl_dashed {␊ - border-right-style: dashed;␊ + .📦b-top-clr_red {␊ + border-top-color: red;␊ }␊ - .📦b-rgt-wdt_2px {␊ - border-right-width: 2px;␊ + .📦b-btm-stl_dashed {␊ + border-bottom-style: dashed;␊ }␊ - .📦b-btm_1px-solid-black {␊ - border-bottom: 1px solid black;␊ + .📦b-lft-stl_dashed {␊ + border-left-style: dashed;␊ }␊ - .📦b-btm-clr_red {␊ - border-bottom-color: red;␊ + .📦b-rgt-stl_dashed {␊ + border-right-style: dashed;␊ }␊ - .📦b-btm-stl_dashed {␊ - border-bottom-style: dashed;␊ + .📦b-top-stl_dashed {␊ + border-top-style: dashed;␊ }␊ .📦b-btm-wdt_2px {␊ border-bottom-width: 2px;␊ }␊ - .📦b-top_1px-solid-black {␊ - border-top: 1px solid black;␊ - }␊ - .📦b-top-clr_red {␊ - border-top-color: red;␊ + .📦b-lft-wdt_2px {␊ + border-left-width: 2px;␊ }␊ - .📦b-top-stl_dashed {␊ - border-top-style: dashed;␊ + .📦b-rgt-wdt_2px {␊ + border-right-width: 2px;␊ }␊ .📦b-top-wdt_2px {␊ border-top-width: 2px;␊ @@ -227,12 +227,12 @@ Generated by [AVA](https://ava.li). .📦opct_1 {␊ opacity: 1;␊ }␊ - .📦ovflw-y_auto {␊ - overflow-y: auto;␊ - }␊ .📦ovflw-x_auto {␊ overflow-x: auto;␊ }␊ + .📦ovflw-y_auto {␊ + overflow-y: auto;␊ + }␊ .📦pst_relative {␊ position: relative;␊ }␊ @@ -248,29 +248,29 @@ Generated by [AVA](https://ava.li). .📦lft_10px {␊ left: 10px;␊ }␊ - .📦mt_10px {␊ - margin-top: 10px;␊ - }␊ .📦mb_10px {␊ margin-bottom: 10px;␊ }␊ - .📦mr_10px {␊ - margin-right: 10px;␊ - }␊ .📦ml_10px {␊ margin-left: 10px;␊ }␊ - .📦pt_10px {␊ - padding-top: 10px;␊ + .📦mr_10px {␊ + margin-right: 10px;␊ + }␊ + .📦mt_10px {␊ + margin-top: 10px;␊ }␊ .📦pb_10px {␊ padding-bottom: 10px;␊ }␊ + .📦pl_10px {␊ + padding-left: 10px;␊ + }␊ .📦pr_10px {␊ padding-right: 10px;␊ }␊ - .📦pl_10px {␊ - padding-left: 10px;␊ + .📦pt_10px {␊ + padding-top: 10px;␊ }␊ .📦txt-algn_right {␊ text-align: right;␊ @@ -356,12 +356,12 @@ Generated by [AVA](https://ava.li). .📦bg-siz_inherit {␊ background-size: inherit;␊ }␊ - .📦bbrr_inherit {␊ - border-bottom-right-radius: inherit;␊ - }␊ .📦bblr_inherit {␊ border-bottom-left-radius: inherit;␊ }␊ + .📦bbrr_inherit {␊ + border-bottom-right-radius: inherit;␊ + }␊ .📦btlr_inherit {␊ border-top-left-radius: inherit;␊ }␊ @@ -371,35 +371,44 @@ Generated by [AVA](https://ava.li). .📦b-btm_inherit {␊ border-bottom: inherit;␊ }␊ + .📦b-lft_inherit {␊ + border-left: inherit;␊ + }␊ + .📦b-rgt_inherit {␊ + border-right: inherit;␊ + }␊ + .📦b-top_inherit {␊ + border-top: inherit;␊ + }␊ .📦b-btm-clr_inherit {␊ border-bottom-color: inherit;␊ }␊ - .📦b-lft_inherit {␊ - border-left: inherit;␊ + .📦b-btm-stl_inherit {␊ + border-bottom-style: inherit;␊ + }␊ + .📦b-btm-wdt_inherit {␊ + border-bottom-width: inherit;␊ }␊ .📦b-lft-clr_inherit {␊ border-left-color: inherit;␊ }␊ - .📦b-rgt_inherit {␊ - border-right: inherit;␊ - }␊ .📦b-rgt-clr_inherit {␊ border-right-color: inherit;␊ }␊ - .📦b-rgt-stl_inherit {␊ - border-right-style: inherit;␊ + .📦b-top-clr_inherit {␊ + border-top-color: inherit;␊ }␊ .📦b-lft-stl_inherit {␊ border-left-style: inherit;␊ }␊ - .📦b-btm-stl_inherit {␊ - border-bottom-style: inherit;␊ + .📦b-lft-wdt_inherit {␊ + border-left-width: inherit;␊ }␊ - .📦b-top_inherit {␊ - border-top: inherit;␊ + .📦b-rgt-stl_inherit {␊ + border-right-style: inherit;␊ }␊ - .📦b-top-clr_inherit {␊ - border-top-color: inherit;␊ + .📦b-rgt-wdt_inherit {␊ + border-right-width: inherit;␊ }␊ .📦b-top-stl_inherit {␊ border-top-style: inherit;␊ @@ -407,15 +416,6 @@ Generated by [AVA](https://ava.li). .📦b-top-wdt_inherit {␊ border-top-width: inherit;␊ }␊ - .📦b-rgt-wdt_inherit {␊ - border-right-width: inherit;␊ - }␊ - .📦b-lft-wdt_inherit {␊ - border-left-width: inherit;␊ - }␊ - .📦b-btm-wdt_inherit {␊ - border-bottom-width: inherit;␊ - }␊ .📦bs_inherit {␊ box-shadow: inherit;␊ }␊ @@ -555,30 +555,30 @@ Generated by [AVA](https://ava.li). .📦top_inherit {␊ top: inherit;␊ }␊ - .📦mr_inherit {␊ - margin-right: inherit;␊ + .📦mb_inherit {␊ + margin-bottom: inherit;␊ }␊ .📦ml_inherit {␊ margin-left: inherit;␊ }␊ + .📦mr_inherit {␊ + margin-right: inherit;␊ + }␊ .📦mt_inherit {␊ margin-top: inherit;␊ }␊ - .📦mb_inherit {␊ - margin-bottom: inherit;␊ - }␊ - .📦pr_inherit {␊ - padding-right: inherit;␊ + .📦pb_inherit {␊ + padding-bottom: inherit;␊ }␊ .📦pl_inherit {␊ padding-left: inherit;␊ }␊ + .📦pr_inherit {␊ + padding-right: inherit;␊ + }␊ .📦pt_inherit {␊ padding-top: inherit;␊ }␊ - .📦pb_inherit {␊ - padding-bottom: inherit;␊ - }␊ .📦color_inherit {␊ color: inherit;␊ }␊ @@ -669,12 +669,12 @@ Generated by [AVA](https://ava.li). .📦bg-siz_initial {␊ background-size: initial;␊ }␊ - .📦bbrr_initial {␊ - border-bottom-right-radius: initial;␊ - }␊ .📦bblr_initial {␊ border-bottom-left-radius: initial;␊ }␊ + .📦bbrr_initial {␊ + border-bottom-right-radius: initial;␊ + }␊ .📦btlr_initial {␊ border-top-left-radius: initial;␊ }␊ @@ -684,35 +684,44 @@ Generated by [AVA](https://ava.li). .📦b-btm_initial {␊ border-bottom: initial;␊ }␊ + .📦b-lft_initial {␊ + border-left: initial;␊ + }␊ + .📦b-rgt_initial {␊ + border-right: initial;␊ + }␊ + .📦b-top_initial {␊ + border-top: initial;␊ + }␊ .📦b-btm-clr_initial {␊ border-bottom-color: initial;␊ }␊ - .📦b-lft_initial {␊ - border-left: initial;␊ + .📦b-btm-stl_initial {␊ + border-bottom-style: initial;␊ + }␊ + .📦b-btm-wdt_initial {␊ + border-bottom-width: initial;␊ }␊ .📦b-lft-clr_initial {␊ border-left-color: initial;␊ }␊ - .📦b-rgt_initial {␊ - border-right: initial;␊ - }␊ .📦b-rgt-clr_initial {␊ border-right-color: initial;␊ }␊ - .📦b-rgt-stl_initial {␊ - border-right-style: initial;␊ + .📦b-top-clr_initial {␊ + border-top-color: initial;␊ }␊ .📦b-lft-stl_initial {␊ border-left-style: initial;␊ }␊ - .📦b-btm-stl_initial {␊ - border-bottom-style: initial;␊ + .📦b-lft-wdt_initial {␊ + border-left-width: initial;␊ }␊ - .📦b-top_initial {␊ - border-top: initial;␊ + .📦b-rgt-stl_initial {␊ + border-right-style: initial;␊ }␊ - .📦b-top-clr_initial {␊ - border-top-color: initial;␊ + .📦b-rgt-wdt_initial {␊ + border-right-width: initial;␊ }␊ .📦b-top-stl_initial {␊ border-top-style: initial;␊ @@ -720,15 +729,6 @@ Generated by [AVA](https://ava.li). .📦b-top-wdt_initial {␊ border-top-width: initial;␊ }␊ - .📦b-rgt-wdt_initial {␊ - border-right-width: initial;␊ - }␊ - .📦b-lft-wdt_initial {␊ - border-left-width: initial;␊ - }␊ - .📦b-btm-wdt_initial {␊ - border-bottom-width: initial;␊ - }␊ .📦bs_initial {␊ box-shadow: initial;␊ }␊ @@ -868,30 +868,30 @@ Generated by [AVA](https://ava.li). .📦top_initial {␊ top: initial;␊ }␊ - .📦mr_initial {␊ - margin-right: initial;␊ + .📦mb_initial {␊ + margin-bottom: initial;␊ }␊ .📦ml_initial {␊ margin-left: initial;␊ }␊ + .📦mr_initial {␊ + margin-right: initial;␊ + }␊ .📦mt_initial {␊ margin-top: initial;␊ }␊ - .📦mb_initial {␊ - margin-bottom: initial;␊ - }␊ - .📦pr_initial {␊ - padding-right: initial;␊ + .📦pb_initial {␊ + padding-bottom: initial;␊ }␊ .📦pl_initial {␊ padding-left: initial;␊ }␊ + .📦pr_initial {␊ + padding-right: initial;␊ + }␊ .📦pt_initial {␊ padding-top: initial;␊ }␊ - .📦pb_initial {␊ - padding-bottom: initial;␊ - }␊ .📦color_initial {␊ color: initial;␊ }␊ diff --git a/test/snapshots/box.js.snap b/test/snapshots/box.js.snap index d6c5a9d707c95c466ddd07b0a02d8590a5d3b47d..da3918677c5f3f226713440306ecd6f5be447e13 100644 GIT binary patch literal 3771 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 literal 3762 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