Skip to content

Commit

Permalink
Portal accepts React.CSSProperties rather than CSSStyleDeclaration (#…
Browse files Browse the repository at this point in the history
…1069)

* make portal accept React.CSSProperties racher than CSSStyleDeclaration

* add test converage ignore
  • Loading branch information
intellild authored and cpylua committed May 9, 2019
1 parent 3b8f1dc commit be9dfa3
Show file tree
Hide file tree
Showing 15 changed files with 591 additions and 18 deletions.
3 changes: 2 additions & 1 deletion packages/zent/jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
"<rootDir>/src/form/.+",
"<rootDir>/src/portal/.+",
"<rootDir>/src/loading/.+",
"<rootDir>/src/slider/Points.tsx"
"<rootDir>/src/slider/Points.tsx",
"<rootDir>/src/utils/style/warnValidStyle.ts"
],
"moduleNameMapper": {
"fecha": "<rootDir>/../../node_modules/fecha/src/fecha.js",
Expand Down
2 changes: 2 additions & 0 deletions packages/zent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@types/react-transition-group": "^2.0.16",
"@types/sortablejs": "^1.7.2",
"@types/tinycolor2": "^1.4.1",
"@types/warning": "^3.0.0",
"autosize": "^4.0.0",
"big.js": "^5.2.2",
"classnames": "^2.2.6",
Expand All @@ -61,6 +62,7 @@
"tinycolor2": "^1.4.1",
"tslib": "^1.9.3",
"utility-types": "^3.4.1",
"warning": "^4.0.3",
"zenticons": "1.4.1"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/zent/src/loading/FullScreenLoading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { IFullScreenLoadingProps, FullScreenDefaultProps } from './props';
import LoadingMask from './components/LoadingMask';
import { Portal } from '../portal';

const NO_STYLE: Partial<CSSStyleDeclaration> = {};
const NO_STYLE: React.CSSProperties = {};

export function FullScreenLoading(props: IFullScreenLoadingProps) {
const {
Expand All @@ -26,7 +26,7 @@ export function FullScreenLoading(props: IFullScreenLoadingProps) {
return null;
}

const style = isNumber(zIndex) ? { zIndex: `${zIndex}` } : NO_STYLE;
const style = isNumber(zIndex) ? { zIndex } : NO_STYLE;

return (
<Portal
Expand Down
3 changes: 2 additions & 1 deletion packages/zent/src/pop/position.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CSSProperties } from 'react';
import capitalize from 'lodash-es/capitalize';
import isFunction from 'lodash-es/isFunction';

Expand All @@ -11,7 +12,7 @@ const ARROW_OFFSET_V = 9;

const createPosition = (x, y, side) => {
return {
getCSSStyle(): Partial<CSSStyleDeclaration> {
getCSSStyle(): CSSProperties {
return {
position: 'absolute',
left: `${Math.round(x)}px`,
Expand Down
7 changes: 4 additions & 3 deletions packages/zent/src/popover/placement/invisible.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CSSProperties } from 'react';
import createPlacement from './create';
import { PositionFunctionImpl } from '../position-function';

Expand All @@ -9,13 +10,13 @@ const locate: PositionFunctionImpl = () => {
const y = -100000;

return {
getCSSStyle(): Partial<CSSStyleDeclaration> {
getCSSStyle(): CSSProperties {
return {
position: 'fixed',
left: `${x}px`,
top: `${y}px`,
zIndex: '-10',
opacity: '0',
zIndex: -10,
opacity: 0,
};
},

Expand Down
4 changes: 3 additions & 1 deletion packages/zent/src/popover/position-function.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { CSSProperties } from 'react';

export interface IPopoverPosition {
getCSSStyle: () => Partial<CSSStyleDeclaration>;
getCSSStyle: () => CSSProperties;
name: string;
}

Expand Down
40 changes: 31 additions & 9 deletions packages/zent/src/portal/Portal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ import noop from 'lodash-es/noop';
import PurePortal, { IPurePortalProps } from './PurePortal';
import { getNodeFromSelector, hasScrollbarY } from './util';
import { SCROLLBAR_WIDTH } from '../utils/getScrollbarWidth';
import { setValueForStyles } from '../utils/style/CSSPropertyOperations';

function diffStyle(prev: React.CSSProperties, next: React.CSSProperties) {
const result: React.CSSProperties = {};
const prevKeys = Object.keys(prev) as Array<keyof React.CSSProperties>;
for (let i = 0; i < prevKeys.length; i += 1) {
const key = prevKeys[i];
if (!next[key]) {
result[key] = '';
}
}
const nextKeys = Object.keys(next) as Array<keyof React.CSSProperties>;
for (let i = 0; i < prevKeys.length; i += 1) {
const key = nextKeys[i];
result[key] = next[key];
}
return result;
}

export interface IPortalProps extends Partial<IPurePortalProps> {
visible?: boolean;
Expand All @@ -26,7 +44,7 @@ export interface IPortalProps extends Partial<IPurePortalProps> {
onClose?: (e: KeyboardEvent | TouchEvent | MouseEvent) => void;
children?: React.ReactNode;
className?: string;
style?: Partial<CSSStyleDeclaration>;
style?: React.CSSProperties;
}

export interface IPortalImperativeHandlers {
Expand All @@ -53,6 +71,7 @@ export const Portal = forwardRef<IPortalImperativeHandlers, IPortalProps>(
const parent = useMemo(() => getNodeFromSelector(selector), [selector]);
const propsRef = useRef<IPortalProps>(props);
propsRef.current = props;
const prevStyleRef = useRef<React.CSSProperties | undefined>(style);
const purePortalRef = useRef<PurePortal>(null);

// Methods for use on ref
Expand All @@ -77,17 +96,20 @@ export const Portal = forwardRef<IPortalImperativeHandlers, IPortalProps>(
return noop;
}
parent.appendChild(node);
className && (node.className = className);
if (style) {
const cssKeys = Object.keys(style) as Array<keyof CSSStyleDeclaration>;
if (cssKeys.length) {
node.style.cssText = cssKeys.map(k => `${k}: ${style[k]}`).join('; ');
}
}
return () => {
parent.removeChild(node);
};
}, [visible, node, parent, style, className]);
}, [visible, node, parent]);

useLayoutEffect(() => {
className && (node.className = className);
}, [node, className]);

useLayoutEffect(() => {
const result = diffStyle(prevStyleRef.current || {}, style || {});
setValueForStyles(node, result);
prevStyleRef.current = style;
}, [node, style]);

useLayoutEffect(() => {
if (!visible || !useLayerForClickAway) {
Expand Down
80 changes: 80 additions & 0 deletions packages/zent/src/utils/style/CSSProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* CSS properties which accept numbers but are not in units of "px".
*/
export const isUnitlessNumber: Record<string, boolean> = {
animationIterationCount: true,
borderImageOutset: true,
borderImageSlice: true,
borderImageWidth: true,
boxFlex: true,
boxFlexGroup: true,
boxOrdinalGroup: true,
columnCount: true,
columns: true,
flex: true,
flexGrow: true,
flexPositive: true,
flexShrink: true,
flexNegative: true,
flexOrder: true,
gridArea: true,
gridRow: true,
gridRowEnd: true,
gridRowSpan: true,
gridRowStart: true,
gridColumn: true,
gridColumnEnd: true,
gridColumnSpan: true,
gridColumnStart: true,
fontWeight: true,
lineClamp: true,
lineHeight: true,
opacity: true,
order: true,
orphans: true,
tabSize: true,
widows: true,
zIndex: true,
zoom: true,

// SVG-related properties
fillOpacity: true,
floodOpacity: true,
stopOpacity: true,
strokeDasharray: true,
strokeDashoffset: true,
strokeMiterlimit: true,
strokeOpacity: true,
strokeWidth: true,
};

/**
* @param {string} prefix vendor-specific prefix, eg: Webkit
* @param {string} key style name, eg: transitionDuration
* @return {string} style name prefixed with `prefix`, properly camelCased, eg:
* WebkitTransitionDuration
*/
function prefixKey(prefix: string, key: string): string {
return prefix + key.charAt(0).toUpperCase() + key.substring(1);
}

/**
* Support style names that may come passed in prefixed by adding permutations
* of vendor prefixes.
*/
const prefixes = ['Webkit', 'ms', 'Moz', 'O'];

// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
// infinite loop, because it iterates over the newly added props too.
Object.keys(isUnitlessNumber).forEach(prop => {
prefixes.forEach(prefix => {
isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
});
});
49 changes: 49 additions & 0 deletions packages/zent/src/utils/style/CSSPropertyOperations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import dangerousStyleValue from './dangerousStyleValue';
import warnValidStyle from './warnValidStyle';

import { CSSProperties } from 'react';

/**
* Operations for dealing with CSS properties.
*/

/**
* Sets the value for multiple styles on a node. If a value is specified as
* '' (empty string), the corresponding style property will be unset.
*
* @param {DOMElement} node
* @param {object} styles
*/
export function setValueForStyles(node: HTMLElement, styles: CSSProperties) {
const style = node.style;
const keys = Object.keys(styles) as Array<keyof CSSProperties>;
for (let i = 0; i < keys.length; i += 1) {
let styleName = keys[i];
const isCustomProperty = styleName.indexOf('--') === 0;
if (process.env.NODE_ENV !== 'production') {
if (!isCustomProperty) {
warnValidStyle(styleName, styles[styleName as any]);
}
}
const styleValue = dangerousStyleValue(
styleName,
styles[styleName],
isCustomProperty
);
if (styleName === 'float') {
styleName = 'cssFloat' as any;
}
if (isCustomProperty) {
style.setProperty(styleName, styleValue);
} else {
style[styleName as any] = styleValue;
}
}
}
Loading

0 comments on commit be9dfa3

Please sign in to comment.