Overlay 弹层
如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @alifd/next@latest -S
用于生成弹层的工具类集合.
开发指南
Overlay 提供了一系列组件用于创建弹层。其中包含:
Overlay
Overlay 可以在页面中弹出一个浮层,封装了定位,动画及其他一些可用性的功能。Overlay 被设计为无状态的组件,其本身并不控制自己显示和隐藏的状态。
注意: 类似 canCloseby* 的配置也需要配合 onRequestClose 才能关闭弹层。
安全节点
Overlay 提供了点击弹层外文档中节点隐藏该弹层的功能,如果想让某个节点点击后不隐藏弹层(如:触发弹层打开的节点),请将该节点传入 safeNode 属性。
定位
align 的值可以是由空格隔开的字符串,如
tl bl
,其中tl
代表目标元素的左上方,bl
代表基准元素的左下方,所以tl bl
的意思是目标元素的左上方对齐基准元素左下方。其中定位的可选值有tl
,tc
,tr
,cl
,cc
,cr
,bl
,bc
,br
。t
为top
的缩写,b
为bottom
的缩写,c
为center
的缩写,l
为left
的缩写,r
为right
的缩写.align 支持的 Boolean 值仅为 false,在设置为 false 时,不使用 JS 定位,这样你可以根据你的需要传入 style 或者 className 进行 CSS 定位。
rtl情况下会自动翻转 r(right)与 l(left), 例如 rtl状态下
tl bl
与 非rtl状态下tr br
等效下面的例子演示了如何将弹层定位到页面的右上角:
<Overlay visible align="tr tr"><span>123</span></Overlay>
Popup
Popup 是对 Overlay 的封装,它接收某个节点作为触发节点,弹出一个浮层,这个浮层默认情况下使用这个节点作为定位的参照对象。
API
Overlay
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
children | 弹层内容 | any | - |
visible | 是否显示弹层 | Boolean | false |
onRequestClose | 弹层请求关闭时触发事件的回调函数签名:Function(type: String, e: Object) => void参数:type: {String} 弹层关闭的来源e: {Object} DOM 事件 | Function | func.noop |
target | 弹层定位的参照元素 | any | Position.VIEWPORT |
align | 弹层相对于参照元素的定位, 详见开发指南的定位部分 | String/Boolean | 'tl bl' |
offset | 弹层相对于trigger的定位的微调, 接收数组hoz, ver, 表示弹层在 left / top 上的增量e.g. 100, 100 表示往右(RTL 模式下是往左) 、下分布偏移100px | Array | 0, 0 |
container | 渲染组件的容器,如果是函数需要返回 ref,如果是字符串则是该 DOM 的 id,也可以直接传入 DOM 节点 | any | - |
hasMask | 是否显示遮罩 | Boolean | false |
canCloseByEsc | 是否支持 esc 按键关闭弹层 | Boolean | true |
canCloseByOutSideClick | 点击弹层外的区域是否关闭弹层,不显示遮罩时生效 | Boolean | true |
canCloseByMask | 点击遮罩区域是否关闭弹层,显示遮罩时生效 | Boolean | true |
beforeOpen | 弹层打开前触发事件的回调函数签名:Function() => void | Function | func.noop |
onOpen | 弹层打开时触发事件的回调函数签名:Function() => void | Function | func.noop |
afterOpen | 弹层打开后触发事件的回调函数, 如果有动画,则在动画结束后触发签名:Function() => void | Function | func.noop |
beforeClose | 弹层关闭前触发事件的回调函数签名:Function() => void | Function | func.noop |
onClose | 弹层关闭时触发事件的回调函数签名:Function() => void | Function | func.noop |
afterClose | 弹层关闭后触发事件的回调函数, 如果有动画,则在动画结束后触发签名:Function() => void | Function | func.noop |
beforePosition | 弹层定位完成前触发的事件签名:Function() => void | Function | func.noop |
onPosition | 弹层定位完成时触发的事件签名:Function(config: Object, node: Object) => void参数:config: {Object} 定位的参数config.align: {Array} 对齐方式,如 'cc', 'cc'(如果开启 needAdjust,可能和预先设置的 align 不同)config.top: {Number} 距离视口顶部距离config.left: {Number} 距离视口左侧距离node: {Object} 定位参照的容器节点 | Function | func.noop |
shouldUpdatePosition | 是否在每次弹层重新渲染后强制更新定位信息,一般用于弹层内容区域大小发生变化时,仍需保持原来的定位方式 | Boolean | false |
autoFocus | 弹层打开时是否让其中的元素自动获取焦点 | Boolean | false |
needAdjust | 当弹层由于页面滚动等情况不在可视区域时,是否自动调整定位以出现在可视区域 | Boolean | true |
disableScroll | 是否禁用页面滚动 | Boolean | false |
cache | 隐藏时是否保留子节点 | Boolean | false |
safeNode | 安全节点,当点击 document 的时候,如果包含该节点则不会关闭弹层,如果是函数需要返回 ref,如果是字符串则是该 DOM 的 id,也可以直接传入 DOM 节点,或者以上值组成的数组 | any | - |
wrapperClassName | 弹层的根节点的样式类 | String | - |
wrapperStyle | 弹层的根节点的内联样式 | Object | - |
animation | 配置动画的播放方式,支持 { in: 'enter-class', out: 'leave-class' } 的对象参数,如果设置为 false,则不播放动画。 请参考 Animate 组件的文档获取可用的动画名 | Object/Boolean | { in: 'expandInDown', out: 'expandOutUp' } |
Overlay.Popup
继承 Overlay 的 API,除非特别说明
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
children | 弹层内容 | ReactNode | - |
trigger | 触发弹层显示或隐藏的元素 | ReactElement | - |
triggerType | 触发弹层显示或隐藏的操作类型,可以是 'click','hover','focus',或者它们组成的数组,如 'hover', 'focus' | String/Array | 'hover' |
triggerClickKeycode | 当 triggerType 为 click 时才生效,可自定义触发弹层显示的键盘码 | Number/Array | KEYCODE.SPACE, KEYCODE.ENTER |
visible | 弹层当前是否显示 | Boolean | - |
defaultVisible | 弹层默认是否显示 | Boolean | false |
onVisibleChange | 弹层显示或隐藏时触发的回调函数签名:Function(visible: Boolean, type: String, e: Object) => void参数:visible: {Boolean} 弹层是否显示type: {String} 触发弹层显示或隐藏的来源 fromTrigger 表示由trigger的点击触发; docClick 表示由document的点击触发e: {Object} DOM事件 | Function | func.noop |
disabled | 设置此属性,弹层无法显示或隐藏 | Boolean | false |
delay | 弹层显示或隐藏的延时时间(以毫秒为单位),在 triggerType 被设置为 hover 时生效 | Number | 200 |
canCloseByTrigger | trigger 是否可以关闭弹层 | Boolean | true |
target | 弹层定位的参照元素 | any | target 属性,即触发元素 |
followTrigger | 是否跟随trigger滚动 | Boolean | false |
ARIA and KeyBoard
说明
: 此组件需要结合其他组件使用,会有提示。参考上文无障碍
。
代码示例
弹出一个弹层。
查看源码在线预览
import { Overlay } from '@alifd/next';
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
}
onClick = () => {
this.setState({
visible: !this.state.visible
});
}
onClose = () => {
this.setState({
visible: false
});
}
render() {
return (
<div>
<button onClick={this.onClick} ref={ref => {
this.btn = ref;
}}>
Toggle visible
</button>
<Overlay visible={this.state.visible}
target={() => this.btn}
safeNode={() => this.btn}
onRequestClose={this.onClose}>
<span className="overlay-demo">
Hello World From Overlay!
</span>
</Overlay>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.overlay-demo {
width: 300px;
height: 100px;
padding: 10px;
border: 1px solid #999999;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
带有遮罩的弹层。
查看源码在线预览
import { Overlay } from '@alifd/next';
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
}
onClick = () => {
this.setState({
visible: true
});
}
onClose = () => {
this.setState({
visible: false
});
}
render() {
return (
<div>
<button onClick={this.onClick} ref={ref => {
this.btn = ref;
}}>
Open
</button>
<Overlay visible={this.state.visible}
safeNode={() => this.btn}
align="cc cc"
hasMask
disableScroll
onRequestClose={this.onClose}>
<span className="overlay-demo">
Hello World From Overlay!
</span>
</Overlay>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.overlay-demo {
width: 300px;
height: 100px;
padding: 10px;
border: 1px solid #999999;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
使用 Popup 弹出一个弹层。
查看源码在线预览
import { Overlay } from '@alifd/next';
const { Popup } = Overlay;
ReactDOM.render(
<div>
<Popup trigger={<button>Open</button>} triggerType="click">
<span className="overlay-demo">
Hello World From Popup!
</span>
</Popup>
<br />
<Popup trigger={<button>Use Down Arrow to open</button>} triggerType="click" triggerClickKeycode={40}>
<span className="overlay-demo">
Hello World From Popup!
</span>
</Popup>
</div>
, mountNode);
.overlay-demo {
width: 300px;
height: 100px;
padding: 10px;
border: 1px solid #999999;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
展示了 Popup 受控显示隐藏的用法。
查看源码在线预览
import { Overlay } from '@alifd/next';
const { Popup } = Overlay;
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
}
onVisibleChange = visible => {
this.setState({
visible
});
}
onGroupVisibleChange = groupVisible => {
this.setState({
groupVisible
});
}
render() {
return (
<div>
<div>
<Popup trigger={<button>Open</button>}
triggerType="click"
visible={this.state.visible}
onVisibleChange={this.onVisibleChange}>
<span className="overlay-demo">
Hello World From Popup!
</span>
</Popup>
</div>
<br />
<div>
<Popup trigger={<button style={{"margin-right": "50px"}} ref={ref => {this.btn1 = ref;}}>Paired Popup 1</button>}
triggerType="click"
visible={this.state.groupVisible}
safeNode={[() => this.btn2, () => this.overlay2]}
onVisibleChange={this.onGroupVisibleChange}>
<span className="overlay-demo" ref={ref => {this.overlay1 = ref;}}>
Hello World From Popup!
</span>
</Popup>
<Popup trigger={<button ref={ref => {this.btn2 = ref;}}>Paired Popup 2</button>}
triggerType="click"
visible={this.state.groupVisible}
safeNode={[() => this.btn1, () => this.overlay1]}
onVisibleChange={this.onGroupVisibleChange}>
<span className="overlay-demo" ref={ref => {this.overlay2 = ref;}}>
Hello World From Popup!
</span>
</Popup>
</div>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.overlay-demo {
width: 300px;
height: 100px;
padding: 10px;
border: 1px solid #999999;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
有弹层嵌套需求时,请使用 container 属性将第二个弹层渲染到第一个弹层内部。
查看源码在线预览
import { Overlay } from '@alifd/next';
const { Popup } = Overlay;
ReactDOM.render(
<Popup trigger={<button>Open first overlay</button>}
triggerType="click">
<div className="overlay-demo">
<Popup trigger={<button>Open second overlay</button>}
triggerType="click"
container={trigger => trigger.parentNode}>
<div className="overlay-demo">
<p>Hello World From Second Overlay!</p>
</div>
</Popup>
<p>Hello World From First Overlay!</p>
</div>
</Popup>
, mountNode);
.overlay-demo {
width: 300px;
height: 100px;
padding: 10px;
border: 1px solid #999999;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
弹层默认参照 document.body 绝对定位,如果弹层显示隐藏的触发元素所在容器(一般为父节点)有滚动条,那么当容器滚动时,会发生触发元素与弹层相分离的情况,解决的办法是将弹层渲染到触发元素所在的容器中。(触发元素所在的容器,必须设置 position 样式,以完成弹层的绝对定位。)
查看源码在线预览
import { Overlay } from '@alifd/next';
const { Popup } = Overlay;
ReactDOM.render(
<div className="scroll-container">
<Popup trigger={<button>Open</button>}
triggerType="click"
container={trigger => trigger.parentNode}>
<div className="overlay-demo">
Hello World From Popup!
</div>
</Popup>
<div style={{ height: '300px' }} />
</div>
, mountNode);
.overlay-demo {
width: 300px;
height: 100px;
padding: 10px;
border: 1px solid #999999;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
.scroll-container {
position: relative;
height: 150px;
padding: 10px;
border: 1px solid #999999;
overflow: auto;
}
可以自定义对齐方式
查看源码在线预览
import { Overlay } from '@alifd/next';
const { Popup } = Overlay;
ReactDOM.render(
<div >
<div id="container1" className="overlay-container">
normal example
<Overlay target="container1" visible align="tr tr"><span>123</span></Overlay>
<Overlay target="container1" visible align="cc cr"><span>123</span></Overlay>
<Overlay target="container1" visible align="bc bc"><span>123</span></Overlay>
</div>
<br /><br /><br /><br />
<div id="container2" className="overlay-container" dir="rtl">
dir=rtl example
<Overlay target="container2" rtl visible align="tr tr"><span>123</span></Overlay>
<Overlay target="container2" rtl visible align="cc cr"><span>123</span></Overlay>
<Overlay target="container2" rtl visible align="bc bc"><span>123</span></Overlay>
</div>
</div>
, mountNode);
.overlay-container {
position: relative;
height: 150px;
margin: 50px;
border: 1px solid #999;
overflow: auto;
text-align: center;
color: red;
}