2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【material-ui】「要素hover→別要素pop」を簡単に記述するコンポーネント

Last updated at Posted at 2021-04-15

#環境

  • React.js
  • TypeScript
  • material-ui

#目的
お気持ち:

  • シンプルなwebサイトにしたいが、詳細も載せたい。
  • 別リンクに飛ぶ詳細は面倒。
  • 要素にhoverした時に詳細がpopしてほしい
  • ↑を可能にするPopper(material-ui)はコード量が30行くらいと多い。
  • pop付要素を手軽に実装できるようにラップしよう。

#概要
Popper(material-ui)をラップし、手軽な実装を実現。

##作成したコンポーネントAddPopper
AddPopperの子要素にhoverすると、props.popに渡したReact要素が表示される。

  • 使用例
    Videotogif.gif

Webサイトに表示した、カプースチンのソナタファンタジーいいよねという文字列の「カプースチン」と「ソナタファンタジー」の部分にhoverした時、補足説明が現れる(popする)ようにする。

app.tsx
function App() {
  return (
    <div className="App">
      <AddPopper
        //捕捉説明①
        pop={<div>作曲家: Nicolai Kapustin(1937-2020, ウクライナ)</div>}
      >
        {/*このspanにhoverすると捕捉説明①が表示される。*/}
        <span>カプースチン</span>
      </AddPopper>{/*補足説明②*/}
      <AddPopper pop={<div>Op.39 4楽章構成</div>}>
        {/*このspanにhoverすると捕捉説明②が表示される。*/}
        <span>ソナタファンタジー</span>
      </AddPopper>
      いいよね。
    </div>
  );
}

  • propsの定義
interface Props {
  pop: React.ReactElement<any> //hover時に表示する要素
  type: 'span' | 'div' //被hover要素がinlineかどうかを指定
  children: React.ReactElement<any> //この要素にhoverすると発火
  placement?: PopperPlacementType; //popの位置を決定
}

#AddPopperのソースコード

import { Fade, PopperPlacementType } from "@material-ui/core";
import { Popper } from "@material-ui/core";
import React, { useCallback, useState } from "react";

interface Props {
  pop: React.ReactElement<any>;
  children: React.ReactElement<any>;
  placement?: PopperPlacementType;
  type?: "span" | "div";
}

export function AddPopper(props: Props) {
  //被hover要素の位置を受け取る。
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);

  //hover時に発火
  const openPop = useCallback((event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  }, []);

  //hoverが外れた時に発火
  const closePop = useCallback(() => {
    setAnchorEl(null);
  }, []);

  //hoverの有無をbooleanで持つ
  const open = Boolean(anchorEl);

  //被hover要素に、カーソル設定とhover時背景色設定を追加している。
  const Base = () =>
    React.cloneElement(props.children, {
      style: {
        cursor: "default",
        backgroundColor: open ? "lightgrey" : undefined,
      },
    });

  //被hover要素とPop要素にマウスイベントを付加
  return React.createElement(
    props.type ?? "div",
    { onMouseEnter: openPop, onMouseLeave: closePop },
    [
      <Base key="base" />,
      <Popper
        key="popper"
        open={open}
        anchorEl={anchorEl}
        transition
        placement={props.placement ?? "bottom-start"}
      >
        {({ TransitionProps }) => (
          <Fade {...TransitionProps} timeout={350}>
            {props.pop}
          </Fade>
        )}
      </Popper>,
    ]
  );
}

#ソースコードの説明

  • 基本的にはPopper(material-ui)の書き方に倣って書いている。
  • props.childrenとして受け取った要素(AddPopperの子要素)には、hover時のカーソルと、hover時の背景色が自動的に指定される。
  • 被hover要素が、inlineの時はspanを、blockの時はdiv、という使い分けのために、React.CreateElementによってtype(タグ名)をpropsに応じて指定可能にしている。
  • props.placementでpopの位置を調整。defaultはbottom-startとなっている。参照->PopperPlacementTypeの定義
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?