import React from 'react'; import ReactDOM from 'react-dom'; import Menu, { SubMenu, Item as MenuItem } from 'rc-menu'; import closest from 'dom-closest'; import classNames from 'classnames'; import Dropdown from '../dropdown'; import Icon from '../icon'; import Checkbox from '../checkbox'; import Radio from '../radio'; import FilterDropdownMenuWrapper from './FilterDropdownMenuWrapper'; export interface FilterMenuProps { locale: any; selectedKeys: string[]; column: { filterMultiple?: boolean, filterDropdown?: React.ReactNode, filters?: { text: string; value: string, children?: any[] }[], filterDropdownVisible?: boolean, onFilterDropdownVisibleChange?: (visible: boolean) => any, fixed?: boolean | string, filterIcon?: React.ReactNode; }; confirmFilter: (column: Object, selectedKeys: string[]) => any; prefixCls: string; dropdownPrefixCls: string; getPopupContainer: (triggerNode?: Element) => HTMLElement; } export default class FilterMenu extends React.Component { static defaultProps = { handleFilter() {}, column: {}, }; neverShown: boolean; constructor(props) { super(props); const visible = ('filterDropdownVisible' in props.column) ? props.column.filterDropdownVisible : false; this.state = { selectedKeys: props.selectedKeys, keyPathOfSelectedItem: {}, // 记录所有有选中子菜单的祖先菜单 visible, }; } componentDidMount() { const { column } = this.props; this.setNeverShown(column); } componentWillReceiveProps(nextProps) { const { column } = nextProps; this.setNeverShown(column); const newState = {} as { selectedKeys: string[]; visible: boolean; }; if ('selectedKeys' in nextProps) { newState.selectedKeys = nextProps.selectedKeys; } if ('filterDropdownVisible' in column) { newState.visible = column.filterDropdownVisible; } if (Object.keys(newState).length > 0) { this.setState(newState); } } setNeverShown = (column) => { const rootNode = ReactDOM.findDOMNode(this); const filterBelongToScrollBody = !!closest(rootNode, `.ant-table-scroll`); if (filterBelongToScrollBody) { // When fixed column have filters, there will be two dropdown menus // Filter dropdown menu inside scroll body should never be shown // To fix https://github.com/ant-design/ant-design/issues/5010 and // https://github.com/ant-design/ant-design/issues/7909 this.neverShown = !!column.fixed; } } setSelectedKeys = ({ selectedKeys }) => { this.setState({ selectedKeys }); } setVisible(visible) { const { column } = this.props; if (!('filterDropdownVisible' in column)) { this.setState({ visible }); } if (column.onFilterDropdownVisibleChange) { column.onFilterDropdownVisibleChange(visible); } } handleClearFilters = () => { this.setState({ selectedKeys: [], }, this.handleConfirm); } handleConfirm = () => { this.setVisible(false); this.confirmFilter(); } onVisibleChange = (visible) => { this.setVisible(visible); if (!visible) { this.confirmFilter(); } } confirmFilter() { if (this.state.selectedKeys !== this.props.selectedKeys) { this.props.confirmFilter(this.props.column, this.state.selectedKeys); } } renderMenuItem(item) { const { column } = this.props; const multiple = ('filterMultiple' in column) ? column.filterMultiple : true; const input = multiple ? ( = 0} /> ) : ( = 0} /> ); return ( {input} {item.text} ); } hasSubMenu() { const { column: { filters = [] } } = this.props; return filters.some(item => !!(item.children && item.children.length > 0)); } renderMenus(items) { return items.map(item => { if (item.children && item.children.length > 0) { const { keyPathOfSelectedItem } = this.state; const containSelected = Object.keys(keyPathOfSelectedItem).some( key => keyPathOfSelectedItem[key].indexOf(item.value) >= 0, ); const subMenuCls = containSelected ? `${this.props.dropdownPrefixCls}-submenu-contain-selected` : ''; return ( {this.renderMenus(item.children)} ); } return this.renderMenuItem(item); }); } handleMenuItemClick = (info) => { if (info.keyPath.length <= 1) { return; } const keyPathOfSelectedItem = this.state.keyPathOfSelectedItem; if (this.state.selectedKeys.indexOf(info.key) >= 0) { // deselect SubMenu child delete keyPathOfSelectedItem[info.key]; } else { // select SubMenu child keyPathOfSelectedItem[info.key] = info.keyPath; } this.setState({ keyPathOfSelectedItem }); } renderFilterIcon = () => { const { column, locale, prefixCls } = this.props; const filterIcon = column.filterIcon as any; const dropdownSelectedClass = this.props.selectedKeys.length > 0 ? `${prefixCls}-selected` : ''; return filterIcon ? React.cloneElement(filterIcon as any, { title: locale.filterTitle, className: classNames(filterIcon.className, { [`${prefixCls}-icon`]: true, }), }) : ; } render() { const { column, locale, prefixCls, dropdownPrefixCls, getPopupContainer } = this.props; // default multiple selection in filter dropdown const multiple = ('filterMultiple' in column) ? column.filterMultiple : true; const dropdownMenuClass = classNames({ [`${dropdownPrefixCls}-menu-without-submenu`]: !this.hasSubMenu(), }); const menus = column.filterDropdown ? ( {column.filterDropdown} ) : ( {this.renderMenus(column.filters)} ); return ( {this.renderFilterIcon()} ); } }