背景
在日常的需求开发中我们经常需要用到弹窗,那么在我们构建弹窗时,在引用组件是都需要引入组件DOM,然后通过事件来控制组件的影藏显示,调用也不是很方便;
本组件使用事前注册DOM的方式,业务方在使用时刻直接调用暴露的方法即可,无需再引用DOM。
示例
组件使用示例
import newDialog from "*./components/newDialog"
newDialog.open({
title: "退出发布",
content: '是否需要保存草稿箱?',
btn: [{ text: "不保存" },{ text: "保存" }],
callback: (event)=>{
if(event.result == 2){
//TODO
}else {
//TODO
}
}
});
API
参数 | 说明 | 类型 | 是否必填 | |
---|---|---|---|---|
title | 弹窗标题 | String | √ | |
content | 弹窗展示内容 | String\ | ReactDom | √ |
btn | 弹窗按钮可配置一个或者两个 | Array | √ | |
callback | 弹窗响应回调方法 | Function | √ |
弹窗按钮说明
//弹窗按钮是json数组配置的 最多配置两个
btn: [{ text: "不保存" },{ text: "保存" }],
//每个按钮只需要配置text按钮文案即可 样式和颜色都集成到css
//如果配置一个按钮就是底部长按钮 配置两个根据配置的数组顺序从左向右展示
回调参数
//点击按钮会返回回调数据并关闭弹出
{
result: 1/2 //返回按钮的索引值
}
完整代码
js代码newDialog.tsx
'use strict';
import './newDialog.scss';
import * as React from 'react';
import cs from "classnames";
import * as ReactDOM from 'react-dom';
import { isIphoneX } from './../../util/util';
/**
* @description 解决移动端滚动穿透事件
**/
const ModalHelper = (function (bodyCls) {
var scrollTop;
return {
afterOpen: function () {
scrollTop = document.scrollingElement.scrollTop;
document.body.classList.add(bodyCls);
document.body.style.top = -scrollTop + 'px';
},
beforeClose: function () {
document.body.classList.remove(bodyCls);
// scrollTop lost after set position:fixed, restore it back.
document.scrollingElement.scrollTop = scrollTop;
}
};
})('modal-open');
interface IAppProps {
title?: string;
content?: any;
btn?: any;
callback?: Function;
}
interface IAppState {
showToast?: Boolean;
title?: string;
content?: any;
btn?: any;
callback?: Function;
isIPhoneX?: Boolean;
}
let DialogDiv;
class Dialog extends React.Component {
constructor(props: IAppProps) {
super(props);
this.state = {
showToast: true,
isIPhoneX: false
};
}
componentWillMount() {
let isIPhoneX = isIphoneX();
this.setState({
isIPhoneX
});
}
static open = (options) => {
const { ...props } = options || {};
DialogDiv = document.createElement('div');
setTimeout(()=>{
document.body.appendChild(DialogDiv);
ReactDOM.render( ,DialogDiv);
},100);
ModalHelper.afterOpen();
}
close = (param) => {
let result = param +1;
this.props.callback({ result: result });
this.setState({
showToast: false
});
document.body.removeChild(DialogDiv);
ModalHelper.beforeClose();
}
render() {
const { showToast, isIPhoneX } = this.state;
const { title, content, btn } = this.props;
let _twoBtn = btn.length > 1 ? true : false;
return (
{title}
{content}
{btn.map((item, idx) => {
return (
{
this.close(idx);
// item.callback();
}}
className={cs('dialog-btn', {
margin_r: _twoBtn && idx == 0,
default: _twoBtn && idx == 0
})}>
{item.text}
);
})}
)
}
}
export default Dialog;
css代码newDialog.scss
$baseFontSize: 16px !default;
// pixels to rems
@function pxToRem($px) {
@return $px / $baseFontSize * 1rem;
}
.dialog-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, .7);
display: none;
z-index: 9999;
&.show {
display: block;
}
}
.new-dialog-box {
width: 100%;
min-height: pxToRem(195px);
background: rgba(255, 255, 255, 1);
position: absolute;
box-sizing: border-box;
margin: 0;
bottom: 0;
z-index: 9999;
transition: all 0.3s;
border-radius: pxToRem(8px) pxToRem(8px) 0 0;
// display: flex;
.dialog-title {
font-size: pxToRem(19px);
margin: pxToRem(25px) auto pxToRem(25px);
text-align: center;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: rgba(51, 51, 51, 1);
line-height: pxToRem(19px);
}
.dialog-content {
padding: 0 pxToRem(25px);
font-size: pxToRem(14px);
font-family: PingFangSC-Regular, PingFang SC;
color: rgba(51, 51, 51, 1);
margin: 0 auto pxToRem(15px);
text-align: center;
color: rgba(0, 0, 0, 1);
line-height: pxToRem(27px);
min-height: pxToRem(50px);
}
.dialog-bottom {
height: pxToRem(45px);
padding: 0 pxToRem(20px);
margin-bottom: pxToRem(15px);
display: flex;
&.isIPhoneX {
margin-bottom: pxToRem(49px);
}
}
.dialog-btn {
// margin: 0 auto pxToRem(30px);
// width: pxToRem(300px);
flex: 1;
height: pxToRem(45px);
background-image: linear-gradient(-44deg, #FCAF3C 0%, #F25228 98%);
box-shadow: 0 8px 14px 0 rgba(190,118,22,0.20);
border-radius: pxToRem(45px);
text-align: center;
line-height: pxToRem(45px);
font-size: pxToRem(15px);
font-family: PingFangSC-Medium, PingFang SC;
color: rgba(255, 255, 255, 1);
&.margin_r {
margin-right: pxToRem(24px);
}
&.default {
background: #FFFFFF;
border: 1px solid #E6E6E6;
box-shadow: 0 10px 20px -8px rgba(173,173,173,0.30);
border-radius: pxToRem(45px);
font-size: pxToRem(15px);
font-family: PingFang-SC-Medium, PingFang-SC;
color: rgba(85, 85, 85, 1);
}
}
}
另外附上代码中iPhone X系列机型判断方法
export const isIphoneX = function() {
// iPhone X、iPhone XS
var isIPhoneX =
/iphone/gi.test(window.navigator.userAgent) &&
window.devicePixelRatio &&
window.devicePixelRatio === 3 &&
window.screen.width === 375 &&
window.screen.height === 812;
// iPhone XS Max
var isIPhoneXSMax =
/iphone/gi.test(window.navigator.userAgent) &&
window.devicePixelRatio &&
window.devicePixelRatio === 3 &&
window.screen.width === 414 &&
window.screen.height === 896;
// iPhone XR
var isIPhoneXR =
/iphone/gi.test(window.navigator.userAgent) &&
window.devicePixelRatio &&
window.devicePixelRatio === 2 &&
window.screen.width === 414 &&
window.screen.height === 896;
if (isIPhoneX || isIPhoneXSMax || isIPhoneXR) {
return true;
}
return false;
}