mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-04-29 12:54:53 +08:00
[change] support CSS custom properties
Update 'setValueForStyles' and style validation to support defining and using custom properties. Fix #516
This commit is contained in:
@@ -14,6 +14,7 @@ import UIExplorer, {
|
|||||||
Code,
|
Code,
|
||||||
Description,
|
Description,
|
||||||
DocItem,
|
DocItem,
|
||||||
|
ExternalLink,
|
||||||
Section,
|
Section,
|
||||||
StyleList
|
StyleList
|
||||||
} from '../../ui-explorer';
|
} from '../../ui-explorer';
|
||||||
@@ -291,6 +292,12 @@ const ViewScreen = () =>
|
|||||||
</UIExplorer>;
|
</UIExplorer>;
|
||||||
|
|
||||||
const stylePropTypes = [
|
const stylePropTypes = [
|
||||||
|
{
|
||||||
|
label: 'web',
|
||||||
|
name: (
|
||||||
|
<ExternalLink href="https://drafts.csswg.org/css-variables/">Custom properties</ExternalLink>
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'alignContent',
|
name: 'alignContent',
|
||||||
typeInfo: 'string'
|
typeInfo: 'string'
|
||||||
|
|||||||
12
docs/storybook/ui-explorer/ExternalLink.js
Normal file
12
docs/storybook/ui-explorer/ExternalLink.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/* eslint-disable react/prop-types */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AppText from './AppText';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ExternalLink = props => <AppText {...props} accessibilityRole="link" target="_blank" />;
|
||||||
|
|
||||||
|
export default ExternalLink;
|
||||||
@@ -16,10 +16,12 @@ const StyleList = ({ stylePropTypes }) =>
|
|||||||
<Text style={styles.name}>
|
<Text style={styles.name}>
|
||||||
{name}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
{': '}
|
{typeInfo ? ': ' : null}
|
||||||
<Text style={styles.code}>
|
{typeInfo
|
||||||
{typeInfo}
|
? <Text style={styles.code}>
|
||||||
</Text>
|
{typeInfo}
|
||||||
|
</Text>
|
||||||
|
: null}
|
||||||
</AppText>
|
</AppText>
|
||||||
)}
|
)}
|
||||||
</View>;
|
</View>;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import AppText from './AppText';
|
import AppText from './AppText';
|
||||||
|
import ExternalLink from './ExternalLink';
|
||||||
import insertBetween from './insertBetween';
|
import insertBetween from './insertBetween';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, View } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
@@ -22,14 +23,12 @@ export const Description = ({ children }) =>
|
|||||||
const Divider = () => <View style={styles.divider} />;
|
const Divider = () => <View style={styles.divider} />;
|
||||||
|
|
||||||
const SourceLink = ({ uri }) =>
|
const SourceLink = ({ uri }) =>
|
||||||
<AppText
|
<ExternalLink
|
||||||
accessibilityRole="link"
|
|
||||||
href={`https://github.com/necolas/react-native-web/tree/master/docs/storybook/${uri}`}
|
href={`https://github.com/necolas/react-native-web/tree/master/docs/storybook/${uri}`}
|
||||||
style={styles.link}
|
style={styles.link}
|
||||||
target="_blank"
|
|
||||||
>
|
>
|
||||||
View source code on GitHub
|
View source code on GitHub
|
||||||
</AppText>;
|
</ExternalLink>;
|
||||||
|
|
||||||
const UIExplorer = ({ children, description, sections, title, url }) =>
|
const UIExplorer = ({ children, description, sections, title, url }) =>
|
||||||
<View style={styles.root}>
|
<View style={styles.root}>
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
import AppText from './AppText';
|
import AppText from './AppText';
|
||||||
import Code from './Code';
|
import Code from './Code';
|
||||||
import DocItem from './DocItem';
|
import DocItem from './DocItem';
|
||||||
|
import ExternalLink from './ExternalLink';
|
||||||
import Section from './Section';
|
import Section from './Section';
|
||||||
import StyleList from './StyleList';
|
import StyleList from './StyleList';
|
||||||
import TextList from './TextList';
|
import TextList from './TextList';
|
||||||
import UIExplorer, { Description } from './UIExplorer';
|
import UIExplorer, { Description } from './UIExplorer';
|
||||||
|
|
||||||
export default UIExplorer;
|
export default UIExplorer;
|
||||||
export { AppText, Code, Description, DocItem, Section, StyleList, TextList };
|
export { AppText, Code, Description, DocItem, ExternalLink, Section, StyleList, TextList };
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import findIndex from 'array-find-index';
|
|||||||
import invariant from 'fbjs/lib/invariant';
|
import invariant from 'fbjs/lib/invariant';
|
||||||
|
|
||||||
// Android 4.4 browser
|
// Android 4.4 browser
|
||||||
const isPrefixed = canUseDOM && !document.hasOwnProperty('hidden') && document.hasOwnProperty('webkitHidden');
|
const isPrefixed =
|
||||||
|
canUseDOM && !document.hasOwnProperty('hidden') && document.hasOwnProperty('webkitHidden');
|
||||||
|
|
||||||
const EVENT_TYPES = ['change'];
|
const EVENT_TYPES = ['change'];
|
||||||
const VISIBILITY_CHANGE_EVENT = isPrefixed ? 'webkitvisibilitychange' : 'visibilitychange';
|
const VISIBILITY_CHANGE_EVENT = isPrefixed ? 'webkitvisibilitychange' : 'visibilitychange';
|
||||||
|
|||||||
@@ -26,23 +26,27 @@ const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
|
|||||||
export default class StyleSheetValidation {
|
export default class StyleSheetValidation {
|
||||||
static validateStyleProp(prop, style, caller) {
|
static validateStyleProp(prop, style, caller) {
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
const isCustomProperty = prop.indexOf('--') === 0;
|
||||||
|
if (isCustomProperty) return;
|
||||||
|
|
||||||
if (allStylePropTypes[prop] === undefined) {
|
if (allStylePropTypes[prop] === undefined) {
|
||||||
const message1 = '"' + prop + '" is not a valid style property.';
|
const message1 = '"' + prop + '" is not a valid style property.';
|
||||||
const message2 =
|
const message2 =
|
||||||
'\nValid style props: ' +
|
'\nValid style props: ' +
|
||||||
JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' ');
|
JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' ');
|
||||||
styleError(message1, style, caller, message2);
|
styleError(message1, style, caller, message2);
|
||||||
}
|
} else {
|
||||||
const error = allStylePropTypes[prop](
|
const error = allStylePropTypes[prop](
|
||||||
style,
|
style,
|
||||||
prop,
|
prop,
|
||||||
caller,
|
caller,
|
||||||
'prop',
|
'prop',
|
||||||
null,
|
null,
|
||||||
ReactPropTypesSecret
|
ReactPropTypesSecret
|
||||||
);
|
);
|
||||||
if (error) {
|
if (error) {
|
||||||
styleError(error.message, style, caller);
|
styleError(error.message, style, caller);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ const colorPropType = function(isRequired, props, propName, componentName, locat
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (color === 'currentcolor' || color === 'inherit') {
|
// Web supports additional color keywords and custom property values
|
||||||
|
if (color === 'currentcolor' || color === 'inherit' || color.indexOf('var(') === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,15 @@ function StyleSheetPropType(shape: { [key: string]: ReactPropsCheckType }): Reac
|
|||||||
if (props[propName]) {
|
if (props[propName]) {
|
||||||
// Just make a dummy prop object with only the flattened style
|
// Just make a dummy prop object with only the flattened style
|
||||||
newProps = {};
|
newProps = {};
|
||||||
newProps[propName] = StyleSheet.flatten(props[propName]);
|
const flatStyle = StyleSheet.flatten(props[propName]);
|
||||||
|
// Remove custom properties from check
|
||||||
|
const nextStyle = Object.keys(flatStyle).reduce((acc, curr) => {
|
||||||
|
if (curr.indexOf('--') !== 0) {
|
||||||
|
acc[curr] = flatStyle[curr];
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
newProps[propName] = nextStyle;
|
||||||
}
|
}
|
||||||
return shapePropType(newProps, propName, componentName, location, ...rest);
|
return shapePropType(newProps, propName, componentName, location, ...rest);
|
||||||
};
|
};
|
||||||
|
|||||||
53
src/vendor/dangerousStyleValue/index.js
vendored
Normal file
53
src/vendor/dangerousStyleValue/index.js
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2013-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @providesModule dangerousStyleValue
|
||||||
|
*/
|
||||||
|
|
||||||
|
import isUnitlessNumber from '../../modules/unitlessNumbers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a value into the proper css writable value. The style name `name`
|
||||||
|
* should be logical (no hyphens), as specified
|
||||||
|
* in `CSSProperty.isUnitlessNumber`.
|
||||||
|
*
|
||||||
|
* @param {string} name CSS property name such as `topMargin`.
|
||||||
|
* @param {*} value CSS property value such as `10px`.
|
||||||
|
* @return {string} Normalized style value with dimensions applied.
|
||||||
|
*/
|
||||||
|
function dangerousStyleValue(name, value, isCustomProperty) {
|
||||||
|
// Note that we've removed escapeTextForBrowser() calls here since the
|
||||||
|
// whole string will be escaped when the attribute is injected into
|
||||||
|
// the markup. If you provide unsafe user data here they can inject
|
||||||
|
// arbitrary CSS which may be problematic (I couldn't repro this):
|
||||||
|
// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
|
||||||
|
// http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
|
||||||
|
// This is not an XSS hole but instead a potential CSS injection issue
|
||||||
|
// which has lead to a greater discussion about how we're going to
|
||||||
|
// trust URLs moving forward. See #2115901
|
||||||
|
|
||||||
|
var isEmpty = value == null || typeof value === 'boolean' || value === '';
|
||||||
|
if (isEmpty) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isCustomProperty &&
|
||||||
|
typeof value === 'number' &&
|
||||||
|
value !== 0 &&
|
||||||
|
!(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])
|
||||||
|
) {
|
||||||
|
return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
return ('' + value).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default dangerousStyleValue;
|
||||||
208
src/vendor/setValueForStyles/index.js
vendored
208
src/vendor/setValueForStyles/index.js
vendored
@@ -10,199 +10,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import unitlessNumbers from '../../modules/unitlessNumbers';
|
import dangerousStyleValue from '../dangerousStyleValue';
|
||||||
|
import warnValidStyle from '../warnValidStyle';
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
var camelizeStyleName = require('fbjs/lib/camelizeStyleName');
|
|
||||||
var warning = require('fbjs/lib/warning');
|
|
||||||
|
|
||||||
// 'msTransform' is correct, but the other prefixes should be capitalized
|
|
||||||
var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
|
|
||||||
|
|
||||||
// style values shouldn't contain a semicolon
|
|
||||||
var badStyleValueWithSemicolonPattern = /;\s*$/;
|
|
||||||
|
|
||||||
var warnedStyleNames = {};
|
|
||||||
var warnedStyleValues = {};
|
|
||||||
var warnedForNaNValue = false;
|
|
||||||
|
|
||||||
var warnHyphenatedStyleName = function(name, owner) {
|
|
||||||
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
warnedStyleNames[name] = true;
|
|
||||||
process.env.NODE_ENV !== 'production'
|
|
||||||
? warning(
|
|
||||||
false,
|
|
||||||
'Unsupported style property %s. Did you mean %s?%s',
|
|
||||||
name,
|
|
||||||
camelizeStyleName(name),
|
|
||||||
checkRenderMessage(owner)
|
|
||||||
)
|
|
||||||
: void 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
var warnBadVendoredStyleName = function(name, owner) {
|
|
||||||
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
warnedStyleNames[name] = true;
|
|
||||||
process.env.NODE_ENV !== 'production'
|
|
||||||
? warning(
|
|
||||||
false,
|
|
||||||
'Unsupported vendor-prefixed style property %s. Did you mean %s?%s',
|
|
||||||
name,
|
|
||||||
name.charAt(0).toUpperCase() + name.slice(1),
|
|
||||||
checkRenderMessage(owner)
|
|
||||||
)
|
|
||||||
: void 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
var warnStyleValueWithSemicolon = function(name, value, owner) {
|
|
||||||
if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
warnedStyleValues[value] = true;
|
|
||||||
process.env.NODE_ENV !== 'production'
|
|
||||||
? warning(
|
|
||||||
false,
|
|
||||||
"Style property values shouldn't contain a semicolon.%s " + 'Try "%s: %s" instead.',
|
|
||||||
checkRenderMessage(owner),
|
|
||||||
name,
|
|
||||||
value.replace(badStyleValueWithSemicolonPattern, '')
|
|
||||||
)
|
|
||||||
: void 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
var warnStyleValueIsNaN = function(name, value, owner) {
|
|
||||||
if (warnedForNaNValue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
warnedForNaNValue = true;
|
|
||||||
process.env.NODE_ENV !== 'production'
|
|
||||||
? warning(
|
|
||||||
false,
|
|
||||||
'`NaN` is an invalid value for the `%s` css style property.%s',
|
|
||||||
name,
|
|
||||||
checkRenderMessage(owner)
|
|
||||||
)
|
|
||||||
: void 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
var checkRenderMessage = function(owner) {
|
|
||||||
if (owner) {
|
|
||||||
var name = owner.getName();
|
|
||||||
if (name) {
|
|
||||||
return ' Check the render method of `' + name + '`.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
* @param {*} value
|
|
||||||
* @param {ReactDOMComponent} component
|
|
||||||
*/
|
|
||||||
var warnValidStyle = function(name, value, component) {
|
|
||||||
var owner;
|
|
||||||
if (component) {
|
|
||||||
owner = component._currentElement._owner;
|
|
||||||
}
|
|
||||||
if (name.indexOf('-') > -1) {
|
|
||||||
warnHyphenatedStyleName(name, owner);
|
|
||||||
} else if (badVendoredStyleNamePattern.test(name)) {
|
|
||||||
warnBadVendoredStyleName(name, owner);
|
|
||||||
} else if (badStyleValueWithSemicolonPattern.test(value)) {
|
|
||||||
warnStyleValueWithSemicolon(name, value, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'number' && isNaN(value)) {
|
|
||||||
warnStyleValueIsNaN(name, value, owner);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var styleWarnings = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a value into the proper css writable value. The style name `name`
|
|
||||||
* should be logical (no hyphens)
|
|
||||||
*
|
|
||||||
* @param {string} name CSS property name such as `topMargin`.
|
|
||||||
* @param {*} value CSS property value such as `10px`.
|
|
||||||
* @param {ReactDOMComponent} component
|
|
||||||
* @return {string} Normalized style value with dimensions applied.
|
|
||||||
*/
|
|
||||||
function dangerousStyleValue(name, value, component) {
|
|
||||||
// Note that we've removed escapeTextForBrowser() calls here since the
|
|
||||||
// whole string will be escaped when the attribute is injected into
|
|
||||||
// the markup. If you provide unsafe user data here they can inject
|
|
||||||
// arbitrary CSS which may be problematic (I couldn't repro this):
|
|
||||||
// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
|
|
||||||
// http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
|
|
||||||
// This is not an XSS hole but instead a potential CSS injection issue
|
|
||||||
// which has lead to a greater discussion about how we're going to
|
|
||||||
// trust URLs moving forward. See #2115901
|
|
||||||
|
|
||||||
var isEmpty = value == null || typeof value === 'boolean' || value === '';
|
|
||||||
if (isEmpty) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
var isNonNumeric = isNaN(value);
|
|
||||||
if (
|
|
||||||
isNonNumeric ||
|
|
||||||
value === 0 ||
|
|
||||||
(unitlessNumbers.hasOwnProperty(name) && unitlessNumbers[name])
|
|
||||||
) {
|
|
||||||
return '' + value; // cast to string
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
var warning = require('fbjs/lib/warning');
|
|
||||||
|
|
||||||
// Allow '0' to pass through without warning. 0 is already special and
|
|
||||||
// doesn't require units, so we don't need to warn about it.
|
|
||||||
if (component && value !== '0') {
|
|
||||||
var owner = component._currentElement._owner;
|
|
||||||
var ownerName = owner ? owner.getName() : null;
|
|
||||||
if (ownerName && !styleWarnings[ownerName]) {
|
|
||||||
styleWarnings[ownerName] = {};
|
|
||||||
}
|
|
||||||
var warned = false;
|
|
||||||
if (ownerName) {
|
|
||||||
var warnings = styleWarnings[ownerName];
|
|
||||||
warned = warnings[name];
|
|
||||||
if (!warned) {
|
|
||||||
warnings[name] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!warned) {
|
|
||||||
process.env.NODE_ENV !== 'production'
|
|
||||||
? warning(
|
|
||||||
false,
|
|
||||||
'a `%s` tag (owner: `%s`) was passed a numeric string value ' +
|
|
||||||
'for CSS property `%s` (value: `%s`) which will be treated ' +
|
|
||||||
'as a unitless number in a future version of React.',
|
|
||||||
component._currentElement.type,
|
|
||||||
ownerName || 'unknown',
|
|
||||||
name,
|
|
||||||
value
|
|
||||||
)
|
|
||||||
: void 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value = value.trim();
|
|
||||||
}
|
|
||||||
return value + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value for multiple styles on a node. If a value is specified as
|
* Sets the value for multiple styles on a node. If a value is specified as
|
||||||
@@ -218,14 +27,19 @@ const setValueForStyles = function(node, styles, component) {
|
|||||||
if (!styles.hasOwnProperty(styleName)) {
|
if (!styles.hasOwnProperty(styleName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
var isCustomProperty = styleName.indexOf('--') === 0;
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
warnValidStyle(styleName, styles[styleName], component);
|
if (!isCustomProperty) {
|
||||||
|
warnValidStyle(styleName, styles[styleName], component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var styleValue = dangerousStyleValue(styleName, styles[styleName], component);
|
var styleValue = dangerousStyleValue(styleName, styles[styleName], isCustomProperty);
|
||||||
if (styleName === 'float' || styleName === 'cssFloat') {
|
if (styleName === 'float') {
|
||||||
styleName = 'cssFloat';
|
styleName = 'cssFloat';
|
||||||
}
|
}
|
||||||
if (styleValue) {
|
if (isCustomProperty) {
|
||||||
|
style.setProperty(styleName, styleValue);
|
||||||
|
} else if (styleValue) {
|
||||||
style[styleName] = styleValue;
|
style[styleName] = styleValue;
|
||||||
} else {
|
} else {
|
||||||
style[styleName] = '';
|
style[styleName] = '';
|
||||||
|
|||||||
170
src/vendor/warnValidStyle/index.js
vendored
Normal file
170
src/vendor/warnValidStyle/index.js
vendored
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2013-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @providesModule warnValidStyle
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var emptyFunction = require('fbjs/lib/emptyFunction');
|
||||||
|
|
||||||
|
var warnValidStyle = emptyFunction;
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
var camelizeStyleName = require('fbjs/lib/camelizeStyleName');
|
||||||
|
var warning = require('fbjs/lib/warning');
|
||||||
|
|
||||||
|
function getComponentName(instanceOrFiber) {
|
||||||
|
if (typeof instanceOrFiber.getName === 'function') {
|
||||||
|
// Stack reconciler
|
||||||
|
const instance = ((instanceOrFiber: any): ReactInstance);
|
||||||
|
return instance.getName();
|
||||||
|
}
|
||||||
|
if (typeof instanceOrFiber.tag === 'number') {
|
||||||
|
// Fiber reconciler
|
||||||
|
const fiber = ((instanceOrFiber: any): Fiber);
|
||||||
|
const { type } = fiber;
|
||||||
|
if (typeof type === 'string') {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
if (typeof type === 'function') {
|
||||||
|
return type.displayName || type.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'msTransform' is correct, but the other prefixes should be capitalized
|
||||||
|
var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
|
||||||
|
|
||||||
|
// style values shouldn't contain a semicolon
|
||||||
|
var badStyleValueWithSemicolonPattern = /;\s*$/;
|
||||||
|
|
||||||
|
var warnedStyleNames = {};
|
||||||
|
var warnedStyleValues = {};
|
||||||
|
var warnedForNaNValue = false;
|
||||||
|
var warnedForInfinityValue = false;
|
||||||
|
|
||||||
|
var warnHyphenatedStyleName = function(name, owner) {
|
||||||
|
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
warnedStyleNames[name] = true;
|
||||||
|
warning(
|
||||||
|
false,
|
||||||
|
'Unsupported style property %s. Did you mean %s?%s',
|
||||||
|
name,
|
||||||
|
camelizeStyleName(name),
|
||||||
|
checkRenderMessage(owner)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
var warnBadVendoredStyleName = function(name, owner) {
|
||||||
|
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
warnedStyleNames[name] = true;
|
||||||
|
warning(
|
||||||
|
false,
|
||||||
|
'Unsupported vendor-prefixed style property %s. Did you mean %s?%s',
|
||||||
|
name,
|
||||||
|
name.charAt(0).toUpperCase() + name.slice(1),
|
||||||
|
checkRenderMessage(owner)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
var warnStyleValueWithSemicolon = function(name, value, owner) {
|
||||||
|
if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
warnedStyleValues[value] = true;
|
||||||
|
warning(
|
||||||
|
false,
|
||||||
|
"Style property values shouldn't contain a semicolon.%s " + 'Try "%s: %s" instead.',
|
||||||
|
checkRenderMessage(owner),
|
||||||
|
name,
|
||||||
|
value.replace(badStyleValueWithSemicolonPattern, '')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
var warnStyleValueIsNaN = function(name, value, owner) {
|
||||||
|
if (warnedForNaNValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
warnedForNaNValue = true;
|
||||||
|
warning(
|
||||||
|
false,
|
||||||
|
'`NaN` is an invalid value for the `%s` css style property.%s',
|
||||||
|
name,
|
||||||
|
checkRenderMessage(owner)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
var warnStyleValueIsInfinity = function(name, value, owner) {
|
||||||
|
if (warnedForInfinityValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
warnedForInfinityValue = true;
|
||||||
|
warning(
|
||||||
|
false,
|
||||||
|
'`Infinity` is an invalid value for the `%s` css style property.%s',
|
||||||
|
name,
|
||||||
|
checkRenderMessage(owner)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
var checkRenderMessage = function(owner) {
|
||||||
|
var ownerName;
|
||||||
|
if (owner != null) {
|
||||||
|
// Stack passes the owner manually all the way to CSSPropertyOperations.
|
||||||
|
ownerName = getComponentName(owner);
|
||||||
|
} else {
|
||||||
|
// Fiber doesn't pass it but uses ReactDebugCurrentFiber to track it.
|
||||||
|
// It is only enabled in development and tracks host components too.
|
||||||
|
// var {getCurrentFiberOwnerName} = require('ReactDebugCurrentFiber');
|
||||||
|
// ownerName = getCurrentFiberOwnerName();
|
||||||
|
// TODO: also report the stack.
|
||||||
|
}
|
||||||
|
if (ownerName) {
|
||||||
|
return '\n\nCheck the render method of `' + ownerName + '`.';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
warnValidStyle = function(name, value, component) {
|
||||||
|
var owner;
|
||||||
|
if (component) {
|
||||||
|
// TODO: this only works with Stack. Seems like we need to add unit tests?
|
||||||
|
// owner = component._currentElement._owner;
|
||||||
|
}
|
||||||
|
if (name.indexOf('-') > -1) {
|
||||||
|
warnHyphenatedStyleName(name, owner);
|
||||||
|
} else if (badVendoredStyleNamePattern.test(name)) {
|
||||||
|
warnBadVendoredStyleName(name, owner);
|
||||||
|
} else if (badStyleValueWithSemicolonPattern.test(value)) {
|
||||||
|
warnStyleValueWithSemicolon(name, value, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
if (isNaN(value)) {
|
||||||
|
warnStyleValueIsNaN(name, value, owner);
|
||||||
|
} else if (!isFinite(value)) {
|
||||||
|
warnStyleValueIsInfinity(name, value, owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default warnValidStyle;
|
||||||
Reference in New Issue
Block a user