"use client"; import React, { useCallback, useState } from "react"; import { LayoutList, Table2, LayoutGrid, Kanban, Settings2 } from "lucide-react"; import { iconSize } from "../../icons/Icon"; import { SearchBar } from "../../components/SearchBar"; import { Popover } from "../../components/Popover"; import { ToggleButtonGroup } from "../../components/ToggleButtonGroup"; import type { ToggleButtonOption } from "../../components/ToggleButtonGroup"; import { Typography } from "../../components/Typography"; import { Select, SelectItem } from "../../components/Select"; import { IconButton } from "../../components/IconButton"; import { cls } from "../../util"; import type { CollectionViewMode, CollectionViewSize, KanbanPropertyOption } from "./CollectionViewTypes"; /** * Props for the {@link CollectionViewToolbar} component. * @group Collection View */ export type CollectionViewToolbarProps = { viewMode: CollectionViewMode; onViewModeChange?: (mode: CollectionViewMode) => void; enabledViews?: CollectionViewMode[]; /** * Custom view modes, supplying the label and icon for their switcher * entries. Without these an `enabledViews` key outside the four built-ins * has no label and no icon to draw. */ customViews?: { key: string, name: string, icon?: React.ReactNode }[]; size?: CollectionViewSize; onSizeChange?: (size: CollectionViewSize) => void; searchString?: string; onSearchChange?: (search?: string) => void; loading?: boolean; actionsStart?: React.ReactNode; actionsEnd?: React.ReactNode; compact?: boolean; kanbanPropertyOptions?: KanbanPropertyOption[]; selectedKanbanProperty?: string; onKanbanPropertyChange?: (property: string) => void; }; const VIEW_MODE_ICONS: Record = { list: , table: , cards: , kanban: , }; const VIEW_MODE_LABELS: Record = { list: "List", table: "Table", cards: "Cards", kanban: "Board", }; const SIZE_LABELS: Record = { xs: "Extra Small", s: "Small", m: "Medium", l: "Large", xl: "Extra Large", }; /** * Toolbar for the headless CollectionView. * * Renders a search bar, view mode toggle (popover with toggle button group), * optional size selector, optional kanban property selector, and action slots. * * @group Collection View */ export function CollectionViewToolbar({ viewMode, onViewModeChange, enabledViews = ["list", "table", "cards", "kanban"], customViews, size = "m", onSizeChange, searchString, onSearchChange, loading, actionsStart, actionsEnd, compact, kanbanPropertyOptions, selectedKanbanProperty, onKanbanPropertyChange, }: CollectionViewToolbarProps) { const [popoverOpen, setPopoverOpen] = useState(false); // A key outside the four built-ins takes its label and icon from the // custom view that declared it; without one it falls back to the key // itself and the list glyph, so an entry is never blank. const viewOptions: ToggleButtonOption[] = enabledViews.map((mode) => { const custom = customViews?.find((entry) => entry.key === mode); return { value: mode, label: VIEW_MODE_LABELS[mode] ?? custom?.name ?? mode, icon: VIEW_MODE_ICONS[mode] ?? custom?.icon ?? , }; }); const handleViewModeChange = useCallback( (mode: CollectionViewMode) => { onViewModeChange?.(mode); }, [onViewModeChange] ); const handleSizeChange = useCallback( (value: string) => { onSizeChange?.(value as CollectionViewSize); }, [onSizeChange] ); const handleKanbanPropertyChange = useCallback( (value: string) => { onKanbanPropertyChange?.(value); }, [onKanbanPropertyChange] ); const showViewToggle = onViewModeChange && enabledViews.length > 1; const showSizeSelector = onSizeChange && viewMode !== "kanban"; const showKanbanSelector = kanbanPropertyOptions && kanbanPropertyOptions.length > 0 && viewMode === "kanban" && onKanbanPropertyChange; const showPopover = showViewToggle || showSizeSelector || showKanbanSelector; return (
{actionsStart} {onSearchChange && ( )}
{actionsEnd} {showPopover && ( setPopoverOpen((prev) => !prev)} > } >
{showViewToggle && (
View value={viewMode} onValueChange={handleViewModeChange} options={viewOptions} />
)} {showSizeSelector && (
Size
)} {showKanbanSelector && (
Group by
)}
)}
); }