Overlay 弹层
如果项目中使用的是 1.x 版本的基础组件(@alifd/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @icedesign/base@latest -S
用于弹层类的工具类集合.
开发指南
Overlay提供了一系列的组件的集合用于创建弹层组件。其中包含:
Gateway
Gateway的作用是将节点渲染到指定的容器中。正常情况下React使用当前节点所在的父节点作为容器,但是Gateway可以将其包含的节点渲染到另外的节点中。
Gateway只有一个配置项就是container
, 它可以接受字符串和函数。
// 将span渲染到id为container的节点中
<Gateway container="container">
<span>123</span>
</Gateway>
实际上也可以传入container为函数,返回节点的ref.
<div ref="container"></div>
<Gateway container={() => this.refs.container}>
<span>123</span>
</Gateway>
Overlay
Overlay利用了Gateway提供的能力,可以在页面中弹出一个浮层,封装了动画和定位及一些可用性的功能。
Overlay被设计为无状态的组件,其本身并不控制自己的显示和隐藏的状态。
注意: 类似的canCloseby*的配置也需要配合onRequestClose才能关闭Overlay
安全节点
弹层同时提供点击文档中的节点隐藏该弹层的功能,但是由于React内部维护了一个事件队列,且并未提供针对document的事件绑定方式,所以在点击任何一个节点都会触发document的click,即便手动阻止冒泡也不行, Overlay采用了一个安全节点的设置来避免这个问题.
定位
align由空格隔开的字符串表示,例如
tl bl
. 其中tl
代表目标元素的左上方,bl
代表基准元素的左下方,所以tl bl
的意思是目标元素的左上方对齐基准元素左下方。其中定位的可选值有tl
,tc
,tr
,cl
,cc
,cr
,bl
,bc
,br
.align支持的Boolean值仅为false,在设置为false的时候,不使用JS定位,这样你可以根据你的需要传入style或者className进行CSS定位。
说明 t
为top
的缩写,b
为bottom
的缩写,c
为center
的缩写,l
为left
的缩写,r
为right
的缩写.
下面的例子演示了如何将弹层定位到页面的右上角
<Overlay visible align="tr tr"><span>123</span></Overlay>
Popup
接收某个节点,弹出一个浮层, 这个浮层默认情况下使用这个节点作为定位的参照对象
API
弹层
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
prefix | 样式类名的品牌前缀 | String | 'next-' |
className | 自定义类名 | String | - |
style | 自定义样式对象 | Object | - |
children | 浮层内容 | any | - |
visible | 是否显示浮层, 如果此属性为false,浮层不会被渲染 | Boolean | false |
canCloseByEsc | 是否支持esc按键关闭浮层 | Boolean | true |
canCloseByOutSideClick | 点击浮层外的区域是否关闭浮层 | Boolean | true |
canCloseByMask | 点击遮罩区域是否关闭浮层 | Boolean | true |
animation | 配置动画的播放方式 | Object/Boolean | { in: 'expandInDown', out: 'expandOutUp' } |
target | 配置浮层定位的参照元素 | any | Position.VIEWPORT |
align | 浮层相对于target的定位, 详见开发指南的定位部分 | String/Boolean | 'tl bl' |
offset | 浮层相对于target定位的微调 | Array | 0, 0 |
beforeClose | 浮层关闭前触发的事件签名:Function() => void | Function | () => {} |
onClose | 浮层关闭后触发的事件签名:Function() => void | Function | () => {} |
afterClose | 浮层关闭后触发的事件, 如果有动画,则在动画结束后触发签名:Function() => void | Function | () => {} |
beforeOpen | 浮层打开前触发的事件签名:Function() => void | Function | () => {} |
onOpen | 浮层打开后触发的事件签名:Function() => void | Function | () => {} |
afterOpen | 浮层打开后触发的事件, 如果有动画,则在动画结束后触发签名:Function() => void | Function | () => {} |
onRequestClose | 浮层请求关闭触发的事件签名:Function(reason: String, e: Event) => void参数:reason: {String} 浮层关闭的来源e: {Event} DOM事件 | Function | () => {} |
beforePosition | 浮层定位完成前触发的事件签名:Function() => void | Function | - |
onPosition | 浮层定位完成后触发的事件签名:Function(config: Object, node: Object) => void参数:config: {Object} 定位的参数node: {Object} 定位的元素 | Function | () => {} |
autoFocus | 浮层打开的时候是否让里面的元素自动获取焦点 | Boolean | false |
hasMask | 是否显示遮罩 | Boolean | false |
cache | 隐藏时是否保留子节点 | Boolean | false |
safeNode | 安全节点,当点击document的时候, 如果包含该节点则不会关闭浮层, 如果是函数需要返回ref, 如果是字符串则是该DOM的id, 也可以直接传入DOM节点 | any | - |
wrapperClassName | 浮层的根节点的样式类 | String | - |
container | 指定渲染组件的容器 | any | - |
shouldUpdatePosition | 强制更新定位信息 | Boolean | - |
needAdjust | 是否自动调整定位的位置 | Boolean | - |
disableScroll | 是否禁用页面滚动 | Boolean | false |
Overlay.Gateway
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
container | 指定渲染children的容器 | any | () => document.body |
Overlay.Popup
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
align | 弹层相对于target的定位, 详见开发指南的定位部分 | String | 'tl bl' |
offset | 弹层相对于target定位的微调 | Array | 0, 0 |
trigger | 触发弹层显示或者隐藏的元素 | any | <div></div> |
triggerType | 触发弹层显示的类型 | String | 'hover' |
visible | 弹层当前显示的状态 | Boolean | - |
defaultVisible | 弹层默认显示的状态 | Boolean | - |
disabled | 设置此属性,弹层无法打开 | Boolean | false |
delay | 弹层在触发以后的延时显示 | Number | 200 |
canCloseByOutSideClick | 点击浮层外的区域是否关闭浮层 | Boolean | true |
onVisibleChange | 弹层在显示和隐藏触发的事件签名:Function(visible: Boolean, type: String, e: Event) => void参数:visible: {Boolean} 弹层是否隐藏和显示type: {String} 触发弹层显示和隐藏的来源e: {Event} DOM事件 | Function | () => {} |
autoFocus | 浮层打开的时候是否让里面的元素自动获取焦点 | Boolean | - |
animation | 配置动画的播放方式 | Object/Boolean | { in: 'expandInDown', out: 'expandOutUp' } |
target | 配置弹层定位的参照元素 | any | - |
Overlay.Position
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
className | 自定义类名 | String | - |
target | 定位参照的元素 | any | - |
contentNode | 定位的目标元素 | any | - |
align | 定位的方式, 详见开发指南的定位部分 | String/Boolean | 'tl bl' |
offset | 相对于target定位的微调 | Array | 0, 0 |
beforePosition | 定位完成前触发的事件签名:Function() => void | Function | () => {} |
onPosition | 定位完成后触发的事件签名:Function(config: Object, node: Object) => void参数:config: {Object} 定位的参数node: {Object} 定位的元素 | Function | () => {} |
needAdjust | 是否自动调整定位的位置 | Boolean | true |
needListenResize | 是否监听Resize事件 | Boolean | true |
shouldUpdatePosition | 强制更新定位信息 | Boolean | false |
isRtl | 对齐方式 | Boolean | false |
代码示例
带有遮罩的弹出层
查看源码在线预览
import { Overlay } from "@icedesign/base";
class App extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
visible: false
};
}
render() {
return (
<span>
<button onClick={this.onClick.bind(this)} ref="target">
Open
</button>
<Overlay
visible={this.state.visible}
hasMask
disableScroll
align="cc cc"
safeNode={() => this.refs.target}
onRequestClose={this.onClose.bind(this)}
>
<span className="overlay-demo">Hello World From Overlay!</span>
</Overlay>
</span>
);
}
onClick() {
this.setState({ visible: true });
}
onClose = reason => {
console.log("onRequestClose emit!, reason: ", reason);
this.setState({
visible: false
});
};
}
ReactDOM.render(<App />, mountNode);
.overlay-demo {
border: 1px solid #999;
padding: 10px;
width: 300px;
height: 100px;
background: #fff;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
演示了Popup受控的示例,可以在内部控制Popup的显示和隐藏
查看源码在线预览
import { Overlay } from "@icedesign/base";
const Popup = Overlay.Popup;
class App extends React.Component {
state = {
visible: false
};
render() {
return (
<Popup
trigger={<button>Open</button>}
visible={this.state.visible}
triggerType="click"
onVisibleChange={this.onVisibleChange}
>
<div className="overlay-demo">
<button onClick={this.onClose}>Close</button>
<p>Hello World From Popup!</p>
</div>
</Popup>
);
}
onClose = () => {
this.setState({
visible: false
});
};
onVisibleChange = visible => {
this.setState({
visible
});
};
}
ReactDOM.render(<App />, mountNode);
.overlay-demo {
border: 1px solid #999;
padding: 10px;
width: 300px;
height: 100px;
background: #fff;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
Gateway 的基本用法
查看源码在线预览
import { Overlay } from "@icedesign/base";
const { Gateway } = Overlay;
class App extends React.Component {
render() {
return (
<div>
<span ref="container" />
<Gateway container={() => this.refs.container}>
<span>Hello World from gateway.</span>
</Gateway>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
弹层嵌套的时候,使用container属性将容器渲染到第一个弹层的节点内部。
查看源码在线预览
import { Overlay } from "@icedesign/base";
const Popup = Overlay.Popup;
class App extends React.Component {
state = {
visible: false,
overlayVisible: false
};
render() {
return (
<Popup
trigger={<button>Open</button>}
triggerType="click"
visible={this.state.visible}
onVisibleChange={this.onVisibleChange}
>
<div className="overlay-demo">
<Popup
triggerType="click"
trigger={<button>Open overlay</button>}
container={trigger => trigger && trigger.parentNode}
visible={this.state.overlayVisible}
onVisibleChange={this.onOverlayVisibleChange}
>
<div className="overlay-demo" onClick={this.hideOverlay}>
Click me will close this overlay, but popup will not close.
</div>
</Popup>
<p>Hello World From Popup!</p>
</div>
</Popup>
);
}
hideOverlay = () => {
this.setState({
overlayVisible: false
});
};
showOverlay = () => {
this.setState({
overlayVisible: true
});
};
onVisibleChange = visible => {
this.setState({
visible
});
};
onOverlayVisibleChange = overlayVisible => {
this.setState({
overlayVisible
});
};
}
ReactDOM.render(<App />, mountNode);
.overlay-demo {
border: 1px solid #999;
padding: 10px;
width: 300px;
height: 100px;
background: #fff;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
弹出一个浮层
查看源码在线预览
import { Overlay } from "@icedesign/base";
class App extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
visible: false
};
}
render() {
return (
<span>
<button onClick={this.onClick.bind(this)} ref="target">
Toggle visible
</button>
<Overlay
visible={this.state.visible}
target={() => this.refs.target}
safeNode={() => this.refs.target}
onRequestClose={this.onClose.bind(this)}
>
<span className="overlay-demo">Hello World From Overlay!</span>
</Overlay>
</span>
);
}
onClick() {
this.setState({ visible: !this.state.visible });
}
onClose = () => {
this.setState({
visible: false
});
};
}
ReactDOM.render(<App />, mountNode);
.overlay-demo {
border: 1px solid #999;
padding: 10px;
width: 300px;
height: 100px;
background: #fff;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
使用Popup弹出一个浮层
查看源码在线预览
import { Overlay } from "@icedesign/base";
const Popup = Overlay.Popup;
const afterClose = () => {
console.log("close");
};
const trigger = <button>Open</button>;
ReactDOM.render(
<Popup trigger={trigger} triggerType="click" afterClose={afterClose}>
<div className="overlay-demo">Hello World From Popup!</div>
</Popup>,
mountNode
);
.overlay-demo {
border: 1px solid #999;
padding: 10px;
width: 300px;
height: 100px;
background: #fff;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
演示弹层如何跟随滚动
查看源码在线预览
import { Overlay } from "@icedesign/base";
const Popup = Overlay.Popup;
class App extends React.Component {
render() {
return (
<div className="scroll-container" ref="scroll">
<Popup
trigger={<button>Open</button>}
container={() => this.refs.scroll}
triggerType="click"
>
<div className="overlay-demo">
<p>Hello World From Popup!</p>
</div>
</Popup>
<div style={{ height: "300px" }} />
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
.overlay-demo {
border: 1px solid #999;
padding: 10px;
width: 300px;
height: 100px;
background: #fff;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
.scroll-container {
position: relative;
height: 150px;
border: 1px solid #999;
padding: 10px;
overflow: auto;
}