import React from 'react'; import { findDOMNode } from 'react-dom'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import Icon from '../icon'; import Button from '../button'; import Overlay from '../overlay'; import Menu from '../menu'; import ConfigProvider from '../config-provider'; import { dom, obj, func } from '../util'; const { Popup } = Overlay; /** * SplitButton */ class SplitButton extends React.Component { static propTypes = { prefix: PropTypes.string, style: PropTypes.object, /** * 按钮的类型 */ type: PropTypes.oneOf(['normal', 'primary', 'secondary']), /** * 按钮组的尺寸 */ size: PropTypes.oneOf(['small', 'medium', 'large']), /** * 主按钮的文案 */ label: PropTypes.node, /** * 设置标签类型 */ component: PropTypes.oneOf(['button', 'a']), /** * 是否为幽灵按钮 */ ghost: PropTypes.oneOf(['light', 'dark', false, true]), /** * 默认激活的菜单项(用法同 Menu 非受控) */ defaultSelectedKeys: PropTypes.array, /** * 激活的菜单项(用法同 Menu 受控) */ selectedKeys: PropTypes.array, /** * 菜单的选择模式 */ selectMode: PropTypes.oneOf(['single', 'multiple']), /** * 选择菜单项时的回调,参考 Menu */ onSelect: PropTypes.func, /** * 点击菜单项时的回调,参考 Menu */ onItemClick: PropTypes.func, /** * 触发按钮的属性(支持 Button 的所有属性透传) */ triggerProps: PropTypes.object, /** * 弹层菜单的宽度是否与按钮组一致 */ autoWidth: PropTypes.bool, /** * 弹层是否显示 */ visible: PropTypes.bool, /** * 弹层默认是否显示 */ defaultVisible: PropTypes.bool, /** * 弹层显示状态变化时的回调函数 * @param {Boolean} visible 弹层显示状态 * @param {String} type 触发弹层显示或隐藏的来源 menuSelect 表示由menu触发; fromTrigger 表示由trigger的点击触发; docClick 表示由document的点击触发 */ onVisibleChange: PropTypes.func, /** * 弹层的触发方式 */ popupTriggerType: PropTypes.oneOf(['click', 'hover']), /** * 弹层对齐方式, 详情见Overlay align */ popupAlign: PropTypes.string, /** * 弹层自定义样式 */ popupStyle: PropTypes.object, /** * 弹层自定义样式类 */ popupClassName: PropTypes.string, /** * 透传给弹层的属性 */ popupProps: PropTypes.object, /** * 是否跟随滚动 */ followTrigger: PropTypes.bool, /** * 透传给 Menu 的属性 */ menuProps: PropTypes.object, /** * 透传给 左侧按钮 的属性 */ leftButtonProps: PropTypes.object, className: PropTypes.string, children: PropTypes.any, }; static defaultProps = { prefix: 'next-', type: 'normal', size: 'medium', autoWidth: true, popupTriggerType: 'click', onVisibleChange: func.noop, onItemClick: func.noop, onSelect: func.noop, defaultSelectedKeys: [], menuProps: {}, leftButtonProps: {}, }; constructor(props, context) { super(props, context); this.state = { selectedKeys: props.selectedKeys || props.defaultSelectedKeys, visible: props.visible || props.defaultVisible, }; } componentDidMount() { // 由于定位目标是 wrapper,如果弹层默认展开,wrapper 还未渲染,didMount 后强制再渲染一次,弹层重新定位 if (this.state.visible) { this.forceUpdate(); } } componentWillReceiveProps(nextProps) { if ('visible' in nextProps) { this.setState({ visible: nextProps.visible, }); } if ('selectedKeys' in nextProps) { this.setState({ selectedKeys: nextProps.selectedKeys, }); } } selectMenuItem = (keys, ...others) => { if (!('selectedKeys' in this.props)) { this.setState({ selectedKeys: keys, }); } this.props.onSelect(keys, ...others); }; clickMenuItem = (key, ...others) => { this.props.onItemClick(key, ...others); this.onVisibleChange(false, 'menuSelect'); }; onPopupOpen = () => { if (this.props.autoWidth && this.wrapper && this.menu) { dom.setStyle(this.menu, { width: this.wrapper.offsetWidth, }); } }; onVisibleChange = (visible, reason) => { if (!('visible' in this.props)) { this.setState({ visible, }); } this.props.onVisibleChange(visible, reason); }; _menuRefHandler = ref => { this.menu = findDOMNode(ref); const refFn = this.props.menuProps.ref; if (typeof refFn === 'function') { refFn(ref); } }; _wrapperRefHandler = ref => { this.wrapper = findDOMNode(ref); }; render() { const { prefix, label, size, type, component, ghost, className, style, children, triggerProps, popupAlign, popupTriggerType, popupStyle, popupClassName, popupProps, followTrigger, selectMode, menuProps, leftButtonProps, disabled, ...others } = this.props; const state = this.state; const classNames = classnames( { [`${prefix}split-btn`]: true, }, className ); const sharedBtnProps = { type, size, component, ghost, disabled, }; const triggerClassNames = classnames({ [`${prefix}split-btn-trigger`]: true, [`${prefix}expand`]: state.visible, opened: state.visible, }); const trigger = ( ); return ( this.wrapper} style={popupStyle} shouldUpdatePosition className={popupClassName} onOpen={this.onPopupOpen} > {children} ); } } SplitButton.Item = Menu.Item; SplitButton.Divider = Menu.Divider; SplitButton.Group = Menu.Group; export default ConfigProvider.config(SplitButton);