Skip to content

Commit

Permalink
feat: add dialog.alert
Browse files Browse the repository at this point in the history
  • Loading branch information
taoyage committed Aug 30, 2022
1 parent 839150b commit 279ed6c
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 11 deletions.
31 changes: 31 additions & 0 deletions packages/dialog/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';

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

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

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

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

export default alert;
Empty file added packages/dialog/confirm.tsx
Empty file.
8 changes: 6 additions & 2 deletions packages/dialog/dialog-action-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Button from '@/button';
export interface Action {
key: string;
text: React.ReactNode;
danger?: boolean;
color?: 'danger' | 'primary' | 'default';
disabled?: boolean;
onClick?: () => void | Promise<void>;
}
Expand All @@ -14,15 +14,19 @@ interface DialogActionButtonProps {
onAction: () => void | Promise<void>;
}

const classPrefix = 'ygm-dialog-button';

const DialogActionButton: React.FC<DialogActionButtonProps> = (props) => {
return (
<Button
key={props.action.key}
className={classPrefix}
onClick={props.onAction}
color={props.action.danger ? 'danger' : 'primary'}
color={props.action.color}
disabled={props.action.disabled}
block
shape="rectangular"
fill="none"
>
{props.action.text}
</Button>
Expand Down
44 changes: 39 additions & 5 deletions packages/dialog/dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import { useSpring, animated } from '@react-spring/web';

import Mask from '@/mask';
import type { MaskProps } from '@/mask';
import Mask, { MaskProps } from '@/mask';
import DialogActionButton, { Action } from '@/dialog/dialog-action-button';

import useIsomorphicLayoutEffect from '@/hooks/useIsomorphicLayoutEffect';

Expand All @@ -13,9 +13,18 @@ export interface DialogProps {
title?: React.ReactNode;
content?: React.ReactNode;
visible?: boolean;
actions?: Action[];
maskStyle?: MaskProps['style'];
/** 点击action后是否关闭 */
closeOnAction?: boolean;
/** Dialog关闭时的回调 */
onClose?: () => void;
/** 显示后回调 */
afterShow?: () => void;
/** 关闭后回调 */
afterClose?: () => void;
/** 点击action后回调 */
onAction?: (action: Action, index: number) => void | Promise<void>;
}

const classPrefix = 'ygm-dialog';
Expand All @@ -32,8 +41,13 @@ const Dialog: React.FC<DialogProps> = (props) => {
friction: 25,
clamp: true,
},
onReset: () => {
onRest: () => {
setActive(props.visible!);
if (props.visible) {
props.afterShow?.();
} else {
props.afterClose?.();
}
},
});

Expand All @@ -52,13 +66,32 @@ const Dialog: React.FC<DialogProps> = (props) => {

const renderContent = () => {
if (props.content) {
return <div className={`${classPrefix}-content`}>{props.content}</div>;
return (
<div className={`${classPrefix}-content`}>
<div>{props.content}</div>
</div>
);
}
return null;
};

const renderFooter = () => {
return <div className={`${classPrefix}-footer`}>footer</div>;
return (
<div className={`${classPrefix}-footer`}>
{props.actions!.map((action, index) => (
<DialogActionButton
key={action.key}
action={action}
onAction={async () => {
await Promise.all([action.onClick?.(), props.onAction?.(action, index)]);
if (props.closeOnAction) {
props.onClose?.();
}
}}
/>
))}
</div>
);
};

return (
Expand All @@ -79,6 +112,7 @@ const Dialog: React.FC<DialogProps> = (props) => {

Dialog.defaultProps = {
visible: false,
actions: [] as Action[],
};

Dialog.displayName = 'Dialog';
Expand Down
17 changes: 17 additions & 0 deletions packages/dialog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import InternalDialog from '@/dialog/dialog';
import alert from '@/dialog/alert';

export type { DialogProps } from '@/dialog/dialog';
export type { DialogAlertProps } from '@/dialog/alert';

type InternalDialogType = typeof InternalDialog;

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

const Dialog = InternalDialog as DialogInterface;

Dialog.alert = alert;

export default Dialog;
12 changes: 12 additions & 0 deletions packages/dialog/show.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';

import Dialog, { DialogProps } from '@/dialog/dialog';
import renderImperatively from '@/utils/render-imperatively';

export type DialogShowProps = Omit<DialogProps, 'visible'>;

function show(props: DialogShowProps) {
const handler = renderImperatively(<Dialog {...props} />);
}

export default show;
26 changes: 25 additions & 1 deletion packages/dialog/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ $class-prefix-dialog: 'ygm-dialog';
&-body {
background-color: var(--background-color);
border-radius: var(--border-radius);
overflow: hidden;
}

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

&-content {
padding: 8px 12px 20px;
padding: 26px 12px;
max-height: 70vh;
overflow-x: hidden;
overflow-y: auto;
font-size: var(--ygm-font-size-m);
line-height: 1.4;
color: var(--ygm-color-text);
display: flex;
justify-content: center;
text-align: center;
}

&-footer {
user-select: none;
display: flex;
align-items: stretch;
border-top: 0.5px solid var(--ygm-color-border);

> .ygm-dialog-button {
flex: 1;
padding: 10px;
border-radius: 0;
font-size: var(--ygm-font-size-m);
border-right: solid 0.5px var(--ygm-color-border);
line-height: 25px;
}

&:last-child {
border-right: none;
}
}
}
1 change: 0 additions & 1 deletion packages/toast/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const Toast: React.FC<ToastProps> = React.memo(({ icon, duration, content, after
const [_, setVisible] = React.useState<boolean>(true);

const iconElement = React.useMemo(() => {
console.log(icon);
if (icon === null || icon === undefined) return null;
switch (icon) {
case 'success':
Expand Down
32 changes: 32 additions & 0 deletions packages/utils/render-imperatively.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { renderToBody } from '@/utils/render';

export interface ElementProps {
visible?: boolean;
onClose?: () => void;
afterClose?: () => void;
}

const renderImperatively = (element: React.ReactElement<ElementProps>) => {
const Wraper = () => {
const [visible, setVisible] = React.useState(false);

const onClose = () => {
setVisible(false);
};

const afterClose = () => {
unmount();
};

React.useEffect(() => {
setVisible(true);
}, []);

return React.cloneElement(element, { ...element.props, visible, onClose, afterClose });
};

const unmount = renderToBody(<Wraper />);
};

export default renderImperatively;
23 changes: 23 additions & 0 deletions packages/utils/render.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import ReactDOM from 'react-dom/client';

export const render = (element: React.ReactElement, container: HTMLElement) => {
const root = ReactDOM.createRoot(container);
root.render(element);

const unmount = () => {
document.body.removeChild(container);
root.unmount();
};

return unmount;
};

export const renderToBody = (element: React.ReactElement) => {
const container = document.createElement('div');
document.body.appendChild(container);

const unmount = render(element, container);

return unmount;
};
20 changes: 18 additions & 2 deletions stories/dialog/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { Meta } from '@storybook/react';

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

import DemoWrap from '../../demos/demo-wrap';
Expand All @@ -18,12 +18,28 @@ export const Basic = () => {
return (
<DemoWrap>
<DemoBlock title="基础用法" style={{ padding: 20 }}>
<Button onClick={() => setVisible1(true)}>显示遮罩</Button>
<Button onClick={() => Dialog.alert({ content: '请优雅的书写代码' })}>显示</Button>
</DemoBlock>

<DemoBlock title="基础用法" style={{ padding: 20 }}>
<Button onClick={() => setVisible1(true)}>显示</Button>
<Dialog
title="标题"
content="代码是写出来给人看的,附带能在机器上运行"
visible={visible1}
onClose={() => setVisible1(false)}
closeOnAction
actions={[
{
key: 'cancel',
text: '取消',
},
{
key: 'confirm',
text: '确认',
color: 'primary',
},
]}
/>
</DemoBlock>
</DemoWrap>
Expand Down

0 comments on commit 279ed6c

Please sign in to comment.