[React Ocean 组件库] 实现 DropMenu 下拉菜单

174 阅读1分钟

交互展示

f0a48784-b830-4377-8805-ac6253102372.gif

使用

const Basic = () => {
  const [visible, setVisible] = useState(false);

  return (
    <DropMenu
      setVisible={setVisible}
      visible={visible}
      data={[
        { content: 'Download' },
        { content: 'Copy' },
        { content: '关闭菜单', click: () => setVisible(false) },
      ]}
      content="下拉菜单"
    />
  );
};

需求

  • DropMenu 基本使用
  • 受控菜单
  • 自定义菜单
  • 自定义图标 beforeIcon, afterIcon
  • Hover 模式
  • 支持子菜单

实现

const DropMenu = (props: DropMenu) => {
  const { content, data, visible, setVisible, as, mode } = props;

  const menuClick = (e: any) => {
    e.stopPropagation();
    setVisible(!visible);
  };

  const documentClick = () => {
    setVisible(false);
  };

  useEffect(() => {
    setVisible(visible);
  }, [visible]);

  const menuItemClick = (e: any, click: any) => {
    e.stopPropagation();
    click?.();
  };

  useEffect(() => {
    visible && onEvent(window, 'click', documentClick)();
    return () => {
      visible && offEvent(window, 'click', documentClick)();
    };
  }, [visible]);

  return (
    <DropMenuWrapper
      className="ocean-menu-wrapper"
      onMouseEnter={(e) => {
        if (mode === 'hover') {
          menuClick(e);
        }
      }}
      onMouseLeave={() => {
        if (mode === 'hover') {
          setVisible(false);
        }
      }}
    >
      <DropStartMenu onClick={(e) => menuClick(e)}>
        {as || (
          <Button type="text">
            <div className="ocean-menu-content">{content}</div>
          </Button>
        )}
      </DropStartMenu>
      <CSSTransition
        classNames={'drop-menu-container'}
        in={visible}
        timeout={330}
        appear
        unmountOnExit
      >
        <ContentWrapper className="ocean-content-wrapper">
          {data?.map((menu, index) => {
            return (
              <DropMenuItem
                key={index}
                onClick={(e) => menuItemClick(e, menu.click)}
              >
                <Button type="text" width={210}>
                  {menu.beforeIcon && menu.beforeIcon}
                  {menu.content}
                  {menu.afterIcon}
                  {menu.children && <RightOutlined />}
                </Button>

                {menu.children && (
                  <ChildrenMenuWrapper className="child-menu-wrapper">
                    {menu.children?.map((child, index) => {
                      return (
                        <Button type="text" key={index} width={210}>
                          {child.beforeIcon && child.beforeIcon}
                          {child.content}
                          {child.afterIcon && child.afterIcon}
                        </Button>
                      );
                    })}
                  </ChildrenMenuWrapper>
                )}
              </DropMenuItem>
            );
          })}
        </ContentWrapper>
      </CSSTransition>
    </DropMenuWrapper>
  );
};