はじめに
前回、とりあえず Phoenix のトップ画面にコンポーネントを配置してみました
しかし、それだと実用的ではないので mix phx.gen.live
で生成した画面に組み込んでみます
基本となるプロジェクトの準備
プロジェクトの作成
新しいプロジェクトを作成し、ディレクトリー配下に移動します
mix archive.install hex phx_new
mix phx.new salad_ui_sample
cd salad_ui_sample
データベースの用意
今回はデータベースを使用するため、 Docker で起動します(ローカルで起動している場合などはそのまま使って問題ありません)
"docker-compose.yml" をカレントディレクトリーに以下の内容で作成します
POSTGRES_USER
などの値は作成したプロジェクトの設定に従っています
---
services:
db:
container_name: postgres
image: postgres:15.7-bullseye
ports:
- "5432:5432"
volumes:
- postgres:/var/lib/postgresql/data
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=salad_ui_sample_dev
volumes:
postgres:
PostgreSQL のコンテナを起動します
--detach
でコンテナを裏で起動したままにしています
docker compose up --detach
依存モジュールをインストールし、データベースを作成します
mix setup
画面の生成
以下のコマンドでユーザー一覧、ユーザー詳細、ユーザー作成/更新などの画面を一括生成します
mix phx.gen.live Accounts User users name:string age:integer
"lib/salad_ui_sample_web/router.ex" に各画面のルートを追記します
...
scope "/", SaladUiSampleWeb do
pipe_through :browser
get "/", PageController, :home
+ live "/users", UserLive.Index, :index
+ live "/users/new", UserLive.Index, :new
+ live "/users/:id/edit", UserLive.Index, :edit
+
+ live "/users/:id", UserLive.Show, :show
+ live "/users/:id/show/edit", UserLive.Show, :edit
end
...
ユーザーテーブルをデータベース上に作成します
mix ecto.migrate
ベースとなる画面の確認
Phoenix を起動します
mix phx.server
http://localhost:4000/users
にアクセスすると、ユーザー一覧画面が確認できます
ユーザーを一人作成してみましょう
SaladUI の導入
プロジェクトに SaladUI を導入します
前回の手順とほぼ同じです
SaladUI モジュールの追加
{:salad_ui, "~> 0.5"}
を "mix.exs" 内の依存モジュールに追加します
...
defp deps do
[
{:phoenix, "~> 1.7.14"},
{:phoenix_html, "~> 4.1"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
# TODO bump on release to {:phoenix_live_view, "~> 1.0.0"},
{:phoenix_live_view, "~> 1.0.0-rc.1", override: true},
...
- {:bandit, "~> 1.5"}
+ {:bandit, "~> 1.5"},
+ {:salad_ui, "~> 0.5"}
]
end
...
依存モジュールをインストールします
mix deps.get
カスタムカラーの追加
shadcn/ui のテーマ提供ページを開きます
前回から少し画面が変わりました
"Customize" のボタンをクリックして好きな色を選択します
カスタムカラー用の CSS コードがモーダルで開くので、右上 "Copy" をクリックしてコードをクリップボードにコピーしておきます
プロジェクト内の "assets/css/app.css" にコピーしたコードを追加します
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
/* This file is for your main application CSS */
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+ --card: 0 0% 100%;
...
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 224.3 76.3% 48%;
+ }
+}
"assets/tailwind.colors.json" を新規作成し、以下のコードを貼り付けます
{
"accent": {
"DEFAULT": "hsl(var(--accent))",
"foreground": "hsl(var(--accent-foreground))"
},
"background": "hsl(var(--background))",
"border": "hsl(var(--border))",
"card": {
"DEFAULT": "hsl(var(--card))",
"foreground": "hsl(var(--card-foreground))"
},
"destructive": {
"DEFAULT": "hsl(var(--destructive))",
"foreground": "hsl(var(--destructive-foreground))"
},
"foreground": "hsl(var(--foreground))",
"input": "hsl(var(--input))",
"muted": {
"DEFAULT": "hsl(var(--muted))",
"foreground": "hsl(var(--muted-foreground))"
},
"popover": {
"DEFAULT": "hsl(var(--popover))",
"foreground": "hsl(var(--popover-foreground))"
},
"primary": {
"DEFAULT": "hsl(var(--primary))",
"foreground": "hsl(var(--primary-foreground))"
},
"ring": "hsl(var(--ring))",
"secondary": {
"DEFAULT": "hsl(var(--secondary))",
"foreground": "hsl(var(--secondary-foreground))"
}
}
TailwindCSS の設定
"assets/tailwind.config.js" を開いて編集します
...
module.exports = {
content: [
"./js/**/*.js",
+ "../deps/salad_ui/lib/**/*.ex",
"../lib/salad_ui_sample_web.ex",
"../lib/salad_ui_sample_web/**/*.*ex"
],
theme: {
extend: {
- colors: {
- brand: "#FD4F00",
- }
+ colors: require("./tailwind.colors.json")
},
},
plugins: [
require("@tailwindcss/forms"),
+ require("@tailwindcss/typography"),
+ require("tailwindcss-animate"),
...
tailwindcss-animate の追加
TailwindCSS のアニメーション用プラグイン tailwindcss-animate を追加します
cd assets
yarn add -D tailwindcss-animate
cd ..
tails の設定
TailwindCSS の Elixir 用ユーティリティーである tails を設定します
"config/config.exs" に以下のコードを追加します
config :tails, colors_file: Path.join(File.cwd!(), "assets/tailwind.colors.json")
SaladUI コンポーネントの適用、追加
ベースとなる画面に SaladUI を組み込んでいきます
別名の設定
CoreComponents と共存させるため、使用する各コンポーネントに別名をつけておきます
"lib/salad_ui_sample_web.ex"
...
defp html_helpers do
quote do
# HTML escaping functionality
import Phoenix.HTML
# Core UI components and translation
import SaladUiSampleWeb.CoreComponents
import SaladUiSampleWeb.Gettext
# Shortcut for generating JS commands
alias Phoenix.LiveView.JS
+ alias SaladUI.Button, as: SaladButton
+ alias SaladUI.Input, as: SaladInput
+ alias SaladUI.Tooltip, as: SaladTooltip
# Routes generation with the ~p sigil
unquote(verified_routes())
end
end
...
ボタンの変更
画面内のボタンに SaladButton の別名を付けるだけで変更可能です
選択したテーマの色が適用されます
"lib/salad_ui_sample_web/live/user_live/index.html.heex"
<.header>
Listing Users
<:actions>
<.link patch={~p"/users/new"}>
- <.button>New User</.button>
+ <SaladButton.button>New User</SaladButton.button>
</.link>
</:actions>
</.header>
...
lib/salad_ui_sample_web/live/user_live/show.html.heex
<.header>
User <%= @user.id %>
<:subtitle>This is a user record from your database.</:subtitle>
<:actions>
<.link patch={~p"/users/#{@user}/show/edit"} phx-click={JS.push_focus()}>
- <.button>Edit user</.button>
+ <SaladButton.button>Edit user</SaladButtonbutton>
</.link>
</:actions>
</.header>
lib/salad_ui_sample_web/live/user_live/form_component.ex
...
<:actions>
- <.button phx-disable-with="Saving...">Save User</.button>
+ <SaladButton.button phx-disable-with="Saving...">Save User</SaladButton.button>
</:actions>
...
Input の変更
Input の場合、単純に入れ替えるだけではいけません
外観は SaladInput
を付けただけですぐに変わりますが、値を変更したりするとエラーが出力されます
name
属性に user[name]
のように <テーブル名>[<列名>]
となるように値を設定しましょう
...
<.simple_form
for={@form}
id="user-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save"
>
- <.input field={@form[:name]} type="text" label="Name" />
+ <SaladInput.input
+ field={@form[:name]}
+ type="text"
+ placeholder="Name"
+ name="user[name]"
+ id="user_name"
+ />
- <.input field={@form[:age]} type="number" label="Age" />
+ <SaladInput.input
+ field={@form[:age]}
+ type="number"
+ placeholder="Age"
+ name="user[age]"
+ id="user_id"
+ />
<:actions>
<SaladButton.button phx-disable-with="Saving...">Save User</SaladButton.button>
</:actions>
</.simple_form>
...
Tooltip を追加する
マウスオーバーでツールチップを表示させたい場合、 SaladTooltip.tooltip
タグでマウスオーバーの対象を囲み、 SaladTooltip.tooltip_content
内にツールチップの内容を記載します
<.header>
Listing Users
<:actions>
- <.link patch={~p"/users/new"}>
- <SaladButton.button>New User</SaladButton.button>
- </.link>
+ <SaladTooltip.tooltip>
+ <.link patch={~p"/users/new"}>
+ <SaladButton.button>New User</SaladButton.button>
+ </.link>
+ <SaladTooltip.tooltip_content>
+ <p>Open Modal.</p>
+ </SaladTooltip.tooltip_content>
+ </SaladTooltip.tooltip>
</:actions>
</.header>
まとめ
SaladUI のコンポーネントに別名を付けることで CoreComponents と共存させました
外見だけなら別名を頭に付けるだけで既存のコンポーネントを変換可能ですが、特に入力が絡むようなものはそのままではエラーになるため、内部処理を理解して書き換えましょう
ちなみに、 Table は大掛かりな変更になりそうなので今回は対象外としました