Skip to content

Commit

Permalink
feat: ✨ implement dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
huang-xiao-jian authored and yangyuncai committed Jul 20, 2020
1 parent 9bb7bfc commit 33e2d77
Show file tree
Hide file tree
Showing 7 changed files with 537 additions and 0 deletions.
64 changes: 64 additions & 0 deletions packages/Dialog/Dialog.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// packages
import { CSSProperties, ReactElement } from 'react';
// internal
import { OpenTypeMixin, ButtonMixin } from '../mixin';

// 默认值填充属性
export interface NeutralDialogProps {
visible: boolean;
width: string;
messageAlign: 'left' | 'right' | 'center';
zIndex: number;
transition: 'fade' | 'scale';
showConfirmButton: boolean;
showCancelButton: boolean;
confirmButtonText: string;
cancelButtonText: string;
confirmButtonColor: string;
cancelButtonColor: string;
confirmButtonLoading: boolean;
cancelButtonLoading: boolean;
overlay: boolean;
overlayStyle: CSSProperties;
}

export interface ExogenousDialogProps {
title?: string | ReactElement;
message?: string | ReactElement;
// 内嵌样式
style?: CSSProperties;
// 容器类名,用以覆盖内部
className?: string;
// 事件回调
onClickOverlay?: () => void;
onCancel?: () => void;
onConfirm?: () => void;
}

export type DialogProps = NeutralDialogProps &
ExogenousDialogProps &
OpenTypeMixin &
ButtonMixin;

export const DefaultDialogManagerOptions: DialogProps = {
visible: false,
transition: 'scale',
width: '320px',
messageAlign: 'center',
zIndex: 100,
showConfirmButton: true,
showCancelButton: false,
confirmButtonText: '确认',
cancelButtonText: '取消',
confirmButtonColor: '#1989fa',
cancelButtonColor: '#333',
confirmButtonLoading: false,
cancelButtonLoading: false,
overlay: true,
overlayStyle: {},
};

