Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(dialog):add position prop support #2056

Merged
merged 1 commit into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/zent/__tests__/dialog/Dialog.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,12 @@ describe('Dialog component', () => {
button.style.left = '100px';
button.style.top = '100px';
button.addEventListener('click', () => open());

const clickEvent = new MouseEvent('click', {
clientX: 100,
clientY: 100,
});
document.body.appendChild(button);
button.click();
button.dispatchEvent(clickEvent);

jest.runAllTimers();

Expand Down
8 changes: 8 additions & 0 deletions packages/zent/src/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import isBrowser from '../utils/isBrowser';
import { DialogElWrapper, DialogInnerEl, IMousePosition } from './DialogEl';
import { openDialog, closeDialog } from './open';
import { addEventListener } from '../utils/component/event-handler';
import { DialogPosition, IDialogPositionType } from './position';

const TIMEOUT = 300; // ms

Expand All @@ -16,6 +17,9 @@ if (isBrowser) {
document.documentElement,
'click',
(e: MouseEvent) => {
if (e.clientX === 0 || e.clientY === 0) {
return;
}
mousePosition = {
x: e.clientX,
y: e.clientY,
Expand All @@ -36,6 +40,7 @@ export interface IDialogProps {
maskClosable?: boolean;
className?: string;
style?: React.CSSProperties;
position?: IDialogPositionType;
onOpened?: () => void;
onClosed?: () => void;
}
Expand All @@ -51,6 +56,7 @@ export class Dialog extends Component<IDialogProps, IDialogState> {
visible: false,
className: '',
style: {},
position: DialogPosition.auto,
title: '',
closeBtn: true,
mask: true,
Expand Down Expand Up @@ -108,6 +114,7 @@ export class Dialog extends Component<IDialogProps, IDialogState> {
visible,
closeBtn,
style,
position,
onOpened,
onClosed,
mask,
Expand Down Expand Up @@ -151,6 +158,7 @@ export class Dialog extends Component<IDialogProps, IDialogState> {
{...props}
style={style}
closeBtn={closeBtn}
position={position}
mousePosition={this.lastMousePosition}
>
{children}
Expand Down
25 changes: 18 additions & 7 deletions packages/zent/src/dialog/DialogEl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, createRef } from 'react';
import cx from 'classnames';
import focusWithoutScroll from '../utils/dom/focusWithoutScroll';
import Icon from '../icon';
import { IDialogPositionType, getPositionTransformOrigin } from './position';

export interface IMousePosition {
x: number;
Expand All @@ -16,6 +17,7 @@ export interface IDialogInnerElProps {
style?: React.CSSProperties;
footer?: React.ReactNode;
mousePosition?: IMousePosition | null;
position?: IDialogPositionType;
}

export class DialogInnerEl extends Component<IDialogInnerElProps> {
Expand All @@ -29,22 +31,31 @@ export class DialogInnerEl extends Component<IDialogInnerElProps> {
this.resetTransformOrigin();
}

setTransformOrigin(style: CSSStyleDeclaration, origin: string) {
['Webkit', 'Moz', 'Ms', 'ms'].forEach(prefix => {
style[`${prefix}TransformOrigin`] = origin;
});
style.transformOrigin = origin;
}

resetTransformOrigin(props = this.props) {
const { mousePosition } = props;
const { mousePosition, position } = props;
let origin = getPositionTransformOrigin(position, this.dialogEl);

if (
origin === undefined &&
mousePosition &&
mousePosition.x >= 0 &&
mousePosition.y >= 0 &&
this.dialogEl &&
this.dialogEl.getBoundingClientRect
) {
const { left: x, top: y } = this.dialogEl.getBoundingClientRect();
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` as any] = origin;
});
style.transformOrigin = origin;
origin = `${mousePosition.x - x}px ${mousePosition.y - y}px 0`;
}

if (origin && this.dialogEl) {
this.setTransformOrigin(this.dialogEl.style, origin);
}
}

Expand Down
38 changes: 24 additions & 14 deletions packages/zent/src/dialog/README_en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,21 @@ Modal window with background mask; It is often used to carry the feedback of det

### API

| Props | Description | Type | Default |
| ------------ | -------------------------------------------------------- | ----------------- | ------- |
| title | Dialog title | `ReactNode` | `''` |
| children | Content of the dialog` | `ReactNode` | `null` |
| footer | Content of the dialog footer | `ReactNode` | `null` |
| visible | Visibility of the dialog | `boolean` | `false` |
| closeBtn | Visibility of the close button at the upper right corner | `boolean` | `true` |
| onClose | Close callback | `(event) => void` | `noop` |
| onClosed | Callback when dialog closing animation is done | `() => void` | |
| onOpened | Callback when dialog opening animation is done | `() => void` | |
| mask | Visibility of the mask | `boolean` | `true` |
| maskClosable | Click on the mask to close the dialog | `boolean` | `true` |
| className | Custom classname | `string` | `''` |
| style | Custom style object | `CSSProperties` | `{}` |
| Props | Description | Type | Default |
| ------------ | -------------------------------------------------------- | --------------------- | ------- |
| title | Dialog title | `ReactNode` | `''` |
| children | Content of the dialog` | `ReactNode` | `null` |
| footer | Content of the dialog footer | `ReactNode` | `null` |
| visible | Visibility of the dialog | `boolean` | `false` |
| closeBtn | Visibility of the close button at the upper right corner | `boolean` | `true` |
| onClose | Close callback | `(event) => void` | `noop` |
| onClosed | Callback when dialog closing animation is done | `() => void` | |
| onOpened | Callback when dialog opening animation is done | `() => void` | |
| mask | Visibility of the mask | `boolean` | `true` |
| maskClosable | Click on the mask to close the dialog | `boolean` | `true` |
| className | Custom classname | `string` | `''` |
| style | Custom style object | `CSSProperties` | `{}` |
| position | Position way | `IDialogPositionType` | `auto` |

#### openDialog

Expand Down Expand Up @@ -72,6 +73,15 @@ Set a `style` prop on Dialog can specify its width, e.g. `style={{ width: '600px

By default the pop-up window width will adapt its content, meanwhile it has a minimum width and maximum width.

#### Position

The `position` prop can be used to specify the opening position of the dialog.

| value | description |
| -------- | ---------------------------------------------------------- |
| `auto` | Automatically chooses the opening position, default value. |
| `center` | The dialog will be displayed in the center of the screen. |

#### The following functions is obsolete in the new design system and is only used as a reference for the old version

<!-- demo-slot-4 -->
Expand Down
38 changes: 24 additions & 14 deletions packages/zent/src/dialog/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,21 @@ scatter: true

### API

| 参数 | 说明 | 类型 | 默认值 |
| ------------ | --------------------------------- | ----------------- | ------- |
| title | 自定义弹框标题 | `ReactNode` | `''` |
| children | 弹框内容: `<Dialog>xxxx</Dialog>` | `ReactNode` | `null` |
| footer | 底部内容 | `ReactNode` | `null` |
| visible | 是否打开对话窗 | `boolean` | `false` |
| closeBtn | 是否显示右上角关闭按钮 | `boolean` | `true` |
| onClose | 关闭操作回调函数 | `(event) => void` | `noop` |
| onOpened | 对话窗打开动画结束后的回调函数 | `() => void` | |
| onClosed | 对话窗关闭动画结束后的回调函数 | `() => void` | |
| mask | 是否显示遮罩 | `boolean` | `true` |
| maskClosable | 点击遮罩是否可以关闭 | `boolean` | `true` |
| className | 自定义额外类名 | `string` | `''` |
| style | 自定义样式 | `CSSProperties` | `{}` |
| 参数 | 说明 | 类型 | 默认值 |
| --------------- | --------------------------------- | --------------------- | ------- |
| title | 自定义弹框标题 | `ReactNode` | `''` |
| children | 弹框内容: `<Dialog>xxxx</Dialog>` | `ReactNode` | `null` |
| footer | 底部内容 | `ReactNode` | `null` |
| visible | 是否打开对话窗 | `boolean` | `false` |
| closeBtn | 是否显示右上角关闭按钮 | `boolean` | `true` |
| onClose | 关闭操作回调函数 | `(event) => void` | `noop` |
| onOpened | 对话窗打开动画结束后的回调函数 | `() => void` | |
| onClosed | 对话窗关闭动画结束后的回调函数 | `() => void` | |
| mask | 是否显示遮罩 | `boolean` | `true` |
| maskClosable | 点击遮罩是否可以关闭 | `boolean` | `true` |
| className | 自定义额外类名 | `string` | `''` |
| style | 自定义样式 | `CSSProperties` | `{}` |
| position | 对话窗打开位置 | `IDialogPositionType` | `auto` |

#### openDialog

Expand Down Expand Up @@ -77,6 +78,15 @@ scatter: true

默认情况下弹出窗口会自适应内容的宽度, 同时有最小宽度和最大宽度.

#### Position API

`position` 属性用于指定对话窗打开位置

| 支持的值 | 描述 |
| -------- | -------------------- |
| `auto` | 自动选择打开位置, 默认值 |
| `center` | 从屏幕中心位置打开 |

#### 以下功能新版设计语言已废弃,仅作为老版使用的参考

<!-- demo-slot-4 -->
Expand Down
25 changes: 25 additions & 0 deletions packages/zent/src/dialog/position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const enum DialogPosition {
auto = 'auto',
center = 'center',
}

export type IDialogPositionType = keyof typeof DialogPosition;

/**
* 根据传入的 position 参数,计算出弹窗的 transformOrigin 属性值
* @param position 弹窗位置,可选值为auto、center
* @param el 弹窗元素
* @returns 返回 transformOrigin 属性值
*/
export const getPositionTransformOrigin = (
position?: IDialogPositionType,
_el?: HTMLDivElement // 后续增加其他位置信息,需要使用 el 计算位置
) => {
switch (position) {
case DialogPosition.center:
return 'center center 0';
case DialogPosition.auto:
default:
return undefined;
}
};
Loading