Files
govoplan-dashboard/webui/src/features/dashboard/WidgetLibrary.tsx
T

101 lines
3.0 KiB
TypeScript

import type { DragEvent as ReactDragEvent } from "react";
import { GripVertical, Plus, RotateCcw, Search } from "lucide-react";
import {
IconButton,
type DashboardWidgetContribution
} from "@govoplan/core-webui";
import type { DashboardDragItem } from "./dashboardEditorTypes";
type WidgetLibraryProps = {
widgets: DashboardWidgetContribution[];
atCapacity: boolean;
query: string;
onQueryChange: (query: string) => void;
onReset: () => void;
onAdd: (widget: DashboardWidgetContribution) => void;
onDragStart: (
event: ReactDragEvent,
item: DashboardDragItem
) => void;
onDragEnd: () => void;
};
export default function WidgetLibrary({
widgets,
atCapacity,
query,
onQueryChange,
onReset,
onAdd,
onDragStart,
onDragEnd
}: WidgetLibraryProps) {
return (
<aside className="dashboard-widget-library" aria-label="Widget library">
<div className="dashboard-widget-library-controls">
<div className="dashboard-widget-library-header">
<h2>Widget library</h2>
</div>
<label className="dashboard-widget-search">
<Search size={16} aria-hidden="true" />
<input
type="search"
value={query}
placeholder="Search widgets"
onChange={(event) => onQueryChange(event.target.value)}
/>
</label>
</div>
<div className="dashboard-widget-library-list">
{widgets.map((widget) => (
<div
key={widget.id}
className={`dashboard-widget-library-item${atCapacity ? " is-disabled" : ""}`}
draggable={!atCapacity}
onDragStart={(event) =>
onDragStart(event, { kind: "catalogue", widgetId: widget.id })
}
onDragEnd={onDragEnd}
>
<GripVertical size={16} aria-hidden="true" />
<div>
<strong>{widget.title}</strong>
<span>{widget.description ?? widget.category ?? widget.moduleId}</span>
</div>
<IconButton
label={`Add ${widget.title}`}
icon={<Plus size={16} />}
variant="ghost"
disabled={atCapacity}
disabledReason={
atCapacity
? "A Dashboard can contain at most 100 widgets."
: undefined
}
onClick={() => onAdd(widget)}
/>
</div>
))}
{widgets.length === 0 && !atCapacity && (
<p className="muted dashboard-widget-library-empty">
{query.trim()
? "No available widgets match the search."
: "All available widgets are already on this Dashboard."}
</p>
)}
{atCapacity && (
<p className="muted dashboard-widget-library-empty">
This Dashboard has reached the 100-widget limit.
</p>
)}
</div>
<IconButton
label="Reset to module defaults"
icon={<RotateCcw size={16} />}
variant="ghost"
onClick={onReset}
/>
</aside>
);
}