From 670d43ba04dc0814481897d9ec5be8e21eb11bd0 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Sat, 20 Jan 2018 13:30:47 -0800 Subject: [PATCH] Refactor how pointerEvents styles are managed --- packages/benchmarks/README.md | 4 +- .../exports/StyleSheet/StyleSheetManager.js | 90 +++++++++---------- 2 files changed, 42 insertions(+), 52 deletions(-) diff --git a/packages/benchmarks/README.md b/packages/benchmarks/README.md index cd3980b4..eae98f06 100644 --- a/packages/benchmarks/README.md +++ b/packages/benchmarks/README.md @@ -39,7 +39,7 @@ No benchmark will run for more than 20 seconds. MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3 RAM. Google Chrome 63. -Typical render timings*: mean ± standard deviations. +Typical render timings: mean ± standard deviations. | Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) | | :--- | ---: | ---: | ---: | @@ -66,7 +66,7 @@ Other libraries Moto G4 (Android 7); Octa-core (4x1.5 GHz & 4x1.2 Ghz); 2 GB RAM. Google Chrome 63 -Typical render timings*: mean ± standard deviations. +Typical render timings: mean ± standard deviations. | Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) | | :--- | ---: | ---: | ---: | diff --git a/packages/react-native-web/src/exports/StyleSheet/StyleSheetManager.js b/packages/react-native-web/src/exports/StyleSheet/StyleSheetManager.js index 19a45ca4..360fe42a 100644 --- a/packages/react-native-web/src/exports/StyleSheet/StyleSheetManager.js +++ b/packages/react-native-web/src/exports/StyleSheet/StyleSheetManager.js @@ -21,57 +21,43 @@ const createClassName = (prop, value) => { return process.env.NODE_ENV !== 'production' ? `rn-${prop}-${hashed}` : `rn-${hashed}`; }; -const createCssRule = (className, prop, value) => { - const css = generateCss({ [prop]: value }); - const selector = `.${className}`; - return `${selector}{${css}}`; -}; +const createCssRules = (selector, prop, value) => { + const rules = []; + let v = value; -const pointerEvents = { - auto: createClassName('pointerEvents', 'auto'), - boxNone: createClassName('pointerEvents', 'box-none'), - boxOnly: createClassName('pointerEvents', 'box-only'), - none: createClassName('pointerEvents', 'none') + // pointerEvents is a special case that requires custom values and additional css rules + if (prop === 'pointerEvents') { + if (value === 'auto' || value === 'box-only') { + v = 'auto !important'; + if (value === 'box-only') { + const css = generateCss({ [prop]: 'none' }); + rules.push(`${selector} > *{${css}}`); + } + } else if (value === 'none' || value === 'box-none') { + v = 'none !important'; + if (value === 'box-none') { + const css = generateCss({ [prop]: 'auto' }); + rules.push(`${selector} > *{${css}}`); + } + } + } + + const css = generateCss({ [prop]: v }); + rules.push(`${selector}{${css}}`); + + return rules; }; // See #513 -const pointerEventsCss = - `.${pointerEvents.auto}{pointer-events:auto !important;}\n` + - `.${pointerEvents.boxOnly}{pointer-events:auto !important;}\n` + - `.${pointerEvents.none}{pointer-events:none !important;}\n` + - `.${pointerEvents.boxNone}{pointer-events:none !important;}\n` + - `.${pointerEvents.boxNone} > *{pointer-events:auto;}\n` + - `.${pointerEvents.boxOnly} > *{pointer-events:none;}`; export default class StyleSheetManager { cache = null; mainSheet = null; constructor() { - // custom pointer event values are implemented using descendent selectors, - // so we manually create the CSS and pre-register the declarations - const pointerEventsPropName = 'pointerEvents'; this.cache = { - byClassName: { - [pointerEvents.auto]: { prop: pointerEventsPropName, value: 'auto' }, - [pointerEvents.boxNone]: { - prop: pointerEventsPropName, - value: 'box-none' - }, - [pointerEvents.boxOnly]: { - prop: pointerEventsPropName, - value: 'box-only' - }, - [pointerEvents.none]: { prop: pointerEventsPropName, value: 'none' } - }, - byProp: { - pointerEvents: { - auto: pointerEvents.auto, - 'box-none': pointerEvents.boxNone, - 'box-only': pointerEvents.boxOnly, - none: pointerEvents.none - } - } + byClassName: {}, + byProp: {} }; // on the client we check for an existing style sheet before injecting style sheets @@ -84,6 +70,11 @@ export default class StyleSheetManager { this.mainSheet = document.getElementById(STYLE_ELEMENT_ID); } } + + // need to pre-register pointerEvents as they have no inline-style equivalent + ['box-only', 'box-none', 'auto', 'none'].forEach(v => { + this.setDeclaration('pointerEvents', v); + }); } getClassName(prop, value) { @@ -110,13 +101,12 @@ export default class StyleSheetManager { const mainSheetTextContext = Object.keys(cache) .reduce((rules, prop) => { - if (prop !== 'pointerEvents') { - Object.keys(cache[prop]).forEach(value => { - const className = this.getClassName(prop, value); - const rule = createCssRule(className, prop, value); - rules.push(rule); - }); - } + Object.keys(cache[prop]).forEach(value => { + const className = this.getClassName(prop, value); + const moreRules = createCssRules(`.${className}`, prop, value); + rules.push(...moreRules); + }); + return rules; }, []) .join('\n'); @@ -124,7 +114,7 @@ export default class StyleSheetManager { return [ { id: 'react-native-stylesheet-static', - textContent: `${staticCss}\n${pointerEventsCss}` + textContent: `${staticCss}` }, { id: STYLE_ELEMENT_ID, @@ -142,8 +132,8 @@ export default class StyleSheetManager { const sheet = this.mainSheet.sheet; // avoid injecting if the rule already exists (e.g., server rendered, hot reload) if (this.mainSheet.textContent.indexOf(className) === -1) { - const rule = createCssRule(className, prop, value); - sheet.insertRule(rule, sheet.cssRules.length); + const rules = createCssRules(`.${className}`, prop, value); + rules.forEach(rule => sheet.insertRule(rule, sheet.cssRules.length)); } } }