@darkui/popup
v0.0.7
Published
* 基于react弹窗组件 * 支持自定义进场离场动画 * 支持自定义弹窗背景效果 * 支持top,left,right,bottom方向弹出 * 支持Show命令式调用 * 支持弹窗队列功能 * 提供toast/dialog/actionSheet,ui组件 * 提供核心api,自定义拓展任意框架/ui 弹窗组件的Show/Queue功能
Downloads
4
Readme
Popup
- 基于react弹窗组件
- 支持自定义进场离场动画
- 支持自定义弹窗背景效果
- 支持top,left,right,bottom方向弹出
- 支持Show命令式调用
- 支持弹窗队列功能
- 提供toast/dialog/actionSheet,ui组件
- 提供核心api,自定义拓展任意框架/ui 弹窗组件的Show/Queue功能
中文文档
快速安装
npm i @darkui/popup --save
引入样式
// 引入样式文件
import "@darkui/popup/es/style.css";
## 基础模式调用
```tsx
import React, { useState } from 'react';
import { Popup } from '@darkui/popup';
export default () => {
const [visibility, setVisibility] = useState(false)
return <div>
<button onClick={() => setVisibility(true)}>打开弹窗</button>
<Popup
visibility={visibility}
onCancel={() => setVisibility(false)}
borderRadius={10}
>
<div style={{padding:'20px 30px'}}>
Hello,world
</div>
</Popup>
</div>
}
自定义背景元素
Popup
中接受一个bg
字段,bg
可以为一个颜色字符串,又或者为一个jsx函数
当遇到背景不只是颜色而参杂着元素时候,可以通过传入一个jsx函数形式替换Popup
原有的背景
type bg = string | () => JSX.Element;
<Popup
visibility={visibility}
onCancel={() => {
setVisibility(false);
}}
bg={() => <div style={{
width: '100%',
height: '100%',
background: 'rgba(0,0,0,0.4)',
backdropFilter: 'blur(10px)'
}}>
</div>}
></Popup>
当bg为函数时,会接受一个status('enter'|'leave')参数,status为进场或者立场状态
export default {
bg: (status: 'enter'|'leave') => {
const animation =
status === 'enter' ? styles['bg-enter-animation'] : styles['bg-leave-animation'];
return <div style={{ animation: `${animation} .4s linear forwards`, }} />
}
}
自定义进场离场动画
Popup
接收一个animation字段用于自定义进场离场动画功能
需注意:如果是队列queue
模式下,只有最后一个会触发离场动画
bgDuration
为背景的过滤时间,如果自定义了背景元素,此属性不在生效
interface Animate {
animation?: string;
bgDuration?: number;
}
<Popup
visibility={false}
onCancel={() => {
// setVisibility(false);
}}
animate={{
enter: {
animation: styles['test-enter-animation']+' .3s forwards',
bgDuration: 300,
},
leave: {
animation: styles['test-leave-animation']+' .3s forwards',
bgDuration: 4000,
}
}}
></Popup>
接入animate.css
animate.css地址https://animate.style/
Popup
默认采用的是animation形式动画,animate.css默认是class绑定动画,如果要在Popup
中使用animate动画,需要传入css库中的动画name
<Popup
visibility={false}
onCancel={() => {
// setVisibility(false);
}}
animate={{
enter: {
animation: 'flipInX .3s',
bgDuration: 300,
},
leave: {
animation: 'flipOutX .3s',
bgDuration: 4000,
}
}}
></Popup>
接收参数
Show 命令式 基础使用
Show
方法返回一个promise对象,promise的值为instance
Show
与常规弹窗不同,当通过此方法打开一个弹窗之后,如果再次调用Show
方法,默认情况下第一个弹窗会缓存下来当关闭第二个弹窗时候,会从新打开第一个弹窗,这不是一个Bug
,可以通过传递replace
为true
来阻止此特征,或者第二个弹窗关闭时候调用interface.closeAll()
/* eslint-disable */
import React from 'react';
import { Show } from '@darkui/popup';
export default () => {
const popup1 = async (direction?: any) => {
const instance = await Show({
direction,
content: () => <h1 style={{color:'#666'}}>测试1-content</h1>,
onCancel() {
instance.close();
},
});
};
return <div>
<button onClick={() => popup1()}>打开弹窗</button>
</div>
}
自定义关闭弹窗行为
Show
方式如果需要自定义关闭弹窗行为,可通过onCancel事件
onCancel接收instance参数,与Show返回的instance一致
Show({
onCancel(instance) {
alert('自定义弹窗')
instance.close();
},
content: () => <div>自定义弹窗关闭</div>
})
instance
通过Show会返回一个instance对象,此对象为当前弹窗内容的实例,可以用来控制关闭当前弹窗
import React from 'react';
function createPopup(text: string) {
return Show({
content: <h1>{text}</h1>
});
}
const instanceList = [];
instanceList.push(await createPopup('第一个弹窗'));
setTimeout(async () => {
instanceList.push(await createPopup('第二个弹窗'));
}, 1000)
setTimeout(async () => {
instanceList[1].close();
// 此时弹窗3关闭时候,会返回到第一个弹窗
instanceList.push(await createPopup('第三个弹窗'));
}, 2000)
关于instance的属性如下
export interface AlterInstance {
/** 当前弹窗的key */
key: string;
/** 关闭当前弹窗 */
close: () => void;
/** 关闭某一个弹窗 */
closeTo: (instance: AlterInstance) => void;
/** 关闭所有弹窗 */
closeAll: () => void;
/** 当前弹窗的属性 */
props: any;
/**
* 当前弹窗控制器
*/
controller: ShowController;
}
Show
接收第二个参数为当前弹窗的队列控制器
interface Options {
controller?: ShowController;
keep?: boolean;
}
默认情况下,所有的Show都还在同一个controller中运行,当已经打开一个弹窗,需要在从新打开一个新弹窗,但是又不关闭当前弹窗时候,可以通过传入一个新的controller形式调用
const controller = new ShowController();
Show({
}, { controller })
Options.keep
表示是否后续的弹窗都运行在传日的控制器当中
接收参数
Show基本参数与Popup保持一致,但是不包含onConfirm/visibility
Queue 队列
队列形式,是基于Show
的二次封装,通过传入多个参数形式,管理展示弹窗
适用于复杂弹窗队列显示,每个弹窗都可以获取到前面所有抛出的内容信息
import { getQueueInfo, Queue } from '@darkui/popup';
Queue([
{
options: {
onCancel() {
const instance = getQueueInfo();
instance.$close('第一个弹窗发出的参数');
},
},
render: () => {
const instance = getQueueInfo();
return (
<div>
<h1>aaaa</h1>
<button
onClick={() => {
instance.$jump(2, 222);
}}
>
跳到ccc
</button>
<button
onClick={() => {
toast('已添加,点击空白处,跳到下一个弹窗');
instance.$replace(() => <div>替换的弹窗</div>, 1);
}}
>
替换二为新的弹窗
</button>
<button
onClick={() => {
toast('已添加,点击空白处,跳到下一个弹窗');
instance.$append(() => <div>新的弹窗</div>, 1);
}}
>
插入一个新的弹窗
</button>
</div>
);
},
},
() => {
const instance = getQueueInfo();
return (
<div>
<h1>bbbb</h1>
<button
onClick={() => {
instance.$close();
}}
>
手动调到下一个
</button>
<br />
<button
onClick={() => {
instance.$closeAll();
}}
>
关闭全部
</button>
</div>
);
},
() => {
const instance = getQueueInfo();
console.log(instance);
return <h1>cccc</h1>;
},
() => <h1>dddd</h1>,
]).then((result) => {
console.log(result);
toast('关闭了所有');
});
Queue
接收一个队列,并返回一个promise对象
type RenderFn = (props: any) => JSX.Element;
interface QueueItem {
/** Popup.show的所选参数 */
options?: Omit<PopupAlterInterface, 'content'>;
props?: Record<string, any>;
/**
* 渲染函数
*/
render: RenderFn;
}
useQueueInfo
在使用Popup.queue
时候,Popup.queue
对外抛出了一个useQueueInfo
的钩子,通过useQueueInfo
可以获取到当前弹窗的实例,以及上一个弹窗所返回的内容,同时可以指定传给下一个弹窗的内容
useQueueInfo
并非完全意义上的react hook,它也可以被用在options的onCancel中
考虑到eslint问题,会导致useQueueInfo
在普通fun报错,因此拓展了getQueueInfo
函数以便使用
const useQueueInfo: () => {
/** 当前弹窗的实例对象 */
// instance: AlterInstance;
/** key */
$key: string;
/** 上一个弹窗发出的信息 */
message?: any[];
/** 上一个弹窗的实例 */
// prevInstance: AlterInstance;
/** 向下一个弹窗传递参数 */
$close: (data?: any) => void;
$controller: ShowController;
$append: (data, index) => void;
/** 关闭当前弹窗 */
$close: () => void;
/** 关闭所有的弹窗 */
$closeAll: () => void;
$getCurrentQueue: () => any;
/** 跳转到指定弹窗,message为下一个弹窗接收的消息 */
$jump: (index, message) => void;
/** 删除指定弹窗 */
$remove: (index) => void;
/** 替换指定弹窗,data为弹窗信息 */
$replace: (index, data) => void;
/** 停止弹窗队列,result为.catch接收的异常消息 */
$stop: (result) => void;
/** 虽有队列弹窗所抛出的信息 */
message: any[];
props: {options: any, render: () => JSX.Element}
}
自定义关闭处理
在队列中想要控制用户点击黑色背景部分逻辑时候可以通过添加options.onCancel
方法来实现
const testDemo = Queue([
{
options: {
onCancel() {
const result = useQueueInfo();
result.$close({
// 传参数
type: 'cancel',
other: { xxx: 'xxx' }
})
}
},
render: () => <div></div>
},
() => {
const info = useQueueInfo();
return <div>{JSON.stringify(info.message)}</div>
}
])
ShowController 控制器
ShowController
是Show方法中最核心的逻辑,继承自ShowControllerCore
,拓展ShowControllerCore
实现了react
框架下的Popup
组件的命令式调用
Show
方法的本质是,通过创建一个ShowController
对象,调用ShowController
内的append/replace方法,向控制器追加弹窗内容,以此形式来实现弹窗控制。
基础调用
import { ShowController, CreateRoot } from ".."
const controller = new ShowController(
CreateRoot(),
{ destory: false }
);
controller.append({
content: <div>Hello,world</div>
});
ShowController
共接收两个参数,皆为可选参数,第一个参数为基础弹窗组件,第二个为控制器配置
CreateRoot
是组件内置react版本的弹窗底层组件,可以通过CreateRoot
来兼容市面上其他的react 弹窗ui库
第二个参数暂时只有destory
字段,用于设置控制器是否自动销毁
demo: 通过ShowControllerCore兼容vue3框架的modal
这里选择的是vue3版本的vant ui
import { CreateRoot } from '@maui/popup/es/show/vue';
import { ShowControllerCore } from '@maui/popup/es/show';
// 继承核心控制器,写入vue3框架核心创建于销毁方法
class VueShowController extends ShowControllerCore {
unmount() {
this.$other.$app.unmount();
}
createRoot() {
const { createApp, h } = Vue;
this.$other.$app = createApp(
h(this.Root, {
controller: this,
onDestory: () => {
if (this.options.destory !== false) {
this.destory();
}
},
}),
);
this.$other.$app.mount(this.$el);
return this;
}
}
// 通过CreateRoot以及createShow创建vue3+vant modal的命令方法
function createVueShow() {
// 需要注意vue3版本与react版本使用不同的CreateRoot
const Root = CreateRoot({
visibilityName: 'show',
cancelEventName: 'onClickOverlay',
destoryEventName: 'onClosed',
component: vant.Popup as any,
});
return createShow<{
round?: boolean;
transition?: string;
style?: any;
content: any;
}>(Root, VueShowController);
}
CreateRoot
CreateRoot
作为一个中转站,处理了不同ui 之前变量以及事件名各不相同的弊端,通过传入当前ui的变量名称,内部会进行重新调整,以保证符合Popup
的运行
以下为CreateRoot
所接受的参数
export interface ShowPopupProps {
format?: (props: any) => any;
controller?: ShowController;
onDestory?: () => void;
component?: ReactElement<any, any>;
cancelEventName?: string;
destoryEventName?: string;
visibilityName?: string;
other?: any;
}
const Root = CreateRoot(props: ShowPopupProps);
const controller = new ShowController(Root);
当前项目中暂时只有react以及vue3版本,其他版本会在后续中补充