fx-picker.tsx 8 KB
import { DownOutlined, SearchOutlined } from '@ant-design/icons';
import { Empty, Input, Tree } from 'antd';
import _ from 'lodash-es';
import React, {
  memo,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { FunctionProps } from '../index';
import { handleHighlight } from './var-picker';
const emptyImg = require('../svg/custom_chart_null.png');
type PickProps = {
  dataSource: FunctionProps[];
  onHover: (nodeData: FunctionProps | null) => void;
  onPick: (funcData: FunctionProps) => void;
  pickFunc: (funcData: FunctionProps) => void;
};
const FxPicker: React.FC<PickProps> = ({
  onHover,
  onPick,
  pickFunc,
  ...props
}): JSX.Element => {
  const [searchVal, setSearchVal] = useState('');
  const [selectIndex, setSelectIndex] = useState(0);
  const searchInputRef = useRef(null);
  const [filterData, setFilterData] = useState<FunctionProps[]>([]);
  // const selectFunc = useRef<string>('');
  // const change

  const dataSource: FunctionProps[] = useMemo(() => {
    let cloneData = _.cloneDeep(props.dataSource || []);
    cloneData.forEach((item) => {
      (item.children || []).forEach((it: any) => {
        // @ts-ignore
        if (!it.titleCopy && typeof it.title === 'string') {
          it.titleCopy = it.title;
        }
        // @ts-ignore
        it.title = <span style={{ display: 'inline-block' }}>{it.title}</span>;
      });
    });
    return cloneData;
  }, [props.dataSource]);

  useEffect(() => {
    let cloneData = _.cloneDeep(dataSource);
    cloneData.forEach((item: any) => {
      if (item.title?.includes(searchVal)) {
        item.title = handleHighlight(item.title, searchVal);
        item._hidden = false;
      } else {
        if ((item.children || []).length === 0) {
          item._hidden = true;
        }
        (item.children || []).forEach((it: any) => {
          if (!it.funcNameEg.toLowerCase()?.includes(searchVal.toLowerCase())) {
            it._hidden = true;
            item.hiddenLen = (item.hiddenLen || 0) + 1;
            if (item.hiddenLen === item.children?.length) {
              item._hidden = true;
            }
          } else {
            const title = handleHighlight(it.titleCopy, searchVal, 'fx');
            it.title = <span style={{ display: 'inline-block' }}>{title}</span>;
          }
        });
      }
    });
    cloneData = cloneData.filter((item: any) => item._hidden !== true);
    cloneData.forEach((item) => {
      item.children = (item.children || []).filter((it: any) => !it._hidden);
    });
    setFilterData(cloneData);
  }, [searchVal]);

  // 根据查询value过滤公式列表数据
  // const dataSourceFilter: FunctionProps[] = useMemo(() => {
  //   // 扁平化dataSource
  //   const dataSourceFlatten: FunctionProps[] = [];
  //   flatten(dataSource, dataSourceFlatten);
  //   return dataSourceFlatten.filter((ds) => ds.title.includes(searchVal));
  // }, [searchVal, dataSource]);

  const handleSearch = _.debounce((val: string) => {
    setSelectIndex(0);
    setSearchVal(val);
  }, 200);

  // 搜索框值变化时触发
  const onHandleSearchChange = (e: any) => {
    const val = e.target.value;
    handleSearch(val);
  };

  // 设置当前选中的函数公式
  // const onHandlePickFunc = (funcData: FunctionProps) => {
  //   _.isFunction(onPick) && onPick(funcData);
  //   searchInputRef?.current?.blur();
  // };
  /**
   * 设置键盘事件逻辑
   * keyCode 38:ArrowUp  40:ArrowDown  13: Enter
   */
  // const onHandleKeydown = (e: any) => {
  //   if (e.keyCode == 38 && selectIndex > 0) {
  //     setSelectIndex(selectIndex - 1);
  //   } else if (e.keyCode == 40 && selectIndex < dataSourceFilter.length - 1) {
  //     setSelectIndex(selectIndex + 1);
  //   } else if (e.keyCode == 13) {
  //     onHandlePickFunc(dataSourceFilter[selectIndex]);
  //   }
  //   _.isFunction(onHover) && onHover(dataSourceFilter[selectIndex]);
  // };

  // 设置popover-content内滚动条位置。DOM更新之后立即执行
  useLayoutEffect(() => {
    const dom = document.getElementsByClassName('fun-selected')[0];
    if (dom) {
      dom.scrollIntoView(Object.assign({ block: 'center', inline: 'nearest' }));
    }
  }, [selectIndex]);

  // const onHandleUpload = () => {
  //   message.info('功能暂未开发');
  // };

  // const onHandleCustomFunc = () => {
  //   message.info('功能暂未开发');
  // };

  const onHandleMouseEnter = (event: any, nodeData: FunctionProps) => {
    if (nodeData.children && nodeData.children.length > 0) return;
    if (_.isFunction(onHover)) {
      onHover(nodeData);
    }
  };

  const onHandleSelectTNode = (key: any, event: any) => {
    if (event.node.children && event.node.children.length > 0) return;
    if (_.isFunction(onPick)) {
      onPick(event.node);
    }
    if (_.isFunction(onHover)) {
      onHover(event.node);
    }
    pickFunc(event.node);
    // selectFunc.current = event.node.funcNameEg;
  };

  // 设置popover-content
  // const searchContent = useMemo(() => {
  //   return (
  //     <div className="search-content">
  //       {dataSourceFilter.map((ds, index) => {
  //         return (
  //           <div
  //             className={index === selectIndex ? 'fun-selected' : ''}
  //             key={ds.key}
  //             onClick={() => onHandlePickFunc(ds)}
  //           >
  //             {ds.title}
  //           </div>
  //         );
  //       })}
  //     </div>
  //   );
  // }, [dataSourceFilter, selectIndex]);

  const treeRender = useMemo(() => {
    return searchVal.length && !filterData?.length ? (
      <Empty
        image={emptyImg}
        style={{
          height: '100%',
          width: '100%',
          display: 'flex',
          flexDirection: 'column',
          justifyContent: 'center',
          color: '#999',
        }}
      />
    ) : (
      <Tree
        key={Math.random()}
        rootClassName={'custom-tree'}
        blockNode={true}
        selectable={true}
        defaultExpandAll={!!searchVal.length}
        switcherIcon={<DownOutlined />}
        titleRender={(nodeData) => {
          const { title, titleDesc } = nodeData;
          return (
            <div
              className="custom-title"
              onMouseEnter={(event) => {
                onHandleMouseEnter(event, nodeData);
              }}
              onMouseLeave={() => {
                // if (nodeData.funcNameEg === selectFunc.current) {
                //   selectFunc.current = '';
                //   return;
                // }
                if (_.isFunction(onHover)) {
                  onHover(null);
                }
              }}
            >
              <span>{title}</span>
              <span>{titleDesc}</span>
            </div>
          );
        }}
        treeData={searchVal.length ? filterData : dataSource}
        defaultExpandedKeys={['COMMON']}
        onSelect={onHandleSelectTNode}
      />
    );
  }, [dataSource, filterData]);

  return (
    <div className="function-box">
      <div className="search-bar">
        {/*<Popover*/}
        {/*  overlayClassName={'custom-search-popover'}*/}
        {/*  placement="bottom"*/}
        {/*  content={searchContent}*/}
        {/*  trigger={'focus'}*/}
        {/*  autoAdjustOverflow={false}*/}
        {/*  getPopupContainer={(HTMLElement) => HTMLElement.parentNode}*/}
        {/*>*/}
        <Input
          bordered={false}
          ref={searchInputRef}
          onChange={onHandleSearchChange}
          prefix={<SearchOutlined className="search" />}
          // addonAfter={
          //   <>
          //     <UploadOutlined className="upload" onClick={onHandleUpload} />
          //     <PlusOutlined className="plus" onClick={onHandleCustomFunc} />
          //   </>
          // }
          placeholder="搜索函数"
        />
        {/*</Popover>*/}
      </div>
      {dataSource?.length ? (
        treeRender
      ) : (
        <div className={'qx-empty-center'}>(暂无数据)</div>
      )}
      {/*{_.size(dataSource) === 0 && (*/}
      {/*  <div className={'qx-empty-center'}>(暂无数据)</div>*/}
      {/*)}*/}
    </div>
  );
};

export default memo(FxPicker);