Minor StyleSheet/injector refactor; small fixes

This commit is contained in:
Nicolas Gallagher
2017-01-02 15:07:05 -08:00
parent e65f91d849
commit 39c76ca50c
9 changed files with 88 additions and 72 deletions
+1 -1
View File
@@ -113,7 +113,7 @@ var Cell = React.createClass({
case 2:
return styles.cellTextO;
default:
return {};
return null;
}
},
@@ -7,7 +7,7 @@ button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform: rotate(0deg); transform: rotate(0deg);}100%{-webkit-transform: rotate(360deg); transform: rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform: translateX(-100%); transform: translateX(-100%);}100%{-webkit-transform: translateX(400%); transform: translateX(400%);}}
.rn_pointerEvents\\:auto,.rn_pointerEvents\\:box-only,.rn_pointerEvents\\:box-none *{pointer-events:auto}.rn_pointerEvents\\:none,.rn_pointerEvents\\:box-only *,.rn_pointerEvents\\:box-none{pointer-events:none}
.rn-pointerEvents\\:auto,.rn_pointerEvents\\:box-only,.rn-pointerEvents\\:box-none *{pointer-events:auto}.rn-pointerEvents\\:none,.rn_pointerEvents\\:box-only *,.rn-pointerEvents\\:box-none{pointer-events:none}
.rn-bottom\\:0px{bottom:0px}
.rn-left\\:0px{left:0px}
.rn-position\\:absolute{position:absolute}
@@ -1,4 +1,4 @@
exports[`apis/StyleSheet/registry resolve with stylesheet, resolves to className 1`] = `
exports[`apis/StyleSheet/registry resolve with register, resolves to className 1`] = `
Object {
"className": "
rn-borderTopColor:red
@@ -17,7 +17,7 @@ rn-width:100px",
}
`;
exports[`apis/StyleSheet/registry resolve with stylesheet, resolves to className 2`] = `
exports[`apis/StyleSheet/registry resolve with register, resolves to className 2`] = `
Object {
"className": "
rn-borderTopColor:red
@@ -36,7 +36,7 @@ rn-width:200px",
}
`;
exports[`apis/StyleSheet/registry resolve with stylesheet, resolves to className 3`] = `
exports[`apis/StyleSheet/registry resolve with register, resolves to className 3`] = `
Object {
"className": "
rn-borderTopColor:red
@@ -55,7 +55,7 @@ rn-width:100px",
}
`;
exports[`apis/StyleSheet/registry resolve with stylesheet, resolves to mixed 1`] = `
exports[`apis/StyleSheet/registry resolve with register, resolves to mixed 1`] = `
Object {
"className": "
rn-left:50px
@@ -75,7 +75,7 @@ rn-position:absolute",
}
`;
exports[`apis/StyleSheet/registry resolve with stylesheet, resolves to mixed 2`] = `
exports[`apis/StyleSheet/registry resolve with register, resolves to mixed 2`] = `
Object {
"className": "
rn-left:50px
@@ -95,7 +95,7 @@ rn-width:200px",
}
`;
exports[`apis/StyleSheet/registry resolve with stylesheet, resolves to mixed 3`] = `
exports[`apis/StyleSheet/registry resolve with register, resolves to mixed 3`] = `
Object {
"className": "
rn-left:50px
@@ -115,7 +115,7 @@ rn-position:absolute",
}
`;
exports[`apis/StyleSheet/registry resolve without stylesheet, resolves to inline styles 1`] = `
exports[`apis/StyleSheet/registry resolve without register, resolves to inline styles 1`] = `
Object {
"className": "
",
@@ -136,7 +136,7 @@ Object {
}
`;
exports[`apis/StyleSheet/registry resolve without stylesheet, resolves to inline styles 2`] = `
exports[`apis/StyleSheet/registry resolve without register, resolves to inline styles 2`] = `
Object {
"className": "
",
@@ -157,7 +157,7 @@ Object {
}
`;
exports[`apis/StyleSheet/registry resolve without stylesheet, resolves to inline styles 3`] = `
exports[`apis/StyleSheet/registry resolve without register, resolves to inline styles 3`] = `
Object {
"className": "
",
+6 -6
View File
@@ -2,7 +2,7 @@
import injector from '../injector';
describe('apis/StyleSheet', () => {
describe('apis/StyleSheet/injector', () => {
beforeEach(() => {
document.head.insertAdjacentHTML('afterbegin', `
<style id="react-native-stylesheet">
@@ -13,10 +13,10 @@ describe('apis/StyleSheet', () => {
});
test('hydrates from SSR', () => {
const classList = injector.getAvailableClassNames();
expect(classList).toEqual([
'rn-alignItems\\:stretch',
'rn-position\\:top'
]);
const classList = injector.getClassNames();
expect(classList).toEqual({
'rn-alignItems\\:stretch': true,
'rn-position\\:top': true
});
});
});
@@ -33,21 +33,21 @@ describe('apis/StyleSheet/registry', () => {
expect(resolve3).not.toEqual(resolve4);
};
test('with stylesheet, resolves to className', () => {
test('with register, resolves to className', () => {
const a = StyleRegistry.register(styleA);
const b = StyleRegistry.register(styleB);
const c = StyleRegistry.register(styleC);
testResolve(a, b, c);
});
test('with stylesheet, resolves to mixed', () => {
test('with register, resolves to mixed', () => {
const a = styleA;
const b = StyleRegistry.register(styleB);
const c = StyleRegistry.register(styleC);
testResolve(a, b, c);
});
test('without stylesheet, resolves to inline styles', () => {
test('without register, resolves to inline styles', () => {
testResolve(styleA, styleB, styleC);
});
});
+4 -3
View File
@@ -28,11 +28,12 @@ const initialize = () => {
);
injector.addRule(
'pointer-events',
'.rn_pointerEvents\\:auto,.rn_pointerEvents\\:box-only,.rn_pointerEvents\\:box-none *{pointer-events:auto}' +
'.rn_pointerEvents\\:none,.rn_pointerEvents\\:box-only *,.rn_pointerEvents\\:box-none{pointer-events:none}'
'.rn-pointerEvents\\:auto,.rn_pointerEvents\\:box-only,.rn-pointerEvents\\:box-none *{pointer-events:auto}' +
'.rn-pointerEvents\\:none,.rn_pointerEvents\\:box-only *,.rn-pointerEvents\\:box-none{pointer-events:none}'
);
StyleRegistry.initialize();
const classNames = injector.getClassNames();
StyleRegistry.initialize(classNames);
};
export default initialize;
+62 -45
View File
@@ -5,14 +5,31 @@
import asap from 'asap';
const emptyObject = {};
const hasOwnProperty = Object.prototype.hasOwnProperty;
const CLASSNAME_REXEP = /\.rn-([^{;\s]+)/g;
const STYLE_ELEMENT_ID = 'react-native-stylesheet';
let registry = {};
let isDirty = false;
let styleNode = null;
/**
* Registers a rule and requests an update to the style sheet
*/
const addRule = (key, rule) => {
if (!registry[key]) {
registry[key] = rule;
isDirty = true;
if (global.document) {
asap(frame);
}
}
};
/**
* Returns a string of the registered rules
*/
const getStyleText = () => {
/* eslint prefer-template:0 */
let result = '\n';
@@ -24,66 +41,66 @@ const getStyleText = () => {
return result;
};
// TODO: SSR support
const getAvailableClassNames = () => {
/**
* Returns an HTML string for server rendering
*/
const getStyleSheetHtml = () => `<style id="${STYLE_ELEMENT_ID}">${getStyleText()}</style>`;
const reset = () => { registry = {}; };
/**
* Finds or injects the style sheet when in a browser environment
*/
let styleNode = null;
const getStyleNode = () => {
if (global.document) {
if (!styleNode) {
// look for existing style sheet (could also be server-rendered)
styleNode = document.getElementById(STYLE_ELEMENT_ID);
if (!styleNode) {
// if there is no existing stylesheet, inject it style sheet
document.head.insertAdjacentHTML('afterbegin', getStyleSheetHtml());
styleNode = document.getElementById(STYLE_ELEMENT_ID);
}
}
if (styleNode) {
const text = styleNode.textContent;
return text.match(CLASSNAME_REXEP).map((name) => name.slice(1));
} else {
return [];
}
} else {
return [];
return styleNode;
}
};
const createStyleHTML = (text) => `<style id="${STYLE_ELEMENT_ID}">${text}</style>`;
/**
* Determines which classes are available in the existing document. Doesn't
* rely on the registry so it can be used to read class names from a SSR style
* sheet.
*/
const getClassNames = () => {
const styleNode = getStyleNode();
if (styleNode) {
const text = styleNode.textContent;
const matches = text.match(CLASSNAME_REXEP);
if (matches) {
return matches.map((name) => name.slice(1)).reduce((classMap, className) => {
classMap[className] = true;
return classMap;
}, {});
}
}
return emptyObject;
};
const frame = () => {
if (!isDirty || !global.document) { return; }
isDirty = false;
styleNode = styleNode || document.getElementById(STYLE_ELEMENT_ID);
if (!styleNode) {
document.head.insertAdjacentHTML('afterbegin', createStyleHTML());
styleNode = document.getElementById(STYLE_ELEMENT_ID);
}
const css = getStyleText();
if (styleNode.styleSheet) {
styleNode.styleSheet.cssText = css;
} else {
/* eslint no-cond-assign:0 */
let last;
while (last = styleNode.lastChild) {
styleNode.removeChild(last);
}
styleNode.appendChild(document.createTextNode(css));
const styleNode = getStyleNode();
if (styleNode) {
const css = getStyleText();
styleNode.textContent = css;
}
};
const addRule = (key, rule) => {
if (!registry[key]) {
registry[key] = rule;
if (!isDirty) {
isDirty = true;
if (global.document) {
asap(frame);
}
}
}
};
const getStyleSheetHtml = () => createStyleHTML(getStyleText());
module.exports = {
addRule,
getAvailableClassNames,
getClassNames,
getStyleSheetHtml,
reset: () => { registry = {}; }
reset
};
+2 -4
View File
@@ -67,7 +67,6 @@ const registerStyle = (id, flatStyle) => {
*/
const resolveProps = (reactNativeStyle) => {
const flatStyle = flattenStyle(reactNativeStyle);
const domStyle = createReactDOMStyle(flatStyle);
const style = {};
@@ -122,9 +121,8 @@ const resolvePropsIfNeeded = (key, style) => {
* Web style registry
*/
const StyleRegistry = {
initialize() {
const classNames = injector.getAvailableClassNames();
classNames.forEach((className) => { injectedClassNames[className] = true; });
initialize(classNames) {
injectedClassNames = classNames;
},
reset() {