PETAL components環境構築

# 目標
下記のcomponentを表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/c6c30042-b1ff-a4d0-9f09-3833144a75a6.png)

# 参考資料

インストール方法

https://petal.build/components

# 大まかな流れ

- phoenix 1.7.0でプロジェクトの作成
  - phoenixのバージョンが1.7.3は相性が悪かった
  - mix.exsにpetal_components追加
- Tailwindの設定
  - phoenix 1.7.0はTailwindインストール済みである
  - tailwind.config.js変更
  - app.css変更
- [your_app]_web.ex変更
  -  今回はpetal_experiment_web.ex
- core_components.exから競合する部分をコメントアウト
- config.exs変更
- alpinejs設定
- サンプルページ作成

# プロジェクト作成
```
$ mix phx.new petal_experiment --no-ecto
$ cd petal_experiment
```
# mix.exsにpetal_components追加

```diff_elixir: mix.exs
defmodule PetalExperiment.MixProject do
  # 〜省略〜
    defp deps do
    [
      {:phoenix, "~> 1.7.0"},
      {:phoenix_html, "~> 3.3"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.18.16"},
      {:heroicons, "~> 0.5"},
      {:floki, ">= 0.30.0", only: :test},
      {:phoenix_live_dashboard, "~> 0.7.2"},
      {:esbuild, "~> 0.5", runtime: Mix.env() == :dev},
      {:tailwind, "~> 0.1.8", runtime: Mix.env() == :dev},
      {:swoosh, "~> 1.3"},
      {:finch, "~> 0.13"},
      {:telemetry_metrics, "~> 0.6"},
      {:telemetry_poller, "~> 1.0"},
      {:gettext, "~> 0.20"},
      {:jason, "~> 1.2"},
-     {:plug_cowboy, "~> 2.5"}
+     {:plug_cowboy, "~> 2.5"},
+     {:petal_components, "~> 1.0"}
    ]
  end
  # 〜省略〜
end
```

```
$ mix deps.get
```

# Tailwindの設定

```diff_javascript:assets/tailwind.config.js
// See the Tailwind configuration guide for advanced usage
// https://tailwindcss.com/docs/configuration

+ const colors = require("tailwindcss/colors")
const plugin = require("tailwindcss/plugin")

module.exports = {
  content: [
    "./js/**/*.js",
    "../lib/*_web.ex",
-   "../lib/*_web/**/*.*ex"
+   "../lib/*_web/**/*.*ex",
+   "../deps/petal_components/**/*.*ex",
  ],
  theme: {
    extend: {
      colors: {
        brand: "#FD4F00",

+        primary: colors.blue,
+        secondary: colors.pink,
+        success: colors.green,
+        danger: colors.red,
+        warning: colors.yellow,
+        info: colors.sky,

        // Options: slate, gray, zinc, neutral, stone
        gray: colors.gray,
      }
    },
  },
  plugins: [
    require("@tailwindcss/forms"),
    plugin(({addVariant}) => addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])),
    plugin(({addVariant}) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])),
    plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
    plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"]))
  ]
}
```

```diff:assets/css/app.css
@import "tailwindcss/base";
+ @import "../../deps/petal_components/assets/default.css";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
```

# petal_experiment_web.exs変更

```diff_elixir:lib/petal_experiment_web.ex
defmodule PetalExperimentWeb do
  # 〜省略〜
    defp html_helpers do
    quote do
      # HTML escaping functionality
      import Phoenix.HTML
      # Core UI components and translation
      import PetalExperimentWeb.CoreComponents
      import PetalExperimentWeb.Gettext
      # Shortcut for generating JS commands
      alias Phoenix.LiveView.JS

+     use PetalComponents

      # Routes generation with the ~p sigil
      unquote(verified_routes())
    end
  end
  # 〜省略〜
end
```

# core_components.exから競合する部分をコメントアウト
下記に関係する箇所が対象
- def modal(assigns) do
- def button(assigns) do
- def table(assigns) do

長いので↓を参考にしてください

