Header

Headerは、Webサイトやアプリケーションのページ上部に表示されるコンポーネントです。
ナビゲーションやメニューなどの主要な要素を表示します。

header/default is coming soon.

プロパティ

Headerは、以下のプロパティをサポートしています。

名前 デフォルト値 説明
desktopMenus GlobalNavigationItem[] [] PC用のメニューとして表示する値の配列です。
mobileMenus MenuItem[] [] SP用のメニューとして表示する値の配列です。
currentIndex number 現在選択されているメニューを示すハイライト用の数値です。
profileImgUrl string '' プロフィール画像のURLです。
placeholder string '' 検索窓のプレースホルダーです。

インストールの手順

以下のコンポーネントのコードを、使いたいプロジェクトにコピー&ペーストします。
パスは実際のプロジェクトの構成にあわせて更新します。

modules/Header.svelte
        <!--
@component
## 概要
- Header は、検索・ナビゲーション・プロフィール画像をひとまとめにしたレスポンシブヘッダーです。
PCではグローバルナビゲーション、SPではハンバーガー+ドロワーでメニューを表示し、渡した配列から項目を生成します。生成された項目(メニュー)ごとに追加でサブメニューを表示することができます。PCではスニペットを渡し、SPではmobileMenusのoptionsに値を書くことで、任意のサブメニューを表示可能です。

## 機能
- 渡した配列をそのままメニューとしてレンダリング
- 検索窓を表示
- プロフィール画像を表示。

## Props
- desktopMenus: PC時のメニュー。グローバルナビゲーションコンポーネントに渡す配列。省略可能で、省略した場合は項目が表示されない。
- mobileMenus: SP時(Drawer内)のメニュー。メニューコンポーネントに渡す配列。省略可能で、省略した場合は項目が表示されない。
- currentIndex: 現在表示中のページの位置を示すハイライト。省略可能で、省略した場合はハイライトが表示されない。
- profileImgUrl: 右上に表示するプロフィール画像URL。省略可能で、省略した場合はデフォルトの灰色アイコンになる。
- placeholder: 検索窓のプレースホルダー。省略可能で、省略した場合はプレースホルダーが表示されない。
- class: ヘッダー全体に付与する追加クラス。省略可能。
- desktopMenusChildren: PC時のサブメニュー用のスニペット。省略可能で、省略した場合はサブメニューが表示されない。

## Usage
```svelte
サブメニューがない場合
<Header {desktopMenus} {mobileMenus} {currentIndex} {profileImgUrl} {placeholder} />

サブメニューがある場合
<Header {desktopMenus} {mobileMenus} {currentIndex} {profileImgUrl} {placeholder}>
  {#snippet desktopMenusChildren(item)}
    {#if item.id === 'menu1'}
      PCサブメニュー1の要素が入ります。
    {/if}
    {#if item.id === 'menu2'}
      PCサブメニュー2の要素が入ります。
    {/if}
  {/snippet}
</Header>
```
-->

<script module lang="ts">
  import type { MenuItem } from '$lib/components/ui/atoms/Menu.svelte';
  import type { GlobalNavigationItem } from '$lib/components/ui/modules/GlobalNavigation.svelte';
  import type { Snippet } from 'svelte';
  import type { ClassValue } from 'svelte/elements';

  export interface HeaderProps {
    /** メニューとして表示する値の配列(PC用)。グローバルナビゲーションコンポーネント用 */
    desktopMenus?: GlobalNavigationItem[];
    /** メニューとして表示する値の配列(SP用)。メニューコンポーネント用 */
    mobileMenus?: MenuItem[];
    /** 現在選択されているメニューを示すハイライト用の数値 */
    currentIndex?: number;
    /** プロフィール画像のURL */
    profileImgUrl?: string;
    /** 検索窓のプレースホルダー */
    placeholder?: string;
    /** 追加クラス */
    class?: ClassValue;
    /** PC用のサブメニュー */
    desktopMenusChildren?: Snippet<[GlobalNavigationItem, number]>;
  }
</script>

