SvelteKitでパンくずリストを実装した話

# はじめに

`SvelteKit`を用いてWebアプリケーションを実装した際、
UIコンポーネントとして[Flowbite-svelte](https://flowbite-svelte.com/)を使いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/2ff80bb8-8b46-7e9c-ce2e-233058a89df2.png)

`Svelte`の`$app/stores`モジュールを使って実装したのでその時のメモ。

## 前提

* `SvelteKit`のアプリケーションが動くこと。
* `Flowbite-svelte`を導入していること。

↑に関しては前の記事で解説しています。

https://qiita.com/doyaseizin/items/6e7e8571cc6c3d7ad2e8

## 実装

### Breadcrumb.svelte
```vue
<script lang="ts">
  import { Breadcrumb, BreadcrumbItem } from 'flowbite-svelte';
  import { page }  from '$app/stores';
  import { pathname } from './pathname';

  interface Paths {
    route: string;
    name: string;
  }

  $: paths = createBreadcrumb($page.url.pathname);
  
  const createBreadcrumb = (routes) => {
    let swap_paths: Array<Paths> = [];
      routes.split('/').forEach((route) => {
      swap_paths.push({route: route, name: pathname[route]})
    })

    return swap_paths = Array.from(new Map(swap_paths.map(o => [o.route, o])).values());
  }

</script>

<Breadcrumb>
  {#each paths as { route, name }, index}
    {#if paths.length == 1 }
      <BreadcrumbItem home>{name}</BreadcrumbItem>
    {:else if index == 0 }
      <BreadcrumbItem href="/" home>{name}</BreadcrumbItem>
    {:else if index == paths.length - 1 }
      <BreadcrumbItem>{name}</BreadcrumbItem>
    {:else}
      <BreadcrumbItem href="/{route}">{name}</BreadcrumbItem>
    {/if}
  {/each}
</Breadcrumb>
```

### pathname.ts

```typescript
export const pathname = {
  '': 'Home',
  'list': '一覧',
  'new': '新規作成'
}
```

## 解説

### ① `$app/stores`モジュールの`page`について

ソースコードの解説の前に`$app/stores`モジュールの`page`について解説します。


こちらのモジュールには、ページ情報が保存されています。
よく使うものを抜粋し、解説していきます。

#### error

エラー時のメッセージが格納されています。
`status`と組み合わせてエラーページを自作できそうです。

例) 404の場合

```typescript
error: { message: 'Not Found' }
```

#### params

パスパラメータが格納されています。
記事ごとのページを表示したりするのに使いそうです。

例) /user/[id] ⇒ /user/1 の場合
```typescript
params: { id: 1 }
```

#### status

レスポンスステータスコードが格納されています。
先ほどの`error`と組み合わせてエラーページを作ったり、状態に合わせて表示物を変えたりできそうです。

例) 200や404の場合

```typescript
// 200
status: 200

// 404 Not Found
status: 404
```

#### url

URL情報が載っています

例) http://localhost:8080の場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/685a9510-4179-a4af-3bb9-090e43246344.png)

### ② ソースコード解説

Flowbite-svelteのパンくずリストは、
クリック時の遷移先と、表示テキストが必要でした

```vue
  <BreadcrumbItem href="/" home>{name}</BreadcrumbItem>
```

そのため、遷移先から表示テキストが参照できるようなHashオブジェクトを作成した。
それが`pathname.ts`です。

```typescript
// pathname.ts
export const pathname = {
  '': 'Home',
  'list': '一覧',
  'new': '新規作成'
}

pathname['list']
// ⇒ '一覧'
```

次に、与えられたパスから、
パンくずリストで使う形式の配列を作成するメソッドを作成しました。

```typescript
const createBreadcrumb = (routes) => {
let swap_paths: Array<Paths> = [];
  routes.split('/').forEach((route) => {
  swap_paths.push({route: route, name: pathname[route]})
})

return swap_paths = Array.from(new Map(swap_paths.map(o => [o.route, o])).values());
}

createBreadcrumb('/list') 
// ⇒ [{route: '', name: 'Home'}, {route: 'list', name: '一覧'}]
```

そしてそのメソッドを、パスの変化に合わせてパンくずリストの配列に代入しています。

```typescript
$: paths = createBreadcrumb($page.url.pathname);
```

あとはFlowbite-svelteのパンくずリストコンポーネントに合わせて、eachで回せばOK
(Homeは家のアイコンが出るように等工夫しています。)

```vue
<Breadcrumb>
  {#each paths as { route, name }, index}
    {#if paths.length == 1 }
      <BreadcrumbItem home>{name}</BreadcrumbItem>
    {:else if index == 0 }
      <BreadcrumbItem href="/" home>{name}</BreadcrumbItem>
    {:else if index == paths.length - 1 }
      <BreadcrumbItem>{name}</BreadcrumbItem>
    {:else}
      <BreadcrumbItem href="/{route}">{name}</BreadcrumbItem>
    {/if}
  {/each}
</Breadcrumb>
```

終わりです👍

![breadcrumb.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/11ce1837-b00e-26d7-1883-bb5c0a017c0b.gif)