mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-06-03 18:52:07 +00:00
[fix] setNativeProps DOM style copying
The 'style' object of an HTML node is a 'CSSStyleDeclaration'. Use the 'CSSStyleDeclaration' API to copy the inline styles, rather than treating it like a plain object. This avoids errors that were resulting from indices and property names being used a key-value pairs in the resulting style copy. Fix #460 Ref #454
This commit is contained in:
@@ -11,8 +11,6 @@ import { prefixInlineStyles } from '../../modules/prefixStyles';
|
|||||||
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
import ReactNativePropRegistry from '../../modules/ReactNativePropRegistry';
|
||||||
import StyleManager from './StyleManager';
|
import StyleManager from './StyleManager';
|
||||||
|
|
||||||
const vendorPrefixPattern = /^(webkit|moz|ms)/;
|
|
||||||
|
|
||||||
const createCacheKey = id => {
|
const createCacheKey = id => {
|
||||||
const prefix = I18nManager.isRTL ? 'rtl' : 'ltr';
|
const prefix = I18nManager.isRTL ? 'rtl' : 'ltr';
|
||||||
return `${prefix}-${id}`;
|
return `${prefix}-${id}`;
|
||||||
@@ -88,10 +86,10 @@ class StyleRegistry {
|
|||||||
* To determine the next style, some of the existing DOM state must be
|
* To determine the next style, some of the existing DOM state must be
|
||||||
* converted back into React Native styles.
|
* converted back into React Native styles.
|
||||||
*/
|
*/
|
||||||
resolveStateful(rnStyleNext, { classList: domClassList, style: domStyle }) {
|
resolveStateful(rnStyleNext, { classList: rdomClassList, style: rdomStyle }) {
|
||||||
// Convert the DOM classList back into a React Native form
|
// Convert the DOM classList back into a React Native form
|
||||||
// Preserves unrecognized class names.
|
// Preserves unrecognized class names.
|
||||||
const { classList: rnClassList, style: rnStyle } = domClassList.reduce(
|
const { classList: rnClassList, style: rnStyle } = rdomClassList.reduce(
|
||||||
(styleProps, className) => {
|
(styleProps, className) => {
|
||||||
const { prop, value } = this.styleManager.getDeclaration(className);
|
const { prop, value } = this.styleManager.getDeclaration(className);
|
||||||
if (prop) {
|
if (prop) {
|
||||||
@@ -104,24 +102,12 @@ class StyleRegistry {
|
|||||||
{ classList: [], style: {} }
|
{ classList: [], style: {} }
|
||||||
);
|
);
|
||||||
|
|
||||||
// DOM style may include vendor prefixes and properties set by other libraries.
|
|
||||||
// Preserve it but transform back into React DOM style.
|
|
||||||
const rdomStyle = Object.keys(domStyle).reduce((acc, styleName) => {
|
|
||||||
const value = domStyle[styleName];
|
|
||||||
if (value !== '') {
|
|
||||||
const reactStyleName = vendorPrefixPattern.test(styleName)
|
|
||||||
? styleName.charAt(0).toUpperCase() + styleName.slice(1)
|
|
||||||
: styleName;
|
|
||||||
acc[reactStyleName] = value;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
// Create next DOM style props from current and next RN styles
|
// Create next DOM style props from current and next RN styles
|
||||||
const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve([
|
const { classList: rdomClassListNext, style: rdomStyleNext } = this.resolve([
|
||||||
rnStyle,
|
rnStyle,
|
||||||
rnStyleNext
|
rnStyleNext
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Next class names take priority over current inline styles
|
// Next class names take priority over current inline styles
|
||||||
const style = { ...rdomStyle };
|
const style = { ...rdomStyle };
|
||||||
rdomClassListNext.forEach(className => {
|
rdomClassListNext.forEach(className => {
|
||||||
@@ -130,8 +116,10 @@ class StyleRegistry {
|
|||||||
style[prop] = '';
|
style[prop] = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Next inline styles take priority over current inline styles
|
// Next inline styles take priority over current inline styles
|
||||||
Object.assign(style, rdomStyleNext);
|
Object.assign(style, rdomStyleNext);
|
||||||
|
|
||||||
// Add the current class names not managed by React Native
|
// Add the current class names not managed by React Native
|
||||||
const className = classListToString(rdomClassListNext.concat(rnClassList));
|
const className = classListToString(rdomClassListNext.concat(rnClassList));
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ describe('apis/StyleSheet/StyleRegistry', () => {
|
|||||||
// note: this also checks for correctly uppercasing the first letter of DOM vendor prefixes
|
// note: this also checks for correctly uppercasing the first letter of DOM vendor prefixes
|
||||||
const domStyleProps = {
|
const domStyleProps = {
|
||||||
classList: [],
|
classList: [],
|
||||||
style: { opacity: 0.5, webkitTransform: 'scale(1)', transform: 'scale(1)' }
|
style: { opacity: 0.5, WebkitTransform: 'scale(1)', transform: 'scale(1)' }
|
||||||
};
|
};
|
||||||
const domStyleNextProps = styleRegistry.resolveStateful(
|
const domStyleNextProps = styleRegistry.resolveStateful(
|
||||||
{ opacity: 1, transform: [{ scale: 2 }] },
|
{ opacity: 1, transform: [{ scale: 2 }] },
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import findNodeHandle from '../findNodeHandle';
|
|||||||
import StyleRegistry from '../../apis/StyleSheet/registry';
|
import StyleRegistry from '../../apis/StyleSheet/registry';
|
||||||
import UIManager from '../../apis/UIManager';
|
import UIManager from '../../apis/UIManager';
|
||||||
|
|
||||||
|
const hyphenPattern = /-([a-z])/g;
|
||||||
|
const toCamelCase = str => str.replace(hyphenPattern, m => m[1].toUpperCase());
|
||||||
|
|
||||||
const NativeMethodsMixin = {
|
const NativeMethodsMixin = {
|
||||||
/**
|
/**
|
||||||
* Removes focus from an input or view. This is the opposite of `focus()`.
|
* Removes focus from an input or view. This is the opposite of `focus()`.
|
||||||
@@ -69,11 +72,17 @@ const NativeMethodsMixin = {
|
|||||||
setNativeProps(nativeProps: Object) {
|
setNativeProps(nativeProps: Object) {
|
||||||
// Copy of existing DOM state
|
// Copy of existing DOM state
|
||||||
const node = findNodeHandle(this);
|
const node = findNodeHandle(this);
|
||||||
|
const nodeStyle = node.style;
|
||||||
const classList = Array.prototype.slice.call(node.classList);
|
const classList = Array.prototype.slice.call(node.classList);
|
||||||
const style = { ...node.style };
|
const style = {};
|
||||||
// See #454
|
// DOM style is a CSSStyleDeclaration
|
||||||
delete style.length;
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
|
||||||
|
for (let i = node.style.length; i > 0; i -= 1) {
|
||||||
|
const property = nodeStyle.item(i);
|
||||||
|
// DOM style uses hyphenated prop names and may include vendor prefixes
|
||||||
|
// Transform back into React DOM style.
|
||||||
|
style[toCamelCase(property)] = nodeStyle.getPropertyValue(property);
|
||||||
|
}
|
||||||
const domStyleProps = { classList, style };
|
const domStyleProps = { classList, style };
|
||||||
|
|
||||||
// Next DOM state
|
// Next DOM state
|
||||||
|
|||||||
Reference in New Issue
Block a user