一般的なダッシュボードレイアウトを作るよ!
コマンドは yarn
を使うので適宜読み替えてください。
登場人物
Nuxt3
Vue に自動インポートやルーター機能、サーバー機能を載せた、なんでもフレームワーク。
React で言う Next。
Nuxt3 になってから、かなり綺麗に整備されており、正直 Next よりシンプルにまとまってると思う。(信者感)
Tailwind
流行りの CSS 全部 Class で書こうライブラリ。
賛否両論はあると思うが、Vue や React で採用すると、UI レイアウトを組むのが非常にはかどる。
慣れたら戻れなくなる。
PrimeVue
Vue の UI ライブラリ。
Vue と言ったら Vuetify が良く言われるが、個人的にはこちらを激押しする。
Tailwind によって組まれており、カスタマイズ、上書きがとても簡単。
ちょっと使いにくいと思うところは、気軽に上書きしていこう。
(あとは Naive UI もおすすめ)
環境構築
Tailwind は moduler が用意されてるので、とても簡単に導入できる。
PrimeVue は手動で入れていく。StyleMode がおすすめ。
参考:
https://nuxt.com/docs/getting-started/installation
https://tailwindcss.com/docs/guides/nuxtjs#modules
https://primevue.org/nuxt
https://primevue.org/icons/
$ npx nuxi@latest init nuxt3-primevue
> Which package manager would you like to use?
yarn
$ cd nuxt3-primevue
# install tailwind
$ npx nuxi module add @nuxtjs/tailwindcss
$ npx tailwindcss init
# install primevue
$ yarn add primevue @primevue/themes primeicons
$ yarn add --dev @primevue/nuxt-module
PrimeVue の設定を nuxt.config.ts
に追記する。
テーマは大きく「Aura」と「Lara」がある。
Lara のほうが余白を大きくしたレイアウト。お好きなほうを。
import Aura from '@primevue/themes/aura';
export default defineNuxtConfig({
modules: [
'@primevue/nuxt-module',
],
css: [
'primeicons/primeicons.css',
],
primevue: {
options: {
theme: {
preset: Aura,
},
},
},
})
App.vue
にコンポーネントを入れてみて、使えるか確認する。
<template>
<div>
<div class="bg-blue-500">
<Button icon="pi pi-home" label="おうち"></Button>
</div>
<NuxtRouteAnnouncer />
<NuxtWelcome />
</div>
</template>
導入成功!
1. ダッシュボードレイアウトを作る
大きく以下の項目に分かれる、よく見るレイアウト。
レイアウトは Nuxt の Layout 機能を使う。とても便利。
レイアウトを仮組みする
私は flex
を多用してレイアウトを組みがちです。
それぞれの div
に色を付けて、配置していきます。
(figuma とか使った方が現代的なんだろうけどさ...)
Layout を使うときは app.vue
の中身を以下で上書きする。
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
そして layouts/dashboardLayout.vue
pages/index.vue
のファイルを作成する。
(久々に触ったら Vue のファイル名、小文字が推奨されている...)
<template>
<div class="h-screen flex flex-col bg-red-800">
<div class="h-[4rem] bg-orange-800">
header
</div>
<div class="grow flex">
<div class="w-[10rem] bg-blue-800">
sidebar
</div>
<div class="grow bg-green-800">
<slot />
</div>
</div>
</div>
</template>
<template>
<NuxtLayout name="dashboard-layout">
content
</NuxtLayout>
</template>
ベースはこんなところ。
まず上下に区切って、余りを全て下に渡す。(flex flex-col
と grow
)
次に下を左右に区切って、余りを全て右に渡す。(flex
と grow
)
スクロールを考慮する
このままでは画面をはみ出したときに、サイドバーがつられてスクロールしてしまう。
必要なエリアのみがスクロールする方が、良い体験だと思っている。
色んな方法があるが、私は flex
を多用する。(2回目)
今回は css variable を利用する。
<template>
<div class="h-screen flex flex-col bg-red-800"> <!-- (1) -->
<div class="h-[var(--app-header-height)] bg-orange-800"> <!-- (2) -->
header
</div>
<div class="grow h-[var(--app-header-height)] flex"> <!-- (3) -->
<div class="w-[var(--app-sidebar-width)] overflow-auto bg-blue-800"> <!-- (4) -->
sidebar
</div>
<div class="grow overflow-auto p-2 bg-green-800"> <!-- (5) -->
<slot />
</div>
</div>
</div>
</template>
<style lang="css">
:root {
--app-header-height: 3.5rem;
--app-sidebar-width: 12rem;
}
</style>
こんな感じ。
ヘッダやサイドバーは大きさが固定なので、:root
要素に値を追加した。
こちらを使用することで、後で書き換えやすくしている。
そしてスクロールさせたい箇所に overflow-auto
を付与した。
flex はお絵描きすると理解しやすい。
これで独立スクロールの完成。
通常はここまでで十分。
2. コンテンツにカードとテーブルを配置する
私がよくやるレイアウト。
content
領域にカードをフルサイズで配置して、カードの body だけスクロールさせたい。
こんな感じ。コンテンツ領域をスクロールするのではなく、テーブル要素だけスクロールさせる。
必要な要素だけスクロールさせると、目にとても良い。(個人の感想)
テーブルとか、現在の検索情報とか常に最上部にあってほしい。業務アプリならなおさら。
これらを使う:
https://primevue.org/card/
https://primevue.org/datatable/
<template>
<NuxtLayout name="dashboard-layout">
<Card <!-- (1) 内部 -->
class="h-full"
:pt="{
body: 'h-full',
content: 'h-full relative grow', <!-- (3) -->
}"
>
<template #title> <!-- (2) -->
たいとる<br>asd<br>asd
</template>
<template #content> <!-- (3) -->
<div class="absolute size-full"> <!-- (5) -->
<DataTable
:value="items"
scrollable
:pt="{
root: 'h-full',
tablecontainer: 'h-full border',
table: 'h-full',
tbody: 'h-full',
bodyrow: 'h-full',
bodycell: 'h-full',
}"
>
<Column field="id" header="ID" body-class="min-w-[2rem] max-w-[2rem]" />
<Column field="product" header="商品名" body-class="min-w-[8rem] max-w-[8rem]" />
<Column field="price" header="価格" body-class="min-w-[5rem] max-w-[5rem]"/>
<Column field="qty" header="個数" body-class="min-w-[5rem] max-w-[5rem]"/>
<Column field="note" header="説明文" body-class="whitespace-nowrap"/>
</DataTable>
</div>
</template>
<template #footer> <!-- (4) -->
フッダ
</template>
</Card>
</NuxtLayout>
</template>
<script setup lang="ts">
const items = computed(() => {
return Array(50).fill({
id: 1,
product: 'たまねぎ',
price: 100,
qty: 5,
note: '淡路島産のおいしいたまねぎです。とても甘く、たまねぎスープで食べるのがおすすめです。',
})
})
</script>
PrimeVue の pt 機能を使っているため、少しわかりにくい可能性あり。
テーブル要素を画面内でスクロールさせようとすると、高さを指定する必要が出てくる。
でも、描画時の高さは簡単には分からず、JavaScript で高さ計算を実装したことがある人も多いと思う。
またこの grow
が厄介で、こいつ自身は高さ要素を持たない。
そのため、残った余りの高さを簡単に取り扱うことができない。。。
そこで!ポイントは二点!
-
h-screen
を指定したタグからすべてのタグにh-full
と 100% を使用する。 -
grow
要素にrelative
を付与して、その子供にabosolude size-full
を使用する。
こうすることで、grow 要素はそのままに height: 100% が実現できるという。
この仕組みを知った時、とても感動した。。。
まとめ
PrimeVue いいよ!
使う人にやさしい UI 作ろうね!
(grid はまだ未勉強...)