<script lang="ts">
  import Button from '$lib/components/ui/atoms/Button.svelte';
  import Input from '$lib/components/ui/atoms/Input.svelte';
  import Menu from '$lib/components/ui/atoms/Menu.svelte';
  import Drawer from '$lib/components/ui/modules/Drawer.svelte';
  import GlobalNavigation from '$lib/components/ui/modules/GlobalNavigation.svelte';
  import { Menu as Hamburger, Search, UserRound, X } from '@lucide/svelte';

  let { desktopMenus = [], mobileMenus = [], currentIndex, profileImgUrl = '', placeholder = '', class: className, desktopMenusChildren }: HeaderProps = $props();

  /** レスポンシブ切り替えの境界値(この値を下回るとモバイル表示、以上でデスクトップ表示) */
  const RESPONSIVE_BREAKPOINT_PX = 768;

  let searchText = $state('');

  let isOpen = $state(false);

  let viewportWidth = $state<number>(0);

  $effect(() => {
    // SPからPCの幅に切り替わった場合、ドロワーを閉じる。
    if (viewportWidth >= RESPONSIVE_BREAKPOINT_PX) {
      isOpen = false;
    }
  });

  /**
   * ドロワーの開閉状態をトグルする。
   * @param e クリックイベント(バブリングを止める)
   */
  function openDrawer(e: MouseEvent): void {
    e.stopPropagation();
    isOpen = !isOpen;
  }
</script>

<header class={[className, 'relative flex items-center justify-between w-full h-15 px-6 z-50 after:absolute after:bottom-0 after:inset-x-0 after:h-px after:bg-base-stroke-default after:content-[\'\'] after:-z-10 max-md:py-3 max-md:px-4 max-md:h-16']} bind:clientWidth={viewportWidth}>
  <!-- メニュー(PC用) -->
  <div class="hidden md:block">
    <GlobalNavigation menus={desktopMenus} {currentIndex}>
      {#snippet children(item, index)}
        {@render desktopMenusChildren?.(item, index)}
      {/snippet}
    </GlobalNavigation>
  </div>

  {#if mobileMenus?.length}
    <!-- ハンバーガーアイコン(SP) -->
    <div class="mr-4 md:hidden">
      <Button class="border border-base-stroke-default" type="button" variant="secondary" size="small" tone="solid" isSquare onclick={openDrawer}>
        <Hamburger size="1.125rem" />
      </Button>
    </div>

    <!-- ハンバーガーアイコンのクリック時に開くドロワー(SP) -->
    <Drawer class="md:hidden" direction="left" bind:open={isOpen}>
      {#snippet children()}
        <div class="relative flex flex-col justify-between h-full px-4 pt-14">
          <Button class="absolute top-2 right-2" variant="secondary" size="small" tone="ghost" isSquare onclick={() => (isOpen = false)}><X size="1rem" /></Button>
          <!-- メニュー(SP用) -->
          <div class="h-full overflow-y-auto">
            {#each mobileMenus as menu, index}
              <Menu class="w-auto" item={menu} current={currentIndex === index ? menu.id : ''} />
            {/each}
          </div>
        </div>
      {/snippet}
    </Drawer>
  {/if}

  <div class="flex items-center shrink-0 gap-4 max-md:shrink max-md:w-full">
    <!-- 検索窓 -->
    <div class="w-75 max-md:w-full">
      <Input type="text" {placeholder} bind:value={searchText}>
        {#snippet startContent()}
          <Search class="text-base-foreground-muted pointer-events-none" size="1rem" />
        {/snippet}
      </Input>
    </div>

    <!-- ユーザー画像 -->
    <div class="shrink-0 size-10">
      {#if profileImgUrl}
        <img class="size-full rounded-full object-cover" src={profileImgUrl} alt="プロフィール画像" />
      {:else}
        <div class="flex items-center justify-center size-10 border border-base-stroke-default rounded-full">
          <UserRound class="size-3 object-contain text-base-foreground-subtle" />
        </div>
      {/if}
    </div>
  </div>
</header>

      

依存コンポーネント

Headerを使うときは、以下のコンポーネントもダウンロードが必要です。

使い方

header/default is coming soon.

サンプル

Default

特に操作が行われていない、デフォルトの状態です。

header/default is coming soon.