用于生成弹层的工具类集合。
Overlay 提供了一系列组件用于创建弹层。其中包含:
v2
版本更新指示 #1.25 版本增加 v2 支持开启新版本弹,功能如下:
功能变化:
API变化:
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),
Popup 是对 Overlay 的封装,它接收某个节点作为触发节点,弹出一个浮层,这个浮层默认情况下使用这个节点作为定位的参照对象。
弹出一个弹层。
import { Overlay, Button } from '@alifd/next';
class Demo extends React.Component {constructor(props) {super(props);this.onClick = () => {this.setState({visible: !this.state.visible});};this.onClose = () => {this.setState({visible: false});};this.state = {visible: false};}render() {return (<div><ButtononClick={this.onClick}ref={ref => {this.btn = ref;}}>Toggle visible</Button><Overlayv2visible={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 #efefef;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
带有遮罩的弹层。
import { Overlay, Button } from '@alifd/next';
class Demo extends React.Component {constructor(props) {super(props);this.onClick = () => {this.setState({visible: true});};this.onClose = () => {this.setState({visible: false});};this.state = {visible: false};}render() {return (<div><ButtononClick={this.onClick}ref={ref => {this.btn = ref;}}>Open</Button><Overlayv2visible={this.state.visible}safeNode={() => this.btn}align="cc cc"hasMaskdisableScrollonRequestClose={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 #efefef;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
Popup
是对 Overlay
的封装,它接收某个节点作为触发节点,弹出一个浮层,这个浮层默认情况下使用这个节点作为定位的参照对象。
import { Overlay, Button, Input } from '@alifd/next';
const { Popup } = Overlay;ReactDOM.render(<div><Popup v2 trigger={<Button>Open</Button>} triggerType="click"><span className="overlay-demo">Hello World From Popup!</span></Popup><br /><br /><Popupv2trigger={<Input placeholder="Use Down Arrow to open" />}triggerType="click"triggerClickKeycode={[32, 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);
}
通过 animation
属性设置动效
import { useState } from 'react';
import { Overlay, Button } from '@alifd/next';
const { Popup } = Overlay;const style = { marginLeft: 10 };const top = (<Button style={{ margin: "5px" }} id="top" className="btrigger">top</Button>);const right = (<Button style={{ margin: "5px" }} id="right" className="btrigger">right</Button>);const bottom = (<Button style={{ margin: "5px" }} id="bottom" className="btrigger">bottom</Button>);const left = (<Button style={{ margin: "5px" }} id="left" className="btrigger">left</Button>);const topLeft = (<Button style={{ margin: "5px" }} id="topLeft" className="btrigger">top left</Button>);const topRight = (<Button style={{ margin: "5px" }} id="topRight" className="btrigger">top right</Button>);const rightTop = (<Button style={{ margin: "5px" }} id="rightTop" className="btrigger">right top</Button>);const rightBottom = (<Button style={{ margin: "5px" }} id="rightBottom" className="btrigger">right bottom</Button>);const bottomLeft = (<Button style={{ margin: "5px" }} id="bottomLeft" className="btrigger">bottom left</Button>);const bottomRight = (<Button style={{ margin: "5px" }} id="bottomRight" className="btrigger">bottom right</Button>);const leftTop = (<Button style={{ margin: "5px" }} id="leftTop" className="btrigger">left top</Button>);const leftBottom = (<Button style={{ margin: "5px" }} id="leftBottom" className="btrigger">left bottom</Button>);const popupProps = {animation: {in: "zoomIn",out: "zoomOut"},triggerType: "hover",placementOffset: 4};const Demo = () => (<div style={{ paddingLeft: 220 }}><div style={{ marginLeft: 75 }}><Popup cache {...popupProps} v2 trigger={topLeft} placement="tl"><span className="tooltips-demo">popup text</span></Popup><Popup {...popupProps} v2 trigger={top} placement="t"><span className="tooltips-demo">popup text</span></Popup><Popup {...popupProps} v2 trigger={topRight} placement="tr"><span className="tooltips-demo">popup text</span></Popup></div><div style={{ width: 80, float: "left" }}><Popup {...popupProps} v2 trigger={leftTop} placement="lt"><span className="tooltips-demo">popup text</span></Popup><Popup {...popupProps} v2 trigger={left} placement="l"><span className="tooltips-demo">popup text</span></Popup><Popup {...popupProps} v2 trigger={leftBottom} placement="lb"><span className="tooltips-demo">popup text</span></Popup></div><div style={{ width: 80, marginLeft: 290 }}><Popup {...popupProps} v2 trigger={rightTop} placement="rt"><span className="tooltips-demo">popup text</span></Popup><Popup {...popupProps} v2 trigger={right} placement="r"><span className="tooltips-demo">popup text</span></Popup><Popup {...popupProps} v2 trigger={rightBottom} placement="rb"><span className="tooltips-demo">popup text</span></Popup></div><div style={{ marginLeft: 80, clear: "both" }}><Popup {...popupProps} v2 trigger={bottomLeft} placement="bl"><span className="tooltips-demo">popup text</span></Popup><Popup {...popupProps} v2 trigger={bottom} placement="b"><span className="tooltips-demo">popup text</span></Popup><Popup {...popupProps} v2 trigger={bottomRight} placement="br"><span className="tooltips-demo">popup text</span></Popup></div></div>);ReactDOM.render(<Demo />, mountNode);
.tooltips-demo {
padding: 8px;
max-width: 300px;
font-size: 14px;
font-weight: 400;
word-wrap: break-word;
color: #fff;
border-radius: 4px;
border: 1px solid transparent;
background-color: #1f2633;
box-shadow: 0 4px 16px 0 rgb(0 0 0 / 12%);
}
通过 triggerType
属性设置触发方式。
import { Overlay } from '@alifd/next';
const { Popup } = Overlay;const style = { marginLeft: 10 };ReactDOM.render(<div><Popupv2trigger={<button style={style}>click</button>}triggerType="click"><span className="overlay-demo">Click to open Popup!</span></Popup><Popupv2trigger={<button style={style}>hover</button>}triggerType="hover"><span className="overlay-demo">Hover to open Popup!</span></Popup><Popupv2trigger={<button style={style}>focus</button>}triggerType="focus"autoFocus={false}><span className="overlay-demo">Focus to open Popup!</span></Popup></div>,mountNode);
.overlay-demo {
width: 300px;
height: 100px;
padding: 10px;
border: 1px solid #eee;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
展示了 Popup
受控显示隐藏的用法。
import { Overlay, Button} from '@alifd/next';
const { Popup } = Overlay;class Demo extends React.Component {constructor(props) {super(props);this.onVisibleChange = visible => {this.setState({visible});};this.onGroupVisibleChange = groupVisible => {this.setState({groupVisible});};this.state = {visible: false};}render() {return (<div><div><Popupv2trigger={<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><Popupv2trigger={<Buttonstyle={{ marginRight: "50px" }}ref={ref => {this.btn1 = ref;}}>Paired Popup 1</Button>}triggerType="click"visible={this.state.groupVisible}safeNode={[() => this.btn2, () => this.overlay2]}onVisibleChange={this.onGroupVisibleChange}><spanclassName="overlay-demo"ref={ref => {this.overlay1 = ref;}}>Hello World From Popup!</span></Popup><Popupv2trigger={<Buttonref={ref => {this.btn2 = ref;}}>Paired Popup 2</Button>}triggerType="click"visible={this.state.groupVisible}safeNode={[() => this.btn1, () => this.overlay1]}onVisibleChange={this.onGroupVisibleChange}><spanclassName="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 #eee;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
有弹层嵌套需求时,请使用 container 属性将第二个弹层渲染到第一个弹层内部。
import { useState } from 'react';
import { Overlay, Button, Select, Switch, Balloon, DatePicker, DatePicker2, TimePicker2 } from '@alifd/next';
const { Popup } = Overlay;const { Tooltip } = Balloon;const Demo = () => {return (<div><Popup v2 trigger={<button>Popup Nest</button>} triggerType="click"><div className="overlay-demo"><Popupv2trigger={<button>Open second Popup</button>}triggerType="click"><div className="overlay-demo"><Popupv2trigger={<button>Open third Popup</button>}triggerType="click"><div className="overlay-demo"><p>Hello World From Third Overlay!</p></div></Popup><p>Hello World From Second Overlay!</p></div></Popup><p>Hello World From First Overlay!</p></div></Popup><Popupv2trigger={<button style={{ marginLeft: 10 }}>Select Nest</button>}triggerType="click"><div className="overlay-demo"><SelectdataSource={["apple", "banana", "orange", "grape"]}itemRender={d => (<Tooltipv2trigger={<div style={{ width: "100%" }}>{d.value}</div>}align="r"onMouseDown={e => e.stopPropagation()}onClick={e => e.stopPropagation()}><span>{d.value}</span></Tooltip>)}popupProps={{ v2: true }}/><DatePicker2popupProps={{ v2: true }}style={{ width: 120, marginLeft: 10 }}/><br /><br /><DatePicker2.RangePickerpopupProps={{ v2: true }}style={{ width: 240 }}/></div></Popup></div>);};ReactDOM.render(<Demo />, mountNode);
.overlay-demo {
width: 300px;
height: 100px;
padding: 10px;
border: 1px solid #eee;
background: #FFFFFF;
box-shadow: 2px 2px 20px rgba(0,0,0,0.15);
}
通过 align
可以自定义对齐方式。
import { Overlay, Button } from '@alifd/next';
const { Popup } = Overlay;ReactDOM.render(<div id="containerId" className="overlay-container"><Overlay v2 target="containerId" visible align="br tl"><Button>br tl</Button></Overlay><Overlay v2 target="containerId" visible align="tc tc"><Button>tc tc</Button></Overlay><Overlay v2 target="containerId" visible align="bl tr"><Button>bl tr</Button></Overlay><Overlay v2 target="containerId" visible align="cr cr"><Button>cr cr</Button></Overlay><Overlay v2 target="containerId" visible align="br br"><Button>br br</Button></Overlay><Overlay v2 target="containerId" visible align="tc bc"><Button>tc bc</Button></Overlay><Overlay v2 target="containerId" visible align="bl bl"><Button>bl bl</Button></Overlay><Overlay v2 target="containerId" visible align="cl cl"><Button>cl cl</Button></Overlay><Overlay v2 target="containerId" visible align="cc cc"><Button>cc cc</Button></Overlay></div>,mountNode);
.overlay-container {
position: relative;
height: 150px;
margin: 50px;
border: 1px solid #eee;
overflow: auto;
text-align: center;
}
如果弹层显示隐藏的触发元素所在容器有滚动条,弹窗会自动更新自己的位置跟随滚动。但是实时更新会消耗较大的计算,可以通过更好挂载容器到父节点获得更好性能
import { useState } from 'react';
import { Overlay, Button } from '@alifd/next';
const { Popup } = Overlay;const style = {width: 400,height: 100,padding: 10,background: "#fff",borderRadius: 2,boxShadow: "2px 2px 20px rgba(0, 0, 0, 0.15)"};const App = () => {const [position, setPosition] = useState({});const [position12, setPosition12] = useState({});const [position13, setPosition13] = useState({});const [position2, setPosition2] = useState({});return (<divstyle={{position: "relative",height: 150,padding: 50,border: "1px solid #eee",overflow: "auto"}}><Popupv2cachetriggerType="click"overlay={<div style={style}>position: {JSON.stringify(position)}</div>}onPosition={result => {const { style } = result;console.log(result);setPosition(style);}}><button style={{ marginTop: 10 }}>Open1</button></Popup><Popupv2cachetriggerType="click"overlay={<div style={style}>position: {JSON.stringify(position12)}</div>}container={trigger => trigger.parentNode}onPosition={result => {const { style } = result;console.log(result);setPosition12(style);}}><button style={{ marginTop: 10, marginLeft: 20 }}>mount parent node</button></Popup><Popupv2cachetriggerType="click"overlay={<div style={style}>position: {JSON.stringify(position13)}</div>}followTriggeronPosition={result => {const { style } = result;console.log(result);setPosition13(style);}}><button style={{ marginTop: 10, marginLeft: 20 }}>followTrigger</button></Popup><br /><Popupv2triggerType="click"overlay={<div style={style}>position: {JSON.stringify(position2)}</div>}onPosition={({ style }) => {setPosition2(style);}}><button style={{ marginTop: 200 }}>Open2</button></Popup><div style={{ height: 300, width: 1200 }} /></div>);};ReactDOM.render(<App />, mountNode);
弹窗跟随测试demo
Id | Data1 | Data2 | Data3 | State | Action |
---|
30000 | normal | 请选择 | |||
30001 | normal | 请选择 | |||
30002 | error | 请选择 | |||
30003 | normal | 请选择 | |||
30004 | normal | 请选择 | |||
30005 | error | 请选择 |
Id |
---|
30000 |
30001 |
30002 |
30003 |
30004 |
30005 |
Action |
---|
请选择 |
请选择 |
请选择 |
请选择 |
请选择 |
请选择 |
import { useState } from 'react';
import { Overlay, Button, Table, Select } from '@alifd/next';
const { Popup } = Overlay;const style = {width: 400,height: 100,padding: 10,background: "#fff",borderRadius: 2,boxShadow: "2px 2px 20px rgba(0, 0, 0, 0.15)"};function TableDemo() {const columns = [1, 2, 3].map(v => {return { dataIndex: `data${v}`, title: `Data${v}`, width: 200 };});columns.unshift({dataIndex: "id",title: "Id",width: 100,lock: "left"});columns.push({dataIndex: "state",title: "State",width: 200});columns.push({title: "Action",width: 100,cell: () => (<Select// style={{ width: 100 }}popupProps={{ v2: true }}dataSource={[{ label: "1", value: 1 },{ label: "2", value: 2 },{ label: "3", value: 3 },{ label: "4", value: 4 },{ label: "5", value: 5 },{ label: "6", value: 6 },{ label: "7", value: 7 },{ label: "8", value: 8 },{ label: "9", value: 9 }]}/>),lock: "right"});const dataSource = [{id: 30000,data: "$13.02",state: "normal"},{id: 30001,data: "$16.02",state: "normal"},{id: 30002,data: "$63.0002",state: "error"},{id: 30003,data: "$13.02",state: "normal"},{id: 30004,data: "$16.02",state: "normal"},{id: 30005,data: "$63.0002",state: "error"}];return (<div className="App"><div style={{ height: 200, overflowY: "scroll", position: "relative" }}><Table dataSource={dataSource} stickyHeader>{columns.map((col, i) => {return <Table.Column key={i} {...col} />;})}</Table></div></div>);}ReactDOM.render(<TableDemo />, mountNode);
参数 | 说明 | 类型 | 默认值 | 版本支持 |
---|---|---|---|---|
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 | '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 | |
autoFocus | 弹层打开时是否让其中的元素自动获取焦点 | Boolean | false | |
disableScroll | 是否禁用页面滚动 | Boolean | false | |
useCapture | 是否在捕获阶段监听,适配 react 17 事件模型变更 | Boolean | true | 1.25 |
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' } | |
v2 | 开启 v2 版本 | Boolean | - | 1.25 |
points | [v2] align 的数组形式,不能和 align 同时使用 | Array | - | 1.25 |
继承 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 | |||||||||||
v2 | 开启 v2 版本 | Boolean | - | |||||||||||
placement | [v2] 快捷位置,包含 'tl' | 't' | 'tr' | 'rt' | 'r' | 'rb' | 'bl' | 'b' | 'br' | 'lt' | 'l' | 'lb' | String | - |
说明
: 此组件需要结合其他组件使用,会有提示。