https://github.com/ymn-t-yamanashi/petal_experiment/commit/0dd2aabe071f5d8529371e99806744e466a3a054#diff-38a06beec84e9a555144d38e8e070ec0ff3d7d6a04554be92e2214a9276a1752

# config.exs変更

```diff_elixir:config/config.exs
import Config

+ config :petal_components, :error_translator_function, {PetalExperimentWeb.ErrorHelpers, :translate_error}
# 〜省略〜
```

# alpinejs設定

```diff:lib/petal_experiment_web/components/layouts/root.html.heex
<!DOCTYPE html>
<html lang="en" style="scrollbar-gutter: stable;">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="csrf-token" content={get_csrf_token()} />
    <.live_title suffix=" · Phoenix Framework">
      <%= assigns[:page_title] || "PetalExperiment" %>
    </.live_title>
    <link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
    <script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
    </script>
+   <script defer src="https://unpkg.com/@alpinejs/collapse@3.x.x/dist/cdn.min.js">
+   </script>
+   <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js">
+   </script>
  </head>
  <body class="bg-white antialiased">
    <%= @inner_content %>
  </body>
</html>
```

# サンプルページ作成

```elixir:lib/petal_experiment_web/live/petal_live/index.ex
defmodule PetalExperimentWeb.PetalLive.Index do
  use PetalExperimentWeb, :live_view

  @impl true
  def mount(_params, _session, socket) do
    {:ok, socket}
  end
end
```

```heex:lib/petal_experiment_web/live/petal_live/index.html.heex
<p class="text-2xl mb-10">Petal Components検証</p>

<p class="text-xl mb-1 mt-10">accordion</p>
<.accordion class="w-full">
  <:item heading="Accordion">
    <.p>
      ほげほげ
    </.p>
  </:item>

  <:item heading="Accordion 2">
    <.p>
      ほげほげ
    </.p>
  </:item>
</.accordion>

<p class="text-xl mb-1 mt-10">alert</p>
<.alert color="success" label="This is a success alert" />

<p class="text-xl mb-1 mt-10">avatar</p>
<.avatar size="xs" />

<p class="text-xl mb-1 mt-10">badge</p>
<.badge color="primary" label="Primary" />

<p class="text-xl mb-1 mt-10">breadcrumbs</p>
<.breadcrumbs
  separator="chevron"
  links={[
    %{label: "Link 1", to: "#"},
    %{label: "Link 2", to: "#", link_type: "live_patch"},
    %{label: "Link 3", to: "#", link_type: "live_redirect"}
  ]}
/>

<p class="text-xl mb-1 mt-10">button</p>
<.button color="primary" label="Primary" />

<p class="text-xl mb-1 mt-10">card</p>
<.card>
  <.card_content category="カテゴリ" class="max-w-sm" heading="ヘッダー">
    内容 12345689123456891234568912345689123456891234568912345689123456891234568912345689
  </.card_content>
</.card>

<p class="text-xl mb-1 mt-10">dropdown</p>
<.dropdown>
  <.dropdown_menu_item label="ほげほげ" />
  <.dropdown_menu_item label="ほげほげ" />
</.dropdown>

<p class="text-xl mb-1 mt-10">spinner</p>
<.spinner show={true} size="sm" />

<p class="text-xl mb-1 mt-10">rating</p>
<.rating rating={4} total={5} />

<p class="text-xl mb-1 mt-10">pagination</p>
<.pagination link_type="a" class="mb-5" path="/?page" current_page={1} total_pages={10} />

<p class="text-xl mb-1 mt-10">progress</p>
<.progress color="primary" value={10} max={100} class="max-w-full" />
```

サンプルページのルーターを追加

```diff_elixir:lib/petal_experiment_web/router.ex
defmodule PetalExperimentWeb.Router 
  # 〜省略〜
  scope "/", PetalExperimentWeb do
    pipe_through :browser

-   get "/", PageController, :home
+   live "/", PetalLive.Index, :index
+   # get "/", PageController, :home
  end  
  # 〜省略〜
end
```

# 実行
```
$ mix phx.server
```

`http://localhost:4000` にアクセス

目標での画面が表示します

# ソース(github)
https://github.com/ymn-t-yamanashi/petal_experiment