export interface DialogManagerAlertOptions extends DialogProps {
asyncClose?: boolean;
closeOnClickOverlay?: boolean;
}
77 changes: 77 additions & 0 deletions packages/Dialog/Dialog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
.van-dialog {
top: 45% !important;
overflow: hidden;
width: 320px;
width: var(--dialog-width, 320px);
font-size: 16px;
font-size: var(--dialog-font-size, 16px);
border-radius: 16px;
border-radius: var(--dialog-border-radius, 16px);
background-color: #fff;
background-color: var(--dialog-background-color, #fff);
}
@media (max-width: 321px) {
.van-dialog {
width: 90%;
width: var(--dialog-small-screen-width, 90%);
}
}
.van-dialog__header {
text-align: center;
padding-top: 24px;
padding-top: var(--dialog-header-padding-top, 24px);
font-weight: 500;
font-weight: var(--dialog-header-font-weight, 500);
line-height: 24px;
line-height: var(--dialog-header-line-height, 24px);
}
.van-dialog__header--isolated {
padding: 24px 0;
padding: var(--dialog-header-isolated-padding, 24px 0);
}
.van-dialog__message {
overflow-y: auto;
text-align: center;
-webkit-overflow-scrolling: touch;
font-size: 14px;
font-size: var(--dialog-message-font-size, 14px);
line-height: 20px;
line-height: var(--dialog-message-line-height, 20px);
max-height: 60vh;
max-height: var(--dialog-message-max-height, 60vh);
padding: 24px;
padding: var(--dialog-message-padding, 24px);
}
.van-dialog__message-text {
word-wrap: break-word;
}
.van-dialog__message--has-title {
padding-top: 12px;
padding-top: var(--dialog-has-title-message-padding-top, 12px);
color: #646566;
color: var(--dialog-has-title-message-text-color, #646566);
}
.van-dialog__message--left {
text-align: left;
}
.van-dialog__message--right {
text-align: right;
}
.van-dialog__footer {
display: flex;
}
.van-dialog__button {
flex: 1;
}
.van-dialog__confirm,
.van-dialog__cancel {
border: 0 !important;
}
.van-dialog-bounce-enter {
transform: translate3d(-50%, -50%, 0) scale(0.7);
opacity: 0;
}
.van-dialog-bounce-leave-active {
transform: translate3d(-50%, -50%, 0) scale(0.9);
opacity: 0;
}
151 changes: 151 additions & 0 deletions packages/Dialog/DialogBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// packages
import React, { FunctionComponent, CSSProperties, isValidElement } from 'react';
import clsx from 'clsx';
import { View, Text } from 'remax/wechat';
// internal
import Popup from '../Popup';
import Button from '../Button';
import { Select } from '../tools/Switch';
import pickStyle from '../tools/pick-style';
// self
import { DialogProps } from './Dialog.constant';
import './Dialog.css';

// scope
const DialogBox: FunctionComponent<DialogProps> = (props) => {
const {
className,
visible,
transition,
width,
zIndex,
overlay,
overlayStyle,
style,
title,
message,
messageAlign,
showCancelButton,
showConfirmButton,
cancelButtonText,
cancelButtonColor,
cancelButtonLoading,
confirmButtonText,
confirmButtonColor,
confirmButtonLoading,
} = props;
// event handlers
const { onClickOverlay, onCancel, onConfirm } = props;
// open type
const {
lang,
sessionFrom,
sendMessageTitle,
sendMessageImg,
sendMessagePath,
showMessageCard,
appParameter,
openType,
onGetUserInfo,
onContact,
onGetPhoneNumber,
onError,
onLaunchApp,
onOpenSetting,
} = props;

const classnames = {
container: clsx(className, 'van-dialog'),
title: clsx('van-dialog__header', {
'van-dialog__header--isolated': !message,
}),
message: clsx(
'van-dialog__message',
`van-dialog__message--${messageAlign}`,
{
'van-dialog__message--has-title': !!title,
}
),
};
const stylesheets: Record<string, CSSProperties> = {
container: {
...(style || {}),
width,
},
cancel: pickStyle({
color: cancelButtonColor,
}),
confirm: pickStyle({
color: confirmButtonColor,
}),
};

return (
<Popup
visible={visible}
transition={transition}
overlay={overlay}
overlayStyle={overlayStyle}
zIndex={zIndex}
position="center"
style={stylesheets.container}
className={classnames.container}
onClickOverlay={onClickOverlay}
>
<Select in={!!title}>
<View className={classnames.title}>{title}</View>
</Select>
<Select in={typeof message === 'string'}>
<View className={classnames.message}>
<Text className="van-dialog__message-text">{message}</Text>
</View>
</Select>
<Select in={isValidElement(message)}>{message}</Select>

<View className="van-hairline--top van-dialog__footer">
<Select in={showCancelButton}>
<View className="van-dialog__button van-hairline--right">
<Button
size="large"
className="van-dialog__cancel"
style={stylesheets.cancel}
loading={cancelButtonLoading}
onClick={onCancel}
>
{cancelButtonText}
</Button>
</View>
</Select>
<Select in={showConfirmButton}>
<View className="van-dialog__button">
<Button
size="large"
className="van-dialog__confirm"
style={stylesheets.confirm}
loading={confirmButtonLoading}
openType={openType}
lang={lang}
appParameter={appParameter}
sessionFrom={sessionFrom}
sendMessageTitle={sendMessageTitle}
sendMessageImg={sendMessageImg}
sendMessagePath={sendMessagePath}
showMessageCard={showMessageCard}
onClick={onConfirm}
onGetUserInfo={onGetUserInfo}
onContact={onContact}
onGetPhoneNumber={onGetPhoneNumber}
onError={onError}
onLaunchApp={onLaunchApp}
onOpenSetting={onOpenSetting}
>
{confirmButtonText}
</Button>
</View>
</Select>
</View>
</Popup>
);
};

export default DialogBox;
102 changes: 102 additions & 0 deletions packages/Dialog/DialogManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* eslint-disable import/prefer-default-export */
//
import {
DialogProps,
DialogManagerAlertOptions,
DefaultDialogManagerOptions,
} from './Dialog.constant';

type Subscriber = (options: DialogProps) => void;

class DialogManager {
public options: DialogProps;

private queue: Subscriber[];

constructor(options: DialogProps) {
this.options = options;
this.queue = [];
}

private pipe(options: Partial<DialogProps>) {
if (this.queue.length === 0) {
throw new Error('DialogProvider required');
}

this.options = { ...this.options, ...options };
this.queue.forEach((callback) => {
callback(this.options);
});
}

// 预设选项管理
setDefaultOptions(options: Partial<DialogProps>) {
this.options = { ...this.options, ...options };
}

resetDefaultOptions() {
this.options = { ...DefaultDialogManagerOptions };
}

// 半自动模式
alert(options: Partial<DialogManagerAlertOptions>) {
const { asyncClose, closeOnClickOverlay, ...rest } = options;

return new Promise((resolve, reject) => {
this.pipe({
...rest,
visible: true,
onConfirm: () => {
// close dialog within auto mode
if (!asyncClose) {
this.close();
}
// fullfil promise
resolve();
},
onCancel: () => {
// close dialog within auto mode
if (!asyncClose) {
this.close();
}
// fullfil promise
reject();
},
onClickOverlay: () => {
if (closeOnClickOverlay) {
// close dialog within auto mode
if (!asyncClose) {
this.close();
}
// fullfil promise
reject();
}
},
});
});
}

// 全手动模式
any(options: Partial<DialogProps>) {
this.pipe(options);
}

close() {
// 关闭在前,重置在后
this.queue.forEach((callback) => {
callback({ ...this.options, visible: false });
});

// 重置内部参数
this.options = { ...DefaultDialogManagerOptions };
}

subscribe(callback: Subscriber) {
this.queue.push(callback);

// 释放订阅
return () => this.queue.splice(this.queue.indexOf(callback), 1);
}
}

export const Dialog = new DialogManager(DefaultDialogManagerOptions);
Loading

0 comments on commit 33e2d77

Please sign in to comment.