サイドバー項目SidebarItemExperimental

サイドバー内で使う選択行です。アイコン、ラベル、件数、階層、開閉、削除操作を同じ行幅で揃えて表示します。

プレビュー

ライブラリ

フォルダ

状態とバリエーション

フラットな項目

階層を持たないナビゲーションでは、先頭の開閉スペースを消して左端を揃えます。

行アクション付き

削除などの行アクションは右端の予約枠に置き、件数の位置を揃えます。

ライブラリ

フォルダ

プロパティ

表は横にスクロールできます
プロパティ初期値説明
idstring-項目を識別する ID。
labelstring-行に表示するラベル。
iconReactNode-ラベルの前に表示するアイコン。
countnumber-右端に表示する件数。
isActiveboolean-現在選択中の項目かどうか。
collapsedboolean-アイコンのみの行にします。既定では最も近い SidebarProvider の折りたたみ状態に追従するため、通常は指定不要です。畳んだ行はラベルをツールチップで出します。
levelnumber0階層の深さ。子要素のインデントに使います。
hasChildrenbooleanfalse開閉できる子要素を持つことを示します。
isExpandedbooleanfalse子要素が開いている状態を示します。
reserveChevronSpacebooleantrue階層表示のための開閉スペースを予約します。
onClick() => void-行を選択した時に呼ばれます。
onToggleExpand(event: MouseEvent) => void-開閉アイコンまたは子を持つ行を操作した時に呼ばれます。
onDelete(event: MouseEvent) => void-右端の削除ボタンを押した時に呼ばれます。
deleteLabelReactNode"Delete"削除ボタンのツールチップと aria-label。
dragOverId / dragActionstring | null-ドラッグ中の並び替え・ネスト位置を行上に表示します。

使い方

import * as React from "react"
import { SidebarItem, TooltipButton } from "@gunjo/ui"
import { IconChevronRight as ChevronRight, IconFileText as FileText, IconFolder as Folder, IconGridDots as Grid, IconPlus as Plus, IconStar as Star, IconTrash as Trash2 } from "@tabler/icons-react"

const libraryItems = [
  { id: "all", label: "すべての素材", icon: Grid, count: 128 },
  { id: "favorite", label: "お気に入り", icon: Star, count: 24 },
  { id: "uncategorized", label: "未分類", icon: Folder, count: 8 },
  { id: "trash", label: "ごみ箱", icon: Trash2, count: 3 },
]

const folderItems = [
  { id: "campaign", label: "キャンペーン", icon: Folder, count: 18, level: 0, hasChildren: true },
  { id: "spring", label: "春の公開素材", icon: FileText, count: 6, level: 1, hasChildren: false },
  { id: "product", label: "商品写真", icon: Folder, count: 42, level: 0, hasChildren: false },
]

export function MediaLibrarySidebarContent() {
  const [activeId, setActiveId] = React.useState("favorite")
  const [foldersExpanded, setFoldersExpanded] = React.useState(true)
  const [campaignExpanded, setCampaignExpanded] = React.useState(true)

  return (
    <div className="w-full max-w-sm rounded-md border bg-muted/30 p-3">
      <section className="space-y-1">
        <p className="px-2 text-xs font-bold uppercase tracking-wide text-muted-foreground">ライブラリ</p>
        {libraryItems.map((item) => {
          const Icon = item.icon

          return (
            <SidebarItem
              key={item.id}
              id={item.id}
              icon={<Icon size={18} />}
              label={item.label}
              count={item.count}
              isActive={activeId === item.id}
              onClick={() => setActiveId(item.id)}
              reserveChevronSpace={false}
            />
          )
        })}
      </section>

      <section className="mt-4 space-y-1">
        <div className="flex items-center justify-between pl-2">
          <p className="text-xs font-bold uppercase tracking-wide text-muted-foreground">フォルダ</p>
          <div className="grid w-14 grid-cols-[1.5rem_1.5rem] items-center justify-end">
            <TooltipButton type="button" variant="ghost" size="icon" className="h-6 w-6 text-muted-foreground" tooltip="フォルダを追加" aria-label="フォルダを追加">
              <Plus size={14} />
            </TooltipButton>
            <TooltipButton
              type="button"
              variant="ghost"
              size="icon"
              className="h-6 w-6 text-muted-foreground"
              tooltip={foldersExpanded ? "フォルダを閉じる" : "フォルダを開く"}
              aria-label={foldersExpanded ? "フォルダを閉じる" : "フォルダを開く"}
              onClick={() => setFoldersExpanded((open) => !open)}
            >
              <ChevronRight size={14} className={foldersExpanded ? "rotate-90 transition-transform" : "transition-transform"} />
            </TooltipButton>
          </div>
        </div>

        {foldersExpanded ? (
          <div className="space-y-1">
            {folderItems.map((item) => {
              if (item.id === "spring" && !campaignExpanded) return null
              const Icon = item.icon

              return (
                <SidebarItem
                  key={item.id}
                  id={item.id}
                  icon={<Icon size={18} />}
                  label={item.label}
                  count={item.count}
                  level={item.level ?? 0}
                  hasChildren={item.hasChildren}
                  isExpanded={campaignExpanded}
                  isActive={activeId === item.id}
                  onClick={() => setActiveId(item.id)}
                  onToggleExpand={(event) => {
                    event.stopPropagation()
                    setCampaignExpanded((open) => !open)
                  }}
                />
              )
            })}
          </div>
        ) : null}
      </section>
    </div>
  )
}

使用コンポーネント