#環境
- React.js
- TypeScript
- material-ui
#目的
お気持ち:
- シンプルなwebサイトにしたいが、詳細も載せたい。
- 別リンクに飛ぶ詳細は面倒。
- 要素にhoverした時に詳細がpopしてほしい。
- ↑を可能にするPopper(material-ui)はコード量が30行くらいと多い。
- pop付要素を手軽に実装できるようにラップしよう。
#概要
Popper(material-ui)をラップし、手軽な実装を実現。
##作成したコンポーネントAddPopper
AddPopperの子要素にhoverすると、props.popに渡したReact要素が表示される。
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の定義