zero-one-uiプロジェクトで作ったUIです。 ReactでGitHub Projectsの見た目をできるだけ真似をして作りました。
ただのドロップダウンメニューやネストできるものなど、細かい違いのあるドロップダウンメニューも作っています。
コマンドからタスクの取得エラーや、タスクの更新エラーを発生させることができ、 それぞれのエラーはユーザーにわかりやすいように表示しています。
ドラッグ&ドロップはライブラリを使って実装したことしかなかったのですが、 今回は何も使用せず、HTML ドラッグ&ドロップ API をそのまま使用して実装しました。 想像していたよりも使いやすく、シンプルなものならこれで十分だと感じました。
ドロップダウンメニューは、最初はRadix UIを使用して実装しようと思ったのですが、マウスを使用してメニューを閉じた場合にも、 メニューのトリガーとなった要素にfocus-visibleが付与されてしまうため、Floating UIを使って実装しています。 原因としては、現在のRadix UIはフォーカスの管理をプログラムで行っている ためらしいです。 他のヘッドレスUIライブラリとしてReact Aria というものもあるのですが、こちらはdata属性を使ってスタイルを当てることができます。 様々な擬似クラスをdata属性を使って拡張しており、例えばhoverクラスにスタイルを当てると、 モバイルでタップしたときにもスタイルが当たってしまうといった問題が起きないらしいです。
これまで、実際のプロジェクトに似たWebアプリはいくつか作ったことがあったのですが、 UIをそのまま作ったことはありませんでした。 CSSがそこまで得意ではなく、UIコンポーネントライブラリを使っての開発ばかりだったので、 難しそうだと感じて避けていたのですが、実際に作ってみると学びがありました。
自分で一から作るのと違って、こういうコンポーネントがほしいから、
前に作ったものをそのまま流用するということが殆どできません。
テキストだけが表示されているボタンを作ったあとに、
そのボタンにアイコンが付いたコンポーネントや色が異なっているコンポーネントを見つけることがよくあります。
こういった状況に対応するために、propsで分岐させることもできますが、
これが増えてくるとコンポーネントが複雑になってくるため、以下のコードのように、
children
を使用した細かいコンポーネントを作っていくことで対応できると学びました。
tsxtype Props = { /* ... */ }; const ItemBase: React.FC<Props> = ( {...props} ) => { const value1 = useSomething1(); const value2 = useSomething2(); useSomething3(); return ( <button {...value1} {...value2} {...props} className={/* .. */}> {children} </button> ); }
似たようなコンポーネントを見つけたときに、どの部分を共通化するとよいかを考えるトレーニングにもなると感じました。
そもそも共通化しないでコピペで済ませるべきではないかとも考えたのですが、 Webフロントエンドの見た目だけを含むようなコンポーネントは、 誤った共通化をしたとしても分離させるのがそこまで難しくなく、 見た目を修正するために複数箇所のコードを変更するほうが面倒くさいと感じるため、積極的に共通化してもよいのではないかと考えています。