Skip to content

Commit

Permalink
feat: add dialog.confirm
Browse files Browse the repository at this point in the history
  • Loading branch information
taoyage committed Sep 12, 2022
1 parent 279ed6c commit b0cdd12
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 26 deletions.
37 changes: 32 additions & 5 deletions packages/button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React from 'react';
import cx from 'classnames';

import SpinnerLoading from '@/spinner-loading';
import { isPromise } from '@/utils/validate';

import './styles/index.scss';

export interface ButtonProps {
Expand All @@ -10,14 +13,36 @@ export interface ButtonProps {
fill?: 'solid' | 'outline' | 'none';
children?: React.ReactNode;
className?: string;
onClick?: () => void;
onClick?: (event: React.MouseEvent<HTMLDivElement>) => Promise<void> | unknown;
block?: boolean;
disabled?: boolean;
loading?: boolean | 'auto';
loadingIcon?: React.ReactNode;
}

const classPrefix = 'ygm-button';

const Button: React.FC<ButtonProps> = React.memo((props) => {
const Button: React.FC<ButtonProps> = (props) => {
const [innerLoading, setInnerLoading] = React.useState(false);
const loading = props.loading === 'auto' ? innerLoading : props.loading;

const onButtonClick = async (e: React.MouseEvent<HTMLDivElement>) => {
if (!props.onClick) return;

const promise = props.onClick(e);

if (isPromise(promise)) {
try {
setInnerLoading(true);
await promise;
setInnerLoading(false);
} catch (e) {
setInnerLoading(false);
throw e;
}
}
};

return (
<div
className={cx(
Expand All @@ -32,18 +57,20 @@ const Button: React.FC<ButtonProps> = React.memo((props) => {
[`${classPrefix}-disabled`]: props.disabled,
}
)}
onClick={props.onClick}
onClick={onButtonClick}
>
{props.children}
{loading ? <div className={`${classPrefix}-loading-wrap`}>{props.loadingIcon}</div> : props.children}
</div>
);
});
};

Button.defaultProps = {
color: 'default',
size: 'middle',
shape: 'default',
fill: 'solid',
loading: false,
loadingIcon: <SpinnerLoading size={16} />,
};

Button.displayName = 'Button';
Expand Down
11 changes: 11 additions & 0 deletions packages/button/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,18 @@ $class-prefix-button: 'ygm-button';
&-shape-rounded {
border-radius: 1000px;
}

&-shape-rectangular {
border-radius: 0;
}

&-loading-wrap {
display: flex;
height: 1.4em;
align-items: center;
justify-content: center;
> .ygm-spinner-loading {
opacity: 0.6;
}
}
}
31 changes: 17 additions & 14 deletions packages/dialog/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,29 @@ import show from '@/dialog/show';

export type DialogAlertProps = Omit<DialogProps, 'visible' | 'closeOnAction' | 'actions'> & {
confirmText?: React.ReactNode;
onConfirm?: () => void;
onConfirm?: () => void | Promise<void>;
};

const alert = (props: DialogAlertProps) => {
const { confirmText = '确认' } = props;

return show({
...props,
closeOnAction: true,
actions: [
{
key: 'confirm',
text: confirmText,
color: 'primary',
return new Promise<void>((resolve) => {
show({
...props,
closeOnAction: true,
actions: [
{
key: 'confirm',
text: confirmText,
color: 'primary',
},
],
onAction: props.onConfirm,
onClose: () => {
props.onClose?.();
resolve();
},
],
onAction: props.onConfirm,
onClose: () => {
props.onClose?.();
},
});
});
};

Expand Down
43 changes: 43 additions & 0 deletions packages/dialog/confirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';

import { DialogProps } from '@/dialog/dialog';
import show from '@/dialog/show';

export type DialogConfirmProps = Omit<DialogProps, 'visible' | 'closeOnAction' | 'actions'> & {
confirmText?: React.ReactNode;
cancelText?: React.ReactNode;
onConfirm?: () => void | Promise<void>;
onCancel?: () => void | Promise<void>;
};

const confirm = (props: DialogConfirmProps) => {
const { confirmText = '确定', cancelText = '取消' } = props;

return new Promise<boolean>((resolve) => {
show({
...props,
closeOnAction: true,
actions: [
{
key: 'cancel',
text: cancelText,
onClick: async () => {
await props.onCancel?.();
resolve(false);
},
},
{
key: 'confirm',
text: confirmText,
color: 'primary',
onClick: async () => {
await props.onConfirm?.();
resolve(true);
},
},
],
});
});
};

export default confirm;
1 change: 1 addition & 0 deletions packages/dialog/dialog-action-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const DialogActionButton: React.FC<DialogActionButtonProps> = (props) => {
block
shape="rectangular"
fill="none"
loading="auto"
>
{props.action.text}
</Button>
Expand Down
2 changes: 1 addition & 1 deletion packages/dialog/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const Dialog: React.FC<DialogProps> = (props) => {
scale: props.visible ? 1 : 0.8,
opacity: props.visible ? 1 : 0,
config: {
mass: 1.2,
mass: 2.2,
tension: 200,
friction: 25,
clamp: true,
Expand Down
3 changes: 3 additions & 0 deletions packages/dialog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import InternalDialog from '@/dialog/dialog';
import alert from '@/dialog/alert';
import confirm from '@/dialog/confirm';

export type { DialogProps } from '@/dialog/dialog';
export type { DialogAlertProps } from '@/dialog/alert';
Expand All @@ -8,10 +9,12 @@ type InternalDialogType = typeof InternalDialog;

export interface DialogInterface extends InternalDialogType {
alert: typeof alert;
confirm: typeof confirm;
}

const Dialog = InternalDialog as DialogInterface;

Dialog.alert = alert;
Dialog.confirm = confirm;

export default Dialog;
5 changes: 3 additions & 2 deletions packages/dialog/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ $class-prefix-dialog: 'ygm-dialog';
width: auto;
min-width: var(--min-width);
max-width: var(--max-width);
transform: translate(-50%, -50%);
transform: translate3d(-50%, -50%, 0);
}

&-body {
Expand All @@ -36,12 +36,13 @@ $class-prefix-dialog: 'ygm-dialog';
}

&-content {
width: auto;
padding: 26px 12px;
max-height: 70vh;
overflow-x: hidden;
overflow-y: auto;
font-size: var(--ygm-font-size-m);
line-height: 1.4;
line-height: 20px;
color: var(--ygm-color-text);
display: flex;
justify-content: center;
Expand Down
2 changes: 1 addition & 1 deletion packages/popup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const Popup: React.FC<PopupProps> = React.memo((props) => {
<Mask visible={props.visible} onMaskClick={props.onMaskClick} />

<animated.div
className={cx(`${classPrefix}-body`, `${classPrefix}-${props.position}`)}
className={cx(`${classPrefix}-body`, `${classPrefix}-${props.position}`, props.className)}
style={{
...props.style,
transform: percent.to((v) => {
Expand Down
3 changes: 3 additions & 0 deletions packages/utils/validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isPromise(obj: unknown): obj is Promise<unknown> {
return !!obj && typeof obj === 'object' && typeof (obj as any).then === 'function';
}
33 changes: 30 additions & 3 deletions stories/dialog/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { Meta } from '@storybook/react';

import Dialog from '@/dialog';
import Button from '@/button';
import Space from '@/space';

import DemoWrap from '../../demos/demo-wrap';
import DemoBlock from '../../demos/demo-block';
import { sleep } from '@/pull-to-refresh/utils';

const DialogStory: Meta = {
title: '反馈/Dialog 弹出框',
Expand All @@ -18,11 +20,36 @@ export const Basic = () => {
return (
<DemoWrap>
<DemoBlock title="基础用法" style={{ padding: 20 }}>
<Button onClick={() => Dialog.alert({ content: '请优雅的书写代码' })}>显示</Button>
<Space direction="vertical">
<Button onClick={() => Dialog.alert({ content: '请优雅的书写代码' })}>弹窗提示</Button>
<Button
onClick={() => Dialog.confirm({ content: '请优雅的书写代码弹窗确认弹窗确认弹窗确认弹窗确认弹窗确认' })}
>
弹窗确认
</Button>
</Space>
</DemoBlock>

<DemoBlock title="基础用法" style={{ padding: 20 }}>
<Button onClick={() => setVisible1(true)}>显示</Button>
<DemoBlock title="异步调用" style={{ padding: 20 }}>
<Space direction="vertical">
<Button
onClick={() => {
Dialog.confirm({
content: '请优雅的书写代码',
onConfirm: async () => {
await sleep(3000);
console.log('success');
},
});
}}
>
异步确认
</Button>
</Space>
</DemoBlock>

<DemoBlock title="组件调用" style={{ padding: 20 }}>
<Button onClick={() => setVisible1(true)}>组件调用</Button>
<Dialog
title="标题"
content="代码是写出来给人看的,附带能在机器上运行"
Expand Down

0 comments on commit b0cdd12

Please sign in to comment.