Balloon 气泡提示
如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @alifd/next@latest -S
开发指南
气泡组件
何时使用
当用户与被说明对象(文字,图片,输入框等)发生交互行为的action开始时, 即刻跟随动作出现一种辅助或帮助的提示信息。
其中Balloon.Tooltip是简化版本,主要用于hover时显示简单文案。
使用注意
对于trigger是自定义的React Component的情况,自定义的React Component 需要透传onMouseEnter/onMouseLeave/onClick 事件。
若要使用无障碍的气泡提示,请传入id。推荐简单提示使用
<Tooltip>
、复杂交互使用<Balloon triggerType="click">
。 triggerType="focus"作为辅助状态用于组件内部,请用户不要直接使用此值。
API
Balloon
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
children | 浮层的内容 | any | - |
type | 样式类型可选值:'normal', 'primary' | Enum | 'normal' |
visible | 弹层当前显示的状态 | Boolean | - |
defaultVisible | 弹层默认显示的状态 | Boolean | false |
onVisibleChange | 弹层在显示和隐藏触发的事件签名:Function(visible: Boolean, type: String) => void参数:visible: {Boolean} 弹层是否隐藏和显示type: {String} 触发弹层显示或隐藏的来源, closeClick 表示由自带的关闭按钮触发; fromTrigger 表示由trigger的点击触发; docClick 表示由document的点击触发 | Function | func.noop |
alignEdge | 弹出层对齐方式 | Boolean | false |
closable | 是否显示关闭按钮 | Boolean | true |
align | 弹出层位置可选值:'t'(上)'r'(右)'b'(下)'l'(左)'tl'(上左)'tr'(上右)'bl'(下左)'br'(下右)'lt'(左上)'lb'(左下)'rt'(右上)'rb'(右下 及其 两两组合) | Enum | 'b' |
offset | 弹层相对于trigger的定位的微调, 接收数组hoz, ver, 表示弹层在 left / top 上的增量e.g. 100, 100 表示往右(RTL 模式下是往左) 、下分布偏移100px | Array | 0, 0 |
trigger | 触发元素 | any | <span /> |
triggerType | 触发行为鼠标悬浮, 鼠标点击('hover','click')或者它们组成的数组,如 'hover', 'click', 强烈不建议使用'focus',若弹窗内容有复杂交互请使用click | String/Array | 'hover' |
onClose | 任何visible为false时会触发的事件签名:Function() => void | Function | func.noop |
needAdjust | 是否进行自动位置调整 | Boolean | false |
delay | 弹层在触发以后的延时显示, 单位毫秒 ms | Number | - |
afterClose | 浮层关闭后触发的事件, 如果有动画,则在动画结束后触发签名:Function() => void | Function | func.noop |
shouldUpdatePosition | 强制更新定位信息 | Boolean | - |
autoFocus | 弹层出现后是否自动focus到内部第一个元素 | Boolean | true |
safeNode | 安全节点:对于triggetType为click的浮层,会在点击除了浮层外的其它区域时关闭浮层.safeNode用于添加不触发关闭的节点, 值可以是dom节点的id或者是节点的dom对象 | String | undefined |
safeId | 用来指定safeNode节点的id,和safeNode配合使用 | String | null |
animation | 配置动画的播放方式 | Object/Boolean | { in: 'zoomIn', out: 'zoomOut', } |
cache | 弹层的dom节点关闭时是否删除 | Boolean | false |
popupContainer | 指定浮层渲染的父节点, 可以为节点id的字符串,也可以返回节点的函数。 | String/Function | - |
popupStyle | 弹层组件style,透传给Popup | Object | {} |
popupClassName | 弹层组件className,透传给Popup | String | '' |
popupProps | 弹层组件属性,透传给Popup | Object | {} |
followTrigger | 是否跟随滚动 | Boolean | - |
id | 弹层id, 传入值才会支持无障碍 | String | - |
Balloon.Tooltip
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
children | tooltip的内容 | any | - |
align | 弹出层位置可选值:'t'(上)'r'(右)'b'(下)'l'(左)'tl'(上左)'tr'(上右)'bl'(下左)'br'(下右)'lt'(左上)'lb'(左下)'rt'(右上)'rb'(右下 及其 两两组合) | Enum | 'b' |
trigger | 触发元素 | any | <span /> |
triggerType | 触发行为鼠标悬浮, 鼠标点击('hover', 'click')或者它们组成的数组,如 'hover', 'click', 强烈不建议使用'focus',若有复杂交互,推荐使用triggerType为click的Balloon组件 | String/Array | 'hover' |
popupStyle | 弹层组件style,透传给Popup | Object | - |
popupClassName | 弹层组件className,透传给Popup | String | - |
popupProps | 弹层组件属性,透传给Popup | Object | - |
pure | 是否pure render | Boolean | - |
popupContainer | 指定浮层渲染的父节点, 可以为节点id的字符串,也可以返回节点的函数。 | String/Function | - |
followTrigger | 是否跟随滚动 | Boolean | - |
id | 弹层id, 传入值才会支持无障碍 | String | - |
已知问题
- 对于 disabled 的元素,onMouseLeave 事件在chrome下无法触发,此为chrome的bug,暂时无法绕过。
ARIA and KeyBoard
按键 | 说明 |
---|---|
SPACE | 当triggerType=‘click’ 时,点击会弹出提示 |
Enter | 当triggerType=‘click’ 时,点击会弹出提示 |
代码示例
最简单的用法。
查看源码在线预览
import { Button, Balloon } from '@alifd/next';
const defaultTrigger = <Button className="btrigger" style={{margin: '5px'}}>default style</Button>;
const disabledTrigger = <Button disabled className="btrigger" style={{margin: '5px'}}>default style</Button>;
const primary = <Button className="btrigger" style={{margin: '5px'}}>primary style</Button>;
const Demo = () => (
<div className="container">
<Balloon trigger={defaultTrigger} closable={false}>
default
</Balloon>
<Balloon type="primary" trigger={primary} triggerType="click">
primary
</Balloon>
<Balloon trigger={disabledTrigger} closable={false}>
disabeled default
</Balloon>
</div>
);
ReactDOM.render(<Demo />, mountNode);
鼠标移入、聚集、点击。
查看源码在线预览
import { Button, Balloon, Input } from '@alifd/next';
const content = (<div><p>content</p></div>);
const MoveTarget = <Button style={{margin: '5px'}}>hover</Button>;
const ClickTarget = <Button style={{margin: '5px'}}>click</Button>;
const FocusTarget = <Button style={{margin: '5px'}}>focus</Button>;
const HoverInputTarget = <Input placeholder="hover" style={{marginRight: '20px'}}/>;
const ClickInputTarget = <Input placeholder="click" style={{marginRight: '20px'}}/>;
const FocusInputTarget = <Input placeholder="focus" />;
const App = () => (
<div>
<Balloon trigger={MoveTarget} triggerType="hover">
{content}
</Balloon>
<Balloon trigger={ClickTarget} triggerType="click">
{content}
</Balloon>
<Balloon trigger={FocusTarget} triggerType="focus">
{content}
</Balloon>
<br/>
<br/>
<Balloon trigger={HoverInputTarget} triggerType="hover">
{content}
</Balloon>
<Balloon trigger={ClickInputTarget} triggerType="click">
{content}
</Balloon>
<Balloon trigger={FocusInputTarget} triggerType="focus">
{content}
</Balloon>
</div>
);
ReactDOM.render(<App />, mountNode);
位置有十二个方向。
查看源码在线预览
import { Button, Balloon } from '@alifd/next';
const top = <Button id="top" style={{margin: '5px'}} className="btrigger">top</Button>;
const right = <Button id="right" style={{margin: '5px'}} className="btrigger">right</Button>;
const bottom = <Button id="bottom" style={{margin: '5px'}} className="btrigger">bottom</Button>;
const left = <Button id="left" style={{margin: '5px'}} className="btrigger">left</Button>;
const topLeft = <Button id="topLeft" style={{margin: '5px'}} className="btrigger">top left</Button>;
const topRight = <Button id="topRight" style={{margin: '5px'}} className="btrigger">top right</Button>;
const rightTop = <Button id="rightTop" style={{margin: '5px'}} className="btrigger">right top</Button>;
const rightBottom = <Button id="rightBottom" style={{margin: '5px'}} className="btrigger">right bottom</Button>;
const bottomLeft = <Button id="bottomLeft" style={{margin: '5px'}} className="btrigger">bottom left</Button>;
const bottomRight = <Button id="bottomRight" style={{margin: '5px'}} className="btrigger">bottom right</Button>;
const leftTop = <Button id="leftTop" style={{margin: '5px'}} className="btrigger">left top</Button>;
const leftBottom = <Button id="leftBottom" style={{margin: '5px'}} className="btrigger">left bottom</Button>;
const Content = () => (
<div>
<h4 style={{marginTop: 0}}>balloon title</h4>
<hr/>
<p>
balloon content
</p>
</div>
);
const App = () => (
<div style={{paddingLeft: 320, paddingTop: 100}}>
<div style={{marginLeft: 75}}>
<Balloon trigger={topLeft} align="tl" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
<Balloon trigger={top} align="t" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
<Balloon trigger={topRight} align="tr" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
</div>
<div style={{width: 80, float: 'left'}}>
<Balloon trigger={leftTop} align="lt" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
<Balloon trigger={left} align="l" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
<Balloon trigger={leftBottom} align="lb" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
</div>
<div style={{width: 80, marginLeft: 290}}>
<Balloon trigger={rightTop} align="rt" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
<Balloon trigger={right} align="r" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
<Balloon trigger={rightBottom} align="rb" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
</div>
<div style={{marginLeft: 80, clear: 'both'}}>
<Balloon trigger={bottomLeft} align="bl" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
<Balloon trigger={bottom} align="b" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
<Balloon trigger={bottomRight} align="br" alignEdge triggerType="click" style={{width: 300}}>
<Content/>
</Balloon>
</div>
</div>
);
ReactDOM.render(<App />, mountNode);
使用 visible
,属性控制浮层显示, 使 balloon 变为受限组件。
查看源码在线预览
import { Button, Balloon } from '@alifd/next';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
}
hide() {
this.setState({
visible: false
});
}
// onVisibleChange callback will be triggered when visible changes.
// For example, for click type, it'll be triggered when clicking the button and later the other areas;
// for hover type, it'll be triggered when mouse enter and mouse leave
handleVisibleChange(visible) {
this.setState({visible});
}
onClose() {
console.log('onClose doing!');
}
afterClose() {
console.log('afterClose doing!');
}
render() {
const visibleTrigger = <Button type="primary" style={{margin: '5px'}}>click to popup the card</Button>;
const clickTrigger = <Button type="primary" style={{margin: '5px'}}>hover to popup the card</Button>;
const content = (<div>
click the button<br/>
<a style={{right: 0}} id="confirmBtn" onClick={this.hide.bind(this)}>confirm</a>
<a style={{marginLeft: '4px'}} id="cancelBtn" onClick={this.hide.bind(this)}>cancel</a>
</div>);
return (
<div>
<Balloon trigger={visibleTrigger}
triggerType="click"
visible={this.state.visible}
onVisibleChange={this.handleVisibleChange.bind(this)}
>
{content}
</Balloon>
<Balloon trigger={clickTrigger}
triggerType="hover"
onClose={this.onClose.bind(this)}
afterClose={this.afterClose.bind(this)}>
{content}
</Balloon>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
使用 visible
,属性控制浮层显示, 使balloon变为受限组件。
查看源码在线预览
import { Button, Balloon } from '@alifd/next';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
}
// triggered every time visible becomes false
onClose() {
console.log('onClose doing!');
}
onClick() {
this.setState({visible: true});
}
render() {
const visibleTrigger = <Button onClick={this.onClick.bind(this)} type="primary">click to pupup the card</Button>;
const content = (<div>content</div>);
return (
<div style={{marginBottom: '100px'}}>
<Balloon trigger={visibleTrigger}
triggerType="click"
visible={this.state.visible}
onClose={this.onClose.bind(this)} >
{content}
</Balloon>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
浮层中如果又有浮层,比如在Balloon
中有DatePicker/Select
的浮层, DatePicker
选择时,Balloon
浮层也会关闭。可以用 followTrigger
解决。
查看源码在线预览
import { Button, Balloon, DatePicker, Select } from '@alifd/next';
import moment from 'moment';
const showSelect = <Button className="btrigger">Show Select</Button>;
const showDatePicker = <Button className="btrigger">Show DatePicker</Button>;
const innerButton = <Button className="btrigger">Show Inner Balloon</Button>;
const dateValue = moment('2018-01-01', 'YYYY-MM-DD', true);
const App = () => (
<div className="container nested">
<Balloon type="primary" autoFocus trigger={showSelect} closable={false} triggerType="click">
<Select dataSource={['apple', 'banana', 'orange']} followTrigger />
</Balloon>
<Balloon type="primary" autoFocus trigger={showDatePicker} closable={false} triggerType="click">
<DatePicker defaultValue={dateValue} followTrigger />
</Balloon>
<Balloon type="primary" autoFocus trigger={innerButton} closable={false} triggerType="click">
<Balloon trigger={<Button type="primary">please click</Button>} followTrigger triggerType="click">
nesting balloon content
</Balloon>
</Balloon>
</div>
);
ReactDOM.render(<App />, mountNode);
.container.nested {
margin-bottom: 50px;
}
简化的Balloon, 只能设置align, trigger和children, 触发条件是hover.
查看源码在线预览
import { Button, Balloon } from '@alifd/next';
const Tooltip = Balloon.Tooltip;
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 App = () => (
<div style={{paddingLeft: 220, paddingTop: 100}}>
<div style={{marginLeft: 75}}>
<Tooltip trigger={topLeft} align="tl">text text</Tooltip>
<Tooltip trigger={top} align="t">text text</Tooltip>
<Tooltip trigger={topRight} align="tr">text text</Tooltip>
</div>
<div style={{width: 80, float: 'left'}}>
<Tooltip trigger={leftTop} align="lt">text text</Tooltip>
<Tooltip trigger={left} align="l">text text</Tooltip>
<Tooltip trigger={leftBottom} align="lb">text text</Tooltip>
</div>
<div style={{width: 80, marginLeft: 290}}>
<Tooltip trigger={rightTop} align="rt">text text</Tooltip>
<Tooltip trigger={right} align="r">text text</Tooltip>
<Tooltip trigger={rightBottom} align="rb">text text</Tooltip>
</div>
<div style={{marginLeft: 80, clear: 'both'}}>
<Tooltip trigger={bottomLeft} align="bl">text text</Tooltip>
<Tooltip trigger={bottom} align="b">text text</Tooltip>
<Tooltip trigger={bottomRight} align="br">text text</Tooltip>
</div>
</div>
);
ReactDOM.render(<App />, mountNode);
.code-box-demo .sui-btn {
margin-right: 1em;
margin-bottom: 1em;
}
弹层id, 传入值才会支持无障碍。
查看源码在线预览
import { Button, Balloon, Input } from '@alifd/next';
import moment from 'moment';
const { Tooltip } = Balloon;
const innerButton = <Button className="btrigger">Fill in form</Button>;
const triggerTooltip = <Button style={{margin: '5px'}}>show tooltip</Button>;
const App = () => (
<div className="container nested">
<Balloon id="inner-a11y-balloon-1" autoFocus trigger={<Button type="primary">Fill in sub-form</Button>} popupContainer={(trigger) => trigger.parentNode} triggerType="click">
please input your age:
<Input placeholder="Age" size="small" label="Age :" id="balloon-input-1" /><br /><br />
</Balloon>
<Balloon id="a11y-balloon" type="primary" autoFocus trigger={innerButton} triggerType="click">
<Balloon id="inner-a11y-balloon" autoFocus trigger={<Button type="primary">Fill in sub-form</Button>} popupContainer={(trigger) => trigger.parentNode} triggerType="click">
please input your age:
<Input placeholder="Age" size="small" label="Age :" id="balloon-input-2" /><br /><br />
</Balloon>
<br />
please input your name:
<Input placeholder="Name" size="small" label="Name :" id="balloon-input-3" /><br /><br />
</Balloon>
<Tooltip trigger={triggerTooltip} id="aria-tooltip">
<p>This is content for tooltip.</p>
</Tooltip>
</div>
);
ReactDOM.render(<App />, mountNode);
.container.nested {
margin-left: 100px;
margin-bottom: 50px;
}