101 lines
3.0 KiB
TypeScript
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>
|
|
);
|
|
}
|