Minor refactor of StyleSheet helpers

This commit is contained in:
Nicolas Gallagher
2016-07-23 18:54:41 -07:00
parent 6f75fb3e0d
commit 1be2c810d1
5 changed files with 93 additions and 81 deletions
+1 -1
View File
@@ -11,9 +11,9 @@
"build:examples": "build-storybook -o dist-examples -c ./examples/.storybook",
"build:umd": "webpack --config webpack.config.js --sort-assets-by --progress",
"deploy:examples": "git checkout gh-pages && rm -rf ./storybook && mv dist-examples storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
"examples": "start-storybook -p 9001 -c ./examples/.storybook",
"lint": "eslint src",
"prepublish": "npm run build && npm run build:umd",
"storybook": "start-storybook -p 9001 -c ./examples/.storybook",
"test": "karma start karma.config.js",
"test:watch": "npm run test -- --no-single-run"
},
+3 -3
View File
@@ -12,11 +12,13 @@ import TextStylePropTypes from '../../components/Text/TextStylePropTypes'
import ViewStylePropTypes from '../../components/View/ViewStylePropTypes'
import warning from 'fbjs/lib/warning'
const allStylePropTypes = {}
class StyleSheetValidation {
static validateStyleProp(prop, style, caller) {
if (process.env.NODE_ENV !== 'production') {
if (allStylePropTypes[prop] === undefined) {
const message1 = `"${prop}" is not a valid style property.`
const message1 = `"${prop}" is not a valid style property on Web.`
const message2 = '\nValid style props: ' + JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' ')
styleError(message1, style, caller, message2)
} else {
@@ -51,8 +53,6 @@ const styleError = (message1, style, caller, message2) => {
)
}
const allStylePropTypes = {}
StyleSheetValidation.addValidStylePropTypes(ImageStylePropTypes)
StyleSheetValidation.addValidStylePropTypes(TextStylePropTypes)
StyleSheetValidation.addValidStylePropTypes(ViewStylePropTypes)
+5 -5
View File
@@ -1,7 +1,7 @@
/* eslint-env mocha */
import assert from 'assert'
import { defaultStyles } from '../predefs'
import { getDefaultStyleSheet } from '../css'
import isPlainObject from 'lodash/isPlainObject'
import StyleSheet from '..'
@@ -28,7 +28,7 @@ suite('apis/StyleSheet', () => {
StyleSheet.create({ root: { color: 'red' } })
assert.equal(
document.getElementById('__react-native-style').textContent,
defaultStyles
getDefaultStyleSheet()
)
})
})
@@ -44,7 +44,7 @@ suite('apis/StyleSheet', () => {
test('render', () => {
assert.equal(
StyleSheet.render().props.dangerouslySetInnerHTML.__html,
defaultStyles
getDefaultStyleSheet()
)
})
@@ -61,9 +61,9 @@ suite('apis/StyleSheet', () => {
{
className: 'test __style_df __style_pebn',
style: {
display: 'flex',
display: null,
opacity: 1,
pointerEvents: 'box-none'
pointerEvents: null
}
}
)
@@ -4,6 +4,21 @@ const POINTER_EVENTS_BOX_NONE_CLASSNAME = '__style_pebn'
const POINTER_EVENTS_BOX_ONLY_CLASSNAME = '__style_pebo'
const POINTER_EVENTS_NONE_CLASSNAME = '__style_pen'
const CSS_RESET =
// reset unwanted styles
'/* React Native */\n' +
'html {font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}\n' +
'body {margin:0}\n' +
'button::-moz-focus-inner, input::-moz-focus-inner {border:0;padding:0}\n' +
'input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration {display:none}'
const CSS_HELPERS =
// vendor prefix 'display:flex' until React supports fallback values for inline styles
`.${DISPLAY_FLEX_CLASSNAME} {display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}\n` +
// implement React Native's pointer event values
`.${POINTER_EVENTS_AUTO_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME}, .${POINTER_EVENTS_BOX_NONE_CLASSNAME} * {pointer-events:auto}\n` +
`.${POINTER_EVENTS_NONE_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME} *, .${POINTER_EVENTS_NONE_CLASSNAME} {pointer-events:none}`
const styleAsClassName = {
display: {
'flex': DISPLAY_FLEX_CLASSNAME
@@ -16,23 +31,8 @@ const styleAsClassName = {
}
}
export const mapStyleToClassName = (prop, value) => {
export const getDefaultStyleSheet = () => `${CSS_RESET}\n${CSS_HELPERS}`
export const getStyleAsHelperClassName = (prop, value) => {
return styleAsClassName[prop] && styleAsClassName[prop][value]
}
// reset unwanted styles beyond the control of React inline styles
const resetCSS =
'/* React Native */\n' +
'html {font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}\n' +
'body {margin:0}\n' +
'button::-moz-focus-inner, input::-moz-focus-inner {border:0;padding:0}\n' +
'input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration {display:none}'
const helperCSS =
// vendor prefix 'display:flex' until React supports fallback values for inline styles
`.${DISPLAY_FLEX_CLASSNAME} {display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}\n` +
// implement React Native's pointer event values
`.${POINTER_EVENTS_AUTO_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME}, .${POINTER_EVENTS_BOX_NONE_CLASSNAME} * {pointer-events:auto}\n` +
`.${POINTER_EVENTS_NONE_CLASSNAME}, .${POINTER_EVENTS_BOX_ONLY_CLASSNAME} *, .${POINTER_EVENTS_NONE_CLASSNAME} {pointer-events:none}`
export const defaultStyles = `${resetCSS}\n${helperCSS}`
+66 -54
View File
@@ -1,76 +1,88 @@
import * as css from './css'
import createReactStyleObject from './createReactStyleObject'
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment'
import flattenStyle from '../../modules/flattenStyle'
import React from 'react'
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry'
import StyleSheetValidation from './StyleSheetValidation'
import { defaultStyles, mapStyleToClassName } from './predefs'
let isRendered = false
let styleElement
let shouldInsertStyleSheet = ExecutionEnvironment.canUseDOM
const STYLE_SHEET_ID = '__react-native-style'
const _injectStyleSheet = () => {
const absoluteFillObject = { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 }
const defaultStyleSheet = css.getDefaultStyleSheet()
const insertStyleSheet = () => {
// check if the server rendered the style sheet
styleElement = document.getElementById(STYLE_SHEET_ID)
// if not, inject the style sheet
if (!styleElement) { document.head.insertAdjacentHTML('afterbegin', renderToString()) }
isRendered = true
}
const _reset = () => {
if (styleElement) { document.head.removeChild(styleElement) }
styleElement = null
isRendered = false
}
const absoluteFillObject = { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 }
const absoluteFill = ReactNativePropRegistry.register(absoluteFillObject)
const create = (styles: Object): Object => {
if (!isRendered && ExecutionEnvironment.canUseDOM) {
_injectStyleSheet()
if (!styleElement) {
document.head.insertAdjacentHTML(
'afterbegin',
`<style id="${STYLE_SHEET_ID}">${defaultStyleSheet}</style>`
)
shouldInsertStyleSheet = false
}
const result = {}
for (let key in styles) {
StyleSheetValidation.validateStyle(key, styles)
result[key] = ReactNativePropRegistry.register(styles[key])
}
return result
}
const render = () => <style dangerouslySetInnerHTML={{ __html: defaultStyles }} id={STYLE_SHEET_ID} />
const renderToString = () => `<style id="${STYLE_SHEET_ID}">${defaultStyles}</style>`
/**
* Accepts React props and converts style declarations to classNames when necessary
*/
const resolve = (props) => {
let className = props.className || ''
let style = createReactStyleObject(props.style)
for (const prop in style) {
const value = style[prop]
const replacementClassName = mapStyleToClassName(prop, value)
if (replacementClassName) {
className += ` ${replacementClassName}`
// delete style[prop]
}
}
return { className, style }
}
module.exports = {
_reset,
absoluteFill,
/**
* For testing
* @private
*/
_reset() {
if (styleElement) {
document.head.removeChild(styleElement)
styleElement = null
shouldInsertStyleSheet = true
}
},
absoluteFill: ReactNativePropRegistry.register(absoluteFillObject),
absoluteFillObject,
create,
create(styles) {
if (shouldInsertStyleSheet) {
insertStyleSheet()
}
const result = {}
for (const key in styles) {
StyleSheetValidation.validateStyle(key, styles)
result[key] = ReactNativePropRegistry.register(styles[key])
}
return result
},
hairlineWidth: 1,
flatten: flattenStyle,
/* @platform web */
render,
/* @platform web */
resolve
render() {
return <style dangerouslySetInnerHTML={{ __html: defaultStyleSheet }} id={STYLE_SHEET_ID} />
},
/**
* Accepts React props and converts style declarations to classNames when necessary
* @platform web
*/
resolve(props) {
let className = props.className || ''
let style = createReactStyleObject(props.style)
for (const prop in style) {
const value = style[prop]
const replacementClassName = css.getStyleAsHelperClassName(prop, value)
if (replacementClassName) {
className += ` ${replacementClassName}`
style[prop] = null
}
}
return { className, style }
}
}