Refactor how pointerEvents styles are managed

This commit is contained in:
Nicolas Gallagher
2018-01-20 13:30:47 -08:00
parent 73a731f2da
commit 670d43ba04
2 changed files with 42 additions and 52 deletions
+2 -2
View File
@@ -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. 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) | | 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 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) | | Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
| :--- | ---: | ---: | ---: | | :--- | ---: | ---: | ---: |
@@ -21,57 +21,43 @@ const createClassName = (prop, value) => {
return process.env.NODE_ENV !== 'production' ? `rn-${prop}-${hashed}` : `rn-${hashed}`; return process.env.NODE_ENV !== 'production' ? `rn-${prop}-${hashed}` : `rn-${hashed}`;
}; };
const createCssRule = (className, prop, value) => { const createCssRules = (selector, prop, value) => {
const css = generateCss({ [prop]: value }); const rules = [];
const selector = `.${className}`; let v = value;
return `${selector}{${css}}`;
};
const pointerEvents = { // pointerEvents is a special case that requires custom values and additional css rules
auto: createClassName('pointerEvents', 'auto'), if (prop === 'pointerEvents') {
boxNone: createClassName('pointerEvents', 'box-none'), if (value === 'auto' || value === 'box-only') {
boxOnly: createClassName('pointerEvents', 'box-only'), v = 'auto !important';
none: createClassName('pointerEvents', 'none') 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 // 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 { export default class StyleSheetManager {
cache = null; cache = null;
mainSheet = null; mainSheet = null;
constructor() { 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 = { this.cache = {
byClassName: { byClassName: {},
[pointerEvents.auto]: { prop: pointerEventsPropName, value: 'auto' }, byProp: {}
[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
}
}
}; };
// on the client we check for an existing style sheet before injecting style sheets // 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); 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) { getClassName(prop, value) {
@@ -110,13 +101,12 @@ export default class StyleSheetManager {
const mainSheetTextContext = Object.keys(cache) const mainSheetTextContext = Object.keys(cache)
.reduce((rules, prop) => { .reduce((rules, prop) => {
if (prop !== 'pointerEvents') { Object.keys(cache[prop]).forEach(value => {
Object.keys(cache[prop]).forEach(value => { const className = this.getClassName(prop, value);
const className = this.getClassName(prop, value); const moreRules = createCssRules(`.${className}`, prop, value);
const rule = createCssRule(className, prop, value); rules.push(...moreRules);
rules.push(rule); });
});
}
return rules; return rules;
}, []) }, [])
.join('\n'); .join('\n');
@@ -124,7 +114,7 @@ export default class StyleSheetManager {
return [ return [
{ {
id: 'react-native-stylesheet-static', id: 'react-native-stylesheet-static',
textContent: `${staticCss}\n${pointerEventsCss}` textContent: `${staticCss}`
}, },
{ {
id: STYLE_ELEMENT_ID, id: STYLE_ELEMENT_ID,
@@ -142,8 +132,8 @@ export default class StyleSheetManager {
const sheet = this.mainSheet.sheet; const sheet = this.mainSheet.sheet;
// avoid injecting if the rule already exists (e.g., server rendered, hot reload) // avoid injecting if the rule already exists (e.g., server rendered, hot reload)
if (this.mainSheet.textContent.indexOf(className) === -1) { if (this.mainSheet.textContent.indexOf(className) === -1) {
const rule = createCssRule(className, prop, value); const rules = createCssRules(`.${className}`, prop, value);
sheet.insertRule(rule, sheet.cssRules.length); rules.forEach(rule => sheet.insertRule(rule, sheet.cssRules.length));
} }
} }
} }