🧸

【Vue3】ジェネリクスコンポーネント + Panda CSS を使ってコンポーネントを作成してみる

2023/10/08に公開

はじめに

2023.05.11 にリリースした Vue3.3 ( Rurouni Kenshin ) でジェネリクスコンポーネント機能が追加されました。

https://blog.vuejs.org/posts/vue-3-3#generic-components

実装の幅が広がる機能ですが Vue3.3 リリースが日が浅いこともあり実装例をあまり見かけない印象です。
本記事ではジェネリクスコンポーネント機能を用いてコンポーネントの実装実験を行っていきます。

ジェネリクスコンポーネントとは

公式ドキュメントでは以下のように説明されています。ジェネリック型パラメーター、なるほど。

Generic type parameters can be declared using the generic attribute on the <script> tag:

https://ja.vuejs.org/api/sfc-script-setup.html#generics

公式ドキュメントだけではぼんやりした使用イメージしか浮かばなかったです。
使用例も踏まえたジェネリクスコンポーネントについては@azukiazusa さんの記事が分かりやすかったです。いつも助かってます。

https://azukiazusa.dev/blog/vue-generic-component/

公式ドキュメント、上記記事、どちらも select コンポーネントでの実装例でした。そこで、select コンポーネント以外の使用例を考えてみます。

実験

今回は以下のようなユースケースを想定してみます。

  • 付箋コンポーネントを作りたい
  • 色は Panda CSS の colors.500を利用したい。そのため、色の選択が出来たらいい
  • 背景色は同じ色にしたい。ただし、濃さは任意に可変させたい(例: red.100red.300など)。

ジェネリクスコンポーネントを使ってみる

実際に使ってみます。
ColorPaletteColorTokenはそれぞれ Panda CSS のデフォルトカラーを利用しています。
デフォルトカラーテーマについては Panda CSS 公式のTheme#Colorsをご覧ください。

Panda CSS のカラーパレットは Tailwind CSS のカラーパレットと類似しています。そのため、Panda CSS 部分は Tailwind CSS に置き換えてもらっても大丈夫です。

<script setup lang="ts" generic="T extends ColorPalette, U extends ColorToken">
/**
 * NOTE: Panda CSSのデフォルトカラーを利用しています.
 *
 * type ColorPalette = "red" | "yellow" | "orange" | ...
 * type ColorToken = "red.50" | "red.100" | ... | "red.950" |
 *                   "yellow.50" | "yellow.100" | ... | "yellow.950" |
 *                   "orange.50" | "orange.100" | ... | "orange.950" | ...
 */
import type { ColorPalette, ColorToken } from "styled-system/tokens/tokens";

const { title, color, bg } = defineProps<{
  title: string;
  color: T;
  bg: Extract<U, `${T}.${string}`>;
}>();
</script>

<template>
  <div
    :class="
      css({
        width: '200px',
        height: '200px',
        color: `${color}.500`,
        backgroundColor: `${bg}`,
        textDecoration: 'none',
        padding: '0.5rem',
        boxShadow: '5px 5px 5px rgba(0, 0, 0, 0.22)',
        zIndex: 5,
      })
    "
  >
    {{ title }}
  </div>
</template>

意図した付箋が表示されていれば大丈夫そうです。

VS Code できちんと補完されている

VS Code で確認してみるときちんと型補完されていました。

colorで指定した色を選択すると TS エラーの線が表示されました。

背景色も同じで良い場合は?

前項の実験ではcolor = colors.500、background-color = anyとしていました。ではbackground-color = colors.100にした場合どうなるでしょうか。その場合は、そもそもジェネリクスもprops.bgも不要になるかと思います。

- <script setup lang="ts" generic="T extends ColorPalette, U extends ColorToken">
+ <script setup lang="ts">
/**
 * NOTE: Panda CSSのデフォルトカラーを利用しています.
 *
 * type ColorPalette = "red" | "yellow" | "orange" | ...
 * type ColorToken = "red.50" | "red.100" | ... | "red.950" |
 *                   "yellow.50" | "yellow.100" | ... | "yellow.950" |
 *                   "orange.50" | "orange.100" | ... | "orange.950" | ...
 */
- import type { ColorPalette, ColorToken } from "styled-system/tokens/tokens";
+ import type { ColorPalette } from "styled-system/tokens/tokens";

const { title, color, bg } = defineProps<{
  title: string;
+ color: ColorPalette;
- color: T;
- bg: Extract<U, `${T}.${string}`>;
}>();
</script>

<template>
  <div
    :class="
      css({
        width: '200px',
        height: '200px',
        color: `${color}.500`,
-       backgroundColor: `${bg}`,
+       backgroundColor: `${color}.100`,
        textDecoration: 'none',
        padding: '0.5rem',
        boxShadow: '5px 5px 5px rgba(0, 0, 0, 0.22)',
        zIndex: 5,
      })
    "
  >
    {{ title }}
  </div>
</template>

おわりに

Vue3.3 で追加された ジェネリクスコンポーネント についての実験を行いました。使い所は考える必要があるものの、より汎用的なコンポーネント実装が出来そうですね。
「型パズル、こうした方がええで」などもあればコメントお願いいたします。

Vue3 についても Panda CSS についてもあまり記事を見かけないなーという印象ですので本記事が少しでも参考になれば幸いです。

Vue・Nuxt 情報が集まる広場 / Plaza for Vue・Nuxt.

Discussion