【Vue3】ジェネリクスコンポーネント + Panda CSS を使ってコンポーネントを作成してみる
はじめに
2023.05.11 にリリースした Vue3.3 ( Rurouni Kenshin ) でジェネリクスコンポーネント機能が追加されました。
実装の幅が広がる機能ですが Vue3.3 リリースが日が浅いこともあり実装例をあまり見かけない印象です。
本記事ではジェネリクスコンポーネント機能を用いてコンポーネントの実装実験を行っていきます。
ジェネリクスコンポーネントとは
公式ドキュメントでは以下のように説明されています。ジェネリック型パラメーター、なるほど。
Generic type parameters can be declared using the generic attribute on the <script> tag:
公式ドキュメントだけではぼんやりした使用イメージしか浮かばなかったです。
使用例も踏まえたジェネリクスコンポーネントについては@azukiazusa さんの記事が分かりやすかったです。いつも助かってます。
公式ドキュメント、上記記事、どちらも select コンポーネントでの実装例でした。そこで、select コンポーネント以外の使用例を考えてみます。
実験
今回は以下のようなユースケースを想定してみます。
- 付箋コンポーネントを作りたい
- 色は Panda CSS の
colors.500
を利用したい。そのため、色の選択が出来たらいい - 背景色は同じ色にしたい。ただし、濃さは任意に可変させたい(例:
red.100
、red.300
など)。
ジェネリクスコンポーネントを使ってみる
実際に使ってみます。
ColorPalette
、ColorToken
はそれぞれ 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 についてもあまり記事を見かけないなーという印象ですので本記事が少しでも参考になれば幸いです。
Discussion