Skip to content

Commit

Permalink
refactor portal (youzan#1040)
Browse files Browse the repository at this point in the history
* refactor portal

* refactor portal

* make withNonScrollable layered portal only

* update Portal docs

* fix popover, preview-image

* getScrollbarWidth add SSR support

* revert unnecessary changes

* move withEscToClose to LayeredPortal

* modify defaultProps for LayeredPortal

* fix FullScreenLoading

* fix select

* rewrite portal

* remove useless code

* fix withNonScrollable

* remove unnecessary type assert

* remove unnecessary hook dependency

* polish onClickAway handler

* doc: update Portal docs

* fix: fix bugs

* fix: fix scrollbar existence check

* fix: remove unused code

* fix: portal tests(not used for now)
  • Loading branch information
intellild authored and cpylua committed Apr 4, 2019
1 parent a30d5ad commit 2a29ea7
Show file tree
Hide file tree
Showing 33 changed files with 416 additions and 724 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@
"babel-eslint": "10.0.1",
"coveralls": "^3.0.3",
"cross-env": "^4.0.0",
"eslint": "^5.9.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-prettier": "^3.3.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-lean-imports": "^0.3.3",
"eslint-plugin-react": "^7.11.1",
"eslint": "^5.9.0",
"husky": "^1.2.0",
"lerna": "2.10.2",
"lint-staged": "^8.0.5",
Expand Down
25 changes: 25 additions & 0 deletions packages/zent/RELEASE_NEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,31 @@

导出的组件名字变了,老的写法

#### `Portal`

```js
import { Portal } from 'zent';
const { PurePortal } = Portal;

const MyPortal1 = Portal.withEscToClose(Portal);
const MyPortal2 = Portal.withNonScrollable(Portal);
```

新的写法

```js
import { Portal, PurePortal } from 'zent'

// 替代 withEscToClose
<Portal closeOnESC>...</Portal>

// 替代 withNonScrollable
<Portal blockPageScroll>...</Portal>
```

- 删除了 `LayeredPortal`,请用 `Portal` 替换。
- 去除 `onMount``onUnmount`,使用方直接使用上层组件的 `componentDidMount``componentWillUnmount` 即可。

```js
import { Layout } from 'zent';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import Portal from 'portal';

Enzyme.configure({ adapter: new Adapter() });

const { withNonScrollable } = Portal;

const MyPortal = withNonScrollable(Portal);

export default class NonScrollable extends Component {
state = {
visible: false,
Expand Down Expand Up @@ -39,15 +35,16 @@ export default class NonScrollable extends Component {
open
</button>
)}
<MyPortal
<Portal
className="non-scrollable-body-portal"
visible={this.state.visible}
onClose={this.onClose}
blockPageScroll
>
<div className="inspect-hint">
Toggle the portal and inspect body.style.overflow in devtool
</div>
</MyPortal>
</Portal>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import Portal from 'portal';

Enzyme.configure({ adapter: new Adapter() });

const { withESCToClose } = Portal;
const MyPortal = withESCToClose(Portal);

class EscToClose extends Component {
state = {
visible: false,
Expand Down Expand Up @@ -38,13 +35,14 @@ class EscToClose extends Component {
open
</button>
)}
<MyPortal
<Portal
className="esc-close-portal"
visible={this.state.visible}
onClose={this.onClose}
closeOnESC
>
<div className="close-hint">Press ESC to close portal</div>
</MyPortal>
</Portal>
</div>
);
}
Expand Down
1 change: 0 additions & 1 deletion packages/zent/__tests__/preview-image.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ describe('previewImage render', () => {
showRotateBtn: true,
index: 0,
});
expect(document.querySelectorAll('.zent-portal').length).toBe(1);
expect(document.querySelectorAll('.zent-image-p-anchor').length).toBe(1);
expect(document.querySelectorAll('.zent-show-image').length).toBe(1);

Expand Down
38 changes: 13 additions & 25 deletions packages/zent/src/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,14 @@ import * as React from 'react';
import { Component } from 'react';
import { CSSTransition } from 'react-transition-group';

import Portal, { IPortalProps } from '../portal';
import Portal from '../portal';
import isBrowser from '../utils/isBrowser';
import { DialogElWrapper, DialogInnerEl } from './DialogEl';
import { DialogElWrapper, DialogInnerEl, IMousePosition } from './DialogEl';
import { openDialog, closeDialog } from './open';

const { withNonScrollable, withESCToClose } = Portal;
const DialogPortal = withNonScrollable(Portal as React.ComponentType<
IPortalProps
>);
const DialogPortalESCToClose = withESCToClose(DialogPortal);

const TIMEOUT = 300; // ms

let mousePosition = null;
let mousePosition: IMousePosition | null = null;

// Inspired by antd and rc-dialog
if (isBrowser) {
Expand All @@ -31,19 +25,14 @@ export interface IDialogProps {
title?: React.ReactNode;
children?: React.ReactNode;
footer?: React.ReactNode;
visible?: boolean;
visible: boolean;
closeBtn?: boolean;
onClose?: (
e:
| KeyboardEvent
| React.MouseEvent<HTMLDivElement>
| React.MouseEvent<HTMLButtonElement>
) => void;
onClose?: (e: KeyboardEvent | MouseEvent | TouchEvent) => void;
mask?: boolean;
maskClosable?: boolean;
className?: string;
prefix?: string;
style?: React.CSSProperties;
prefix: string;
style: React.CSSProperties;
onOpened?: () => void;
onClosed?: () => void;
}
Expand All @@ -70,7 +59,7 @@ export class Dialog extends Component<IDialogProps, IDialogState> {
static openDialog = openDialog;
static closeDialog = closeDialog;

lastMousePosition = null;
lastMousePosition: IMousePosition | null = null;

constructor(props: IDialogProps) {
super(props);
Expand All @@ -80,7 +69,7 @@ export class Dialog extends Component<IDialogProps, IDialogState> {
};
}

onClose = (e: KeyboardEvent | React.MouseEvent<HTMLDivElement>) => {
onClose = (e: KeyboardEvent | MouseEvent | TouchEvent) => {
const { onClose } = this.props;
onClose && onClose(e);
};
Expand Down Expand Up @@ -139,14 +128,13 @@ export class Dialog extends Component<IDialogProps, IDialogState> {
this.lastMousePosition = null;
}

// 有关闭按钮的时候同时具有ESC关闭的行为
const PortalComponent = closeBtn ? DialogPortalESCToClose : DialogPortal;

return (
<PortalComponent
<Portal
visible={visible || exiting}
onClose={this.onClose}
className={`${prefix}-dialog-r-anchor`}
closeOnESC={closeBtn}
blockPageScroll
>
<DialogElWrapper
prefix={prefix}
Expand Down Expand Up @@ -176,7 +164,7 @@ export class Dialog extends Component<IDialogProps, IDialogState> {
</DialogInnerEl>
</CSSTransition>
</DialogElWrapper>
</PortalComponent>
</Portal>
);
}
}
Expand Down
38 changes: 19 additions & 19 deletions packages/zent/src/dialog/DialogEl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ import { Component, createRef } from 'react';
import cx from 'classnames';
import focusWithoutScroll from '../utils/dom/focusWithoutScroll';

export interface IMousePosition {
x: number;
y: number;
}

export interface IDialogInnerElProps {
prefix?: string;
title?: React.ReactNode;
onClose?: React.MouseEventHandler<HTMLButtonElement>;
onClose?: (e: KeyboardEvent | MouseEvent | TouchEvent) => void;
className?: string;
closeBtn?: boolean;
style?: React.CSSProperties;
footer?: React.ReactNode;
mousePosition?: {
x: number;
y: number;
} | null;
mousePosition?: IMousePosition | null;
}

export class DialogInnerEl extends Component<IDialogInnerElProps> {
Expand All @@ -41,7 +43,7 @@ export class DialogInnerEl extends Component<IDialogInnerElProps> {
const origin = `${mousePosition.x - x}px ${mousePosition.y - y}px 0`;
const style = this.dialogEl.style;
['Webkit', 'Moz', 'Ms', 'ms'].forEach(prefix => {
style[`${prefix}TransformOrigin`] = origin;
style[`${prefix}TransformOrigin` as any] = origin;
});
style.transformOrigin = origin;
}
Expand All @@ -67,24 +69,23 @@ export class DialogInnerEl extends Component<IDialogInnerElProps> {
);
}

onClickClose = (e: React.MouseEvent<HTMLButtonElement>) => {
const { onClose } = this.props;
if (onClose) {
onClose(e as any);
}
};

render() {
const {
onClose,
className,
prefix,
closeBtn,
footer,
style,
children,
} = this.props;
const { className, prefix, closeBtn, footer, style, children } = this.props;

const Header = this.renderHeader();

const closeBtnCls = cx(`${prefix}-dialog-r-close`, {
[`${prefix}-dialog-r-has-title`]: !!Header,
});
const Closer = closeBtn && (
<button type="button" className={closeBtnCls} onClick={onClose}>
<button type="button" className={closeBtnCls} onClick={this.onClickClose}>
×
</button>
);
Expand Down Expand Up @@ -113,8 +114,7 @@ export interface IDialogElWrapper {
mask?: boolean;
maskClosable?: boolean;
visible?: boolean;
closing?: boolean;
onClose(e: React.MouseEvent<HTMLDivElement>): void;
onClose(e: MouseEvent | TouchEvent | KeyboardEvent): void;
}

export class DialogElWrapper extends Component<IDialogElWrapper> {
Expand All @@ -139,7 +139,7 @@ export class DialogElWrapper extends Component<IDialogElWrapper> {
this.props.mask &&
this.props.maskClosable
) {
this.props.onClose(e);
this.props.onClose(e as any);
}
};

Expand Down
4 changes: 2 additions & 2 deletions packages/zent/src/dialog/open.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface ICloseDialogOption {
}

interface IStandaloneDialogProps {
options: IOpenDialogOption & { dialogId: string };
options: Partial<IOpenDialogOption> & { dialogId: string };
container: HTMLDivElement;
}

Expand Down Expand Up @@ -112,7 +112,7 @@ export interface IOpenDialogOption extends Omit<IDialogProps, 'onClose'> {
/*
打开一个dialog,返回值是一个用来关闭dialog的函数。
*/
export function openDialog(options: IOpenDialogOption = {}) {
export function openDialog(options: Partial<IOpenDialogOption> = {}) {
if (!isBrowser) return noop;

const { dialogId = uniqueId('__zent-dialog__'), parentComponent } = options;
Expand Down
36 changes: 16 additions & 20 deletions packages/zent/src/loading/FullScreenLoading.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import * as React from 'react';
import cx from 'classnames';
import isUndefined from 'lodash-es/isUndefined';
import isNumber from 'lodash-es/isNumber';

import PurePortal from '../portal/PurePortal';
import withNonScrollable from '../portal/withNonScrollable';
import useDelayed from './hooks/useDelayed';
import { IFullScreenLoadingProps, FullScreenDefaultProps } from './props';
import LoadingMask from './components/LoadingMask';
import { Portal } from '../portal';

const NO_STYLE = {};

const NonScrollablePurePortal = withNonScrollable(PurePortal);
const NO_STYLE: Partial<CSSStyleDeclaration> = {};

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

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

return (
<NonScrollablePurePortal selector={document.body} append>
<div
className={cx('zent-loading', 'zent-loading--fullscreen', className)}
style={style}
>
<LoadingMask
icon={icon}
size={iconSize}
text={iconText}
textPosition={textPosition}
/>
</div>
</NonScrollablePurePortal>
<Portal
className={cx('zent-loading', 'zent-loading--fullscreen', className)}
style={style}
blockPageScroll
>
<LoadingMask
icon={icon}
size={iconSize}
text={iconText}
textPosition={textPosition}
/>
</Portal>
);
}

Expand Down
8 changes: 4 additions & 4 deletions packages/zent/src/loading/props.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as React from 'react';

export interface ILoadingBaseProps {
loading?: boolean;
delay?: number;
icon?: 'youzan' | 'circle';
loading: boolean;
delay: number;
icon: 'youzan' | 'circle';
iconSize?: number;
iconText?: React.ReactNode;
textPosition?: 'top' | 'bottom' | 'left' | 'right';
textPosition: 'top' | 'bottom' | 'left' | 'right';
className?: string;
}

Expand Down
Loading

0 comments on commit 2a29ea7

Please sign in to comment.