var-picker.tsx 8.14 KB
import { DownOutlined, SearchOutlined } from '@ant-design/icons';
import { getWidgetsIcon } from '@qx/utils';
import { Empty, Input, Tooltip, Tree } from 'antd';
import _ from 'lodash-es';
import React, {
  memo,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { ColsTreeSelectProps } from '../index';
import emptyImg from '../svg/custom_chart_null.png';
import { widgetMapping } from '../util';

type VarProps = {
  dataSource: ColsTreeSelectProps[];
  onPick: (varData: ColsTreeSelectProps) => void;
};

const IconMap: any = {
  CUR_USER: 'qx-f-icon-user',
  CUR_ORG: 'qx-f-icon-org',
  CUR_CORP: 'qx-f-icon-corp',
  LOGIN_INFO: 'qx-f-icon-loginInfo',
  CUR_SERVER: 'qx-f-icon-server',
};

export const handleHighlight = (title: string, val: string, type?: string) => {
  if (!title) return null;
  let index: number;
  if (type === 'fx') {
    index = title.toLowerCase()?.indexOf(val.toLowerCase());
  } else {
    index = title.indexOf(val);
  }
  return (
    <span>
      {title.slice(0, index)}
      <span className={'tree-node-high'}>
        {title.slice(index, index + val.length)}
      </span>
      {title.slice(index + (val?.length || 0))}
    </span>
  );
};

const VarPicker: React.FC<VarProps> = ({ onPick, ...props }) => {
  const [searchVal, setSearchVal] = useState('');
  const [selectIndex, setSelectIndex] = useState(0);
  const searchInputRef = useRef(null);
  const [filterData, setFilterData] = useState<ColsTreeSelectProps[]>([]);

  const dataSource: ColsTreeSelectProps[] = useMemo(() => {
    let cloneData = _.cloneDeep(props.dataSource || []);
    cloneData.forEach((item) => {
      (item.children || []).forEach((it) => {
        // @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.titleStr?.includes(searchVal)) {
        item.titleStr = handleHighlight(item.titleStr, searchVal);
        item._hidden = false;
      } else {
        (item.children || []).forEach((it: any) => {
          if (!it.titleStr?.includes(searchVal)) {
            it._hidden = true;
            item.hiddenLen = (item.hiddenLen || 0) + 1;
            if (item.hiddenLen === item.children?.length) {
              item._hidden = true;
            }
          } else {
            it.titleStr = handleHighlight(it.titleStr, searchVal);
          }
        });
      }
    });
    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: ColsTreeSelectProps[] = useMemo(() => {
  //   // 扁平化dataSource
  //   const dataSourceFlatten: ColsTreeSelectProps[] = [];
  //   flatten(dataSource, dataSourceFlatten);
  //   return dataSourceFlatten.filter((ds) => ds.titleStr?.includes(searchVal));
  // }, [searchVal, dataSource]);

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

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

  // 设置当前选中的变量
  // const onHandlePickVar = (varData: ColsTreeSelectProps) => {
  //   _.isFunction(onPick) && onPick(varData);
  //   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) {
  //     onHandlePickVar(dataSourceFilter[selectIndex]);
  //   }
  // };

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

  const onHandleSelectTNode: any = (key: string, event: any) => {
    if (event.node.children && event.node.children.length > 0) return;
    if (_.isFunction(onPick)) {
      onPick(event.node);
    }
  };

  // 设置popover-content
  // const searchContent = useMemo(() => {
  //   return (
  //     <div className="search-content">
  //       {dataSourceFilter.map((ds, index) => {
  //         return (
  //           <div
  //             className={index === selectIndex ? 'var-selected' : ''}
  //             key={ds.key}
  //             onClick={() => onHandlePickVar(ds)}
  //           >
  //             {ds.titleStr}
  //           </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',
        }}
        description="暂无数据"
      />
    ) : (
      <Tree
        key={Math.random()}
        rootClassName={'custom-tree'}
        blockNode={true}
        selectable={true}
        switcherIcon={<DownOutlined />}
        defaultExpandAll={!!searchVal.length}
        titleRender={(nodeData: any) => {
          const { titleStr, widget } = nodeData;
          const key = nodeData.key.slice(6, -1);
          const { name, color, bgColor } = widgetMapping[widget] || {};
          const icon = getWidgetsIcon(widget);
          return (
            <div className="custom-title">
              <Tooltip title={titleStr} placement={'topRight'}>
                <div
                  style={
                    IconMap[key]
                      ? { display: 'flex', alignItems: 'center' }
                      : {}
                  }
                >
                  {IconMap[key] ? (
                    <span className={`qx-f-icon-common ${IconMap[key]}`} />
                  ) : widget ? (
                    <span className="icon">{icon}</span>
                  ) : null}
                  <span>{titleStr}</span>
                </div>
              </Tooltip>
              {widget && (
                <span
                  className="tag"
                  style={{ backgroundColor: bgColor, color: color }}
                >
                  {name}
                </span>
              )}
            </div>
          );
        }}
        treeData={searchVal.length ? filterData : dataSource}
        defaultExpandedKeys={['FORM']}
        onSelect={onHandleSelectTNode}
      />
    );
  }, [dataSource, filterData]);

  return (
    <div className="variable-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" />}
          placeholder="搜索变量"
        />
        {/*</Popover>*/}
      </div>
      {dataSource?.length ? (
        treeRender
      ) : (
        <Empty
          image={emptyImg}
          style={{
            height: '100%',
            width: '100%',
            display: 'flex',
            flexDirection: 'column',
            justifyContent: 'center',
            color: '#999',
          }}
          description="暂无数据"
        />
      )}
      {/*{_.size(dataSource) === 0 && (*/}
      {/*  <div className={'qx-empty-center'}>(暂无数据)</div>*/}
      {/*)}*/}
    </div>
  );
};

export default memo(VarPicker);