zarm
Version:
基于 React 的移动端UI库
505 lines (460 loc) • 20.3 kB
Markdown
# Modal 模态框
## 基本用法
```jsx
import { useRef, useReducer } from 'react';
import { Modal, List, Button, Select } from 'zarm';
const initState = {
normal: {
visible: false,
},
hasFooter: {
visible: false,
},
closable: {
visible: false,
},
onlyBody: {
visible: false,
},
animation: {
visible: false,
animationType: 'fade',
},
customContainer: {
visible: false,
},
overlength: {
visible: false,
},
};
const reducer = (state, action) => {
const { type, key, animationType } = action;
switch (type) {
case 'visible':
return {
...state,
[key]: {
...state[key],
visible: !state[key].visible,
},
};
case 'animation':
return {
...state,
[key]: {
...state[key],
animationType,
},
};
default:
}
};
const Demo = () => {
const myRef = useRef();
const [state, dispatch] = useReducer(reducer, initState);
const toggle = (key) => dispatch({ type: 'visible', key });
return (
<>
<List>
<List.Item
title="普通"
suffix={
<Button size="xs" onClick={() => toggle('normal')}>
开启
</Button>
}
/>
<List.Item
title="自定义底部"
suffix={
<Button size="xs" onClick={() => toggle('hasFooter')}>
开启
</Button>
}
/>
<List.Item
title="遮罩层可关闭"
suffix={
<Button size="xs" onClick={() => toggle('closable')}>
开启
</Button>
}
/>
<List.Item
title="无头部,无底部"
suffix={
<Button size="xs" onClick={() => toggle('onlyBody')}>
开启
</Button>
}
/>
<List.Item
title="动画效果"
suffix={
<Button size="xs" onClick={() => toggle('animation')}>
开启
</Button>
}
>
<Select
value={state.animation.animationType}
dataSource={[
{ value: 'fade', label: '淡出淡入效果' },
{ value: 'zoom', label: '缩放效果' },
{ value: 'rotate', label: '旋转效果' },
{ value: 'door', label: '开关门效果' },
{ value: 'flip', label: '翻转效果' },
{ value: 'move-up', label: '向上移入效果' },
{ value: 'move-down', label: '向下移入效果' },
{ value: 'move-left', label: '向左移入效果' },
{ value: 'move-right', label: '向右移入效果' },
{ value: 'slide-up', label: '向上滑入效果' },
{ value: 'slide-down', label: '向下滑入效果' },
{ value: 'slide-left', label: '向左滑入效果' },
{ value: 'slide-right', label: '向右滑入效果' },
]}
itemRender={(data) => data && `${data.label}(${data.value})`}
displayRender={(selected) => selected.map((item) => item && item.label)}
onConfirm={(selected) => {
dispatch({
type: 'animation',
key: 'animation',
animationType: selected[0],
});
}}
/>
</List.Item>
<List.Item
suffix={
<Button size="xs" onClick={() => toggle('customContainer')}>
开启
</Button>
}
>
挂载到指定 DOM 节点
</List.Item>
<List.Item
suffix={
<Button size="xs" onClick={() => toggle('overlength')}>
开启
</Button>
}
>
超长内容
</List.Item>
</List>
<div id="test-div" style={{ position: 'relative', zIndex: 1 }} ref={myRef} />
<Modal visible={state.normal.visible} title="标题" closable onClose={() => toggle('normal')}>
模态框内容
</Modal>
<Modal
title="标题"
visible={state.hasFooter.visible}
footer={
<Button block shape="rect" theme="primary" onClick={() => toggle('hasFooter')}>
确定
</Button>
}
>
模态框内容
</Modal>
<Modal
visible={state.closable.visible}
title="标题"
maskClosable
onClose={() => toggle('closable')}
>
点击遮罩层关闭
</Modal>
<Modal visible={state.onlyBody.visible} maskClosable onClose={() => toggle('onlyBody')}>
无头部,无底部
</Modal>
<Modal
visible={state.animation.visible}
animationType={state.animation.animationType}
maskClosable
onClose={() => toggle('animation')}
>
<div style={{ height: 100 }}>
当前使用的动画类型animationType:'{state.animation.animationType}'
</div>
</Modal>
<Modal
visible={state.customContainer.visible}
maskClosable
onClose={() => toggle('customContainer')}
mountContainer={() => myRef.current}
>
挂载到指定dom节点
</Modal>
<Modal
visible={state.overlength.visible}
title="标题"
closable
onClose={() => toggle('overlength')}
maskClosable
>
{Array.from(Array(100).fill(0)).map((_, index) => (
<div key={index}>
模态框内容
<br />
</div>
))}
</Modal>
</>
);
};
ReactDOM.render(<Demo />, mountNode);
```
## 带操作按钮
```jsx
import { useState } from 'react';
import { Modal, List, Button } from 'zarm';
const Demo = () => {
const [visible, setVisible] = useState(false);
const toggle = () => setVisible(!visible);
return (
<>
<List>
<List.Item
title="自定义操作按钮"
suffix={
<Button size="xs" onClick={toggle}>
开启
</Button>
}
/>
</List>
<Modal
visible={visible}
title="标题"
actions={[
{
key: 'online',
text: '在线阅读',
theme: 'default',
},
{
key: 'download',
text: '下载文件',
theme: 'default',
disabled: true,
},
[
{
key: 'cancel',
text: '取消',
},
{
key: 'delete',
text: '删除',
bold: true,
theme: 'danger',
},
],
]}
onAction={async (action) => {
switch (action.key) {
case 'cancel':
toggle();
break;
default:
// 模拟异步操作
await new Promise((resolve) => setTimeout(resolve, 3000));
toggle();
}
console.log(action);
}}
>
模态框内容
</Modal>
</>
);
};
ReactDOM.render(<Demo />, mountNode);
```
## 警告框 Alert
```jsx
import { List, Button, Modal, Toast } from 'zarm';
const Demo = () => {
return (
<List>
<List.Item
title="静态方法关闭"
suffix={
<Button
size="xs"
onClick={() => {
Modal.alert({
className: 'test',
title: '警告框标题',
content: '这里是警告框的内容部分',
onConfirm: () => {
console.log('点击确认');
},
});
}}
>
开启
</Button>
}
/>
<List.Item
title="使用 Promise 关闭"
suffix={
<Button
size="xs"
onClick={() => {
Modal.alert({
title: '警告框标题',
content: '这里是警告框的内容部分,点击关闭按钮,将触发 Promise 关闭警告框',
onConfirm: async () => {
await new Promise((resolve) => setTimeout(resolve, 3000));
Toast.show({ content: '提交成功' });
},
});
}}
>
开启
</Button>
}
/>
</List>
);
};
ReactDOM.render(<Demo />, mountNode);
```
## 确认框 Confirm
```jsx
import { List, Button, Modal, Toast } from 'zarm';
const Demo = () => {
return (
<List>
<List.Item
title="静态方法关闭"
suffix={
<Button
size="xs"
onClick={() => {
Modal.confirm({
title: '确认信息',
content: '这里是确认框的内容部分',
onCancel: () => {
console.log('点击cancel');
},
onConfirm: () => {
console.log('点击ok');
},
});
}}
>
开启
</Button>
}
/>
<List.Item
title="使用 Promise 关闭"
suffix={
<Button
size="xs"
onClick={() => {
Modal.confirm({
title: '确定要删除吗?',
content: '这里是确认框的内容部分,点击确定按钮,将触发 Promise 关闭确认框',
onConfirm: async () => {
await new Promise((resolve) => setTimeout(resolve, 3000));
Toast.show({ content: '提交成功' });
},
});
}}
>
开启
</Button>
}
/>
</List>
);
};
ReactDOM.render(<Demo />, mountNode);
```
## API
| 属性 | 类型 | 默认值 | 说明 |
| :---------------- | :------------------------------------------------------------------- | :------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| shape | string | 'radius' | 形状,可选值 `rect`、`radius` |
| visible | boolean | false | 是否显示 |
| animationType | string | 'fade' | 动画效果,可选值 `fade`, `door`, `flip`, `rotate`, `zoom`,`move-up`, `move-down`, `move-left`, `move-right`,`slide-up`, `slide-down`, `slide-left`, `slide-right` |
| animationDuration | number | 200 | 动画执行时间(单位:毫秒) |
| width | string | number | '70%' | 宽度 |
| mask | boolean | true | 是否展示遮罩层 |
| maskType | string | 'normal' | 遮罩层的类型,可选值 `transparent`, `normal` |
| maskClosable | boolean | false | 是否点击遮罩层时关闭,需要和 onCancel 一起使用 |
| closable | boolean | false | 右上角是否显示关闭按钮,需要和 onCancel 一起使用 |
| onClose | () => void | - | maskClosable 或 closable 为 true 时,点击遮罩或者右上角关闭按钮触发的函数 |
| title | ReactNode | - | 标题 |
| footer | ReactNode | - | 弹窗底部内容 |
| actions | (ModalActionProps \| ModalActionProps[])[] | [] | 操作按钮配置 |
| onAction | (action: ModalActionProps, index: number) => void \| Promise\<void\> | - | 点击操作按钮后触发的函数 |
| destroy | boolean | true | 弹层关闭后是否移除节点 |
| afterOpen | () => void | - | 模态框打开后的回调 |
| afterClose | () => void | - | 模态框关闭后的回调 |
| mountContainer | MountContainer | () => document.body | 指定 Modal 挂载的 HTML 节点 |
### ModalActionProps 操作按钮属性
| 属性 | 类型 | 默认值 | 说明 |
| :------- | :--------- | :-------- | :---------------------------------------------- |
| text | ReactNode | - | 按钮文字 |
| theme | string | 'primary' | 按钮主题,可选值 `default`、`primary`、`danger` |
| disabled | boolean | false | 按钮是否禁用 |
| bold | boolean | false | 是否加粗 |
| onClick | () => void | - | 按钮点击后触发的回调函数 |
## 指令式 API
```tsx
// 显示警告框,点击“确定”按钮执行 onConfirm 方法,如需做更多操作,参考下方 Confirm 的例子
const alert = Modal.alert({
title: '警告框标题',
content: '这里是警告框的内容部分',
});
// 显示确认框,若关闭时需要 Promise,onConfirm、onCancel 均支持 Promise
const confirm = Modal.confirm({
title: '确认框标题',
content: '这里是确认框的内容部分,点击确定按钮,将触发 Promise 关闭确认框',
onConfirm: () => {
return fetch.get('xxx.api').then((res) => {
if(res.code === 0) {
return true; // 关闭弹窗
} else {
return false; // 阻止弹窗关闭
}
}).catch(...);
}
});
```
| 属性 | 类型 | 默认值 | 说明 |
| :---------- | :--------- | :---------------------------- | :------------------------------------------ |
| title | ReactNode | - | 弹出框的标题 |
| content | ReactNode | - | 弹出框的内容 |
| cancelText | ReactNode | '取消' | 取消按钮的内容 |
| confirmText | ReactNode | '确定' | 确定按钮的内容 |
| onConfirm | () => void | - | 点击“确定”后的回调函数 |
| onCancel | () => void | - | 点击“取消”后的回调函数 |
## CSS 变量
| 属性 | 默认值 | 说明 |
| :------------------------- | :---------------------------------- | :------------------------- |
| --background | 'rgb(242, 242, 242)' | 背景色 |
| --border-radius | '14px' | 圆角大小 |
| --shadow | '0 7px 21px var(--za-color-shadow)' | 阴影样式 |
| --title-font-size | '17px' | 标题字体大小 |
| --title-font-weight | 500 | 标题字体粗细 |
| --title-text-color | 'var(--za-color-text)' | 标题字体颜色 |
| --close-size | '20px' | 关闭图标字体大小 |
| --close-color | '#ccc' | 关闭图标颜色 |
| --close-active-color | '#999' | 关闭图标激活状态颜色 |
| --body-font-size | '13px' | 内容字体大小 |
| --body-text-color | 'var(--za-color-text)' | 内容字体颜色 |
| --body-padding | '16px' | 内容内边距 |
| --button-background | 'transparent' | 操作按钮背景 |
| --button-height | '44px' | 操作按钮高度 |
| --button-font-size | '17px' | 操作按钮字体大小 |
| --button-font-weight | 500 | 操作按钮字体粗细 |
| --button-text-color | 'var(--za-theme-primary)' | 操作按钮字体颜色 |
| --button-active-background | 'var(--za-background-active)' | 操作按钮选中背景 |
| --button-disabled-opacity | 'var(--za-opacity-disabled)' | 操作按钮禁用状态时的透明度 |