【SvelteKit + Flowbite-svelte + svelte-icons】よくあるパスワードフォームを実装する

# はじめに

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/8372eb05-f54b-d860-b243-c9e0c655af67.png" width="300"/>

**[SvelteKit](https://kit.svelte.jp/)**  + **[Flowbite-svelte](https://flowbite-svelte.com/)**
でよくあるパスワードフォームの実装をしました。
よく使うと思うので、記事にしておき、使うときにコピペしようと思いますw

アイコンには **[svelte-icons](https://svelte-icons.vercel.app/)** を使っています。


## 完成イメージ

![スクリーンショット 2023-02-18 23.15.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/138ab662-9fd3-e12b-1832-93746382c2ab.png)

* 目のマークをクリックすると、パスワードが表示される

![スクリーンショット 2023-02-18 23.15.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/d22827e5-a017-b006-96c4-ff745eee9d6b.png)


# [Flowbite-svelte](https://flowbite-svelte.com/)導入

1. `svelte-add`で`tailwindcss`のインストール

    ```bash
    // npm
    $ npx svelte-add@latest tailwindcss
    
    // yarn
    $ yarn -s run svelte-add@latest tailwindcss
    
    // pnpm
    $ pnpm dlx svelte-add@latest tailwindcss
    ```

1. 依存関係含めインストール

    ```bash
    // npm
    $ npm i -D flowbite flowbite-svelte classnames @popperjs/core
    
    // yarn
    $ yarn add flowbite flowbite-svelte classnames @popperjs/core
    
    // pnpm
    $ pnpm i -D flowbite flowbite-svelte classnames @popperjs/core
    ```

# [svelte-icons](https://svelte-icons.vercel.app/)導入

```
// npm
$ npm i -D svelte-icons

// yarn
$ yarn add svelte-icons

// pnpm
$ pnpm i -D svelte-icons
```

# 実装

## 先にソースコードから

```vue:+page.svelte
<script lang="ts">
  import { Heading, Input, Label } from "flowbite-svelte";
  import FaRegEyeSlash from 'svelte-icons/fa/FaRegEyeSlash.svelte'
  import FaRegEye from 'svelte-icons/fa/FaRegEye.svelte'
  let email = ''
  let password = ''
  let passwordFormType: 'password' | 'text' = 'password'
  let viewPassword = false

  const clickHandler = (): void => {
    viewPassword = !viewPassword
    if (viewPassword) passwordFormType = 'text'
    else passwordFormType = 'password'
  }
</script>

<div class="h-full w-4/5 mx-auto mt-10 p-5 border bg-gray-100 rounded-lg">
  <form action="login">
    <div class="text-center">
      <Heading tag="h3">SignIn</Heading>
    </div>
    <Label for="email">email</Label>
    <Input
      id="email"
      bind:value={email}
      type="email"
    />

    <Label for="password">password</Label>
    <Input
      id="password"
      bind:value={password}
      type={passwordFormType}
    >
      <button type="button" class="h-1/2" slot="right" on:click={clickHandler}>
        {#if viewPassword}
          <FaRegEye />
          {:else}
          <FaRegEyeSlash />
        {/if}
      </button>
    </Input>
  </form>
</div>
```

## 目のアイコンの表示

`<Input></Input>`コンポーネントの`slot="right"`にボタンを入れています。

```vue
<Input
  id="password"
  bind:value={password}
  type={passwordFormType}
>
  <button type="button" class="h-1/2" slot="right" on:click={clickHandler}>
    {#if viewPassword}
      <FaRegEye />
    {:else}
      <FaRegEyeSlash />
    {/if}
  </button>
</Input>
```

これによりフォームの右側に目のアイコンを表示させることができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/9b67b524-c3ad-42d2-a086-f89ac3e17ab0.png" width="100"/>

## パスワード表示 / 非表示

1. パスワードフォームの`type`を変数に持っておきます。
    * 初期値: 'password'
    * 例のソースコードでは`passwordFormType`
1. アイコンの切り替え用にflagも定義しておきます。
1. 先ほど表示させたボタンの`on:click`属性で1.2.で定めた値を書き換えます

```vue:一部抜粋
<script lang="ts">
  let password = ""
  let passwordFormType: "password" | "text" = "password"
  const clickHandler = () => {
    viewPassword = !viewPassword
    if (viewPassword) passwordFormType = 'text'
    else passwordFormType = 'password'
  }
</script>

<Input type={passwordFormType} bind:value={password} >
    <button
        type="button"
        class="h-1/2"
        slot="right"
        on:click={clickHandler}>
```

これでフォーム横のアイコンをクリックしたときに
パスワードフォームの`type`が切り替わり、表示非表示を切り替えることができるでしょう