niva-ui
Version:
A beautiful, minimalistic React + Tailwind UI framework for SaaS applications
2,404 lines • 96.1 kB
JavaScript
"use client";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
Alert: () => Alert,
AlertDescription: () => AlertDescription,
AlertIcon: () => AlertIcon,
AlertTitle: () => AlertTitle,
Avatar: () => Avatar,
Badge: () => Badge,
Button: () => Button,
Card: () => Card,
CardContent: () => CardContent,
CardDescription: () => CardDescription,
CardFooter: () => CardFooter,
CardHeader: () => CardHeader,
CardTitle: () => CardTitle,
Chip: () => Chip,
ChipGroup: () => ChipGroup,
CodeBlock: () => CodeBlock,
DataTable: () => DataTable,
DropdownMenu: () => DropdownMenu,
DropdownMenuCheckboxItem: () => DropdownMenuCheckboxItem,
DropdownMenuContent: () => DropdownMenuContent,
DropdownMenuGroup: () => DropdownMenuGroup,
DropdownMenuItem: () => DropdownMenuItem,
DropdownMenuLabel: () => DropdownMenuLabel,
DropdownMenuPortal: () => DropdownMenuPortal,
DropdownMenuRadioGroup: () => DropdownMenuRadioGroup,
DropdownMenuRadioItem: () => DropdownMenuRadioItem,
DropdownMenuSeparator: () => DropdownMenuSeparator,
DropdownMenuShortcut: () => DropdownMenuShortcut,
DropdownMenuSub: () => DropdownMenuSub,
DropdownMenuSubContent: () => DropdownMenuSubContent,
DropdownMenuSubTrigger: () => DropdownMenuSubTrigger,
DropdownMenuTrigger: () => DropdownMenuTrigger,
Form: () => Form,
FormField: () => FormField,
FormSubmit: () => FormSubmit,
Input: () => Input,
Loader: () => Loader,
MetricCard: () => MetricCard,
Modal: () => Modal,
ModalContent: () => ModalContent,
ModalDescription: () => ModalDescription,
ModalFooter: () => ModalFooter,
ModalHeader: () => ModalHeader,
ModalTitle: () => ModalTitle,
Navbar: () => Navbar,
PageLoader: () => PageLoader,
Pagination: () => Pagination,
PaginationInfo: () => PaginationInfo,
SearchBar: () => SearchBar,
Select: () => Select,
Sidebar: () => Sidebar,
Skeleton: () => Skeleton,
StatCard: () => StatCard,
Table: () => Table,
TableBody: () => TableBody,
TableCaption: () => TableCaption,
TableCell: () => TableCell,
TableFooter: () => TableFooter,
TableHead: () => TableHead,
TableHeader: () => TableHeader,
TableRow: () => TableRow,
ThemeProvider: () => ThemeProvider,
Toast: () => Toast,
ToastContainer: () => ToastContainer,
ToastProvider: () => ToastProvider,
avatarVariants: () => avatarVariants,
badgeVariants: () => badgeVariants,
buttonVariants: () => buttonVariants,
cn: () => cn,
formVariants: () => formVariants,
inputVariants: () => inputVariants,
modalVariants: () => modalVariants,
navbarVariants: () => navbarVariants,
paginationVariants: () => paginationVariants,
searchBarVariants: () => searchBarVariants,
selectVariants: () => selectVariants,
sidebarVariants: () => sidebarVariants,
statCardVariants: () => statCardVariants,
toast: () => toast,
toastVariants: () => toastVariants,
useConfirmModal: () => useConfirmModal,
useFormContext: () => useFormContext,
useSidebar: () => useSidebar,
useToast: () => useToast
});
module.exports = __toCommonJS(index_exports);
// src/components/ui/button.tsx
var React2 = __toESM(require("react"));
var import_react_slot = require("@radix-ui/react-slot");
var import_class_variance_authority = require("class-variance-authority");
// src/lib/utils.ts
var import_clsx = require("clsx");
var import_tailwind_merge = require("tailwind-merge");
function cn(...inputs) {
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
}
// src/components/ui/button.tsx
var buttonVariants = (0, import_class_variance_authority.cva)(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline"
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
);
var Button = React2.forwardRef(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? import_react_slot.Slot : "button";
return /* @__PURE__ */ React2.createElement(
Comp,
{
className: cn(buttonVariants({ variant, size, className })),
ref,
...props
}
);
}
);
Button.displayName = "Button";
// src/components/ui/badge.tsx
var React3 = __toESM(require("react"));
var import_class_variance_authority2 = require("class-variance-authority");
var badgeVariants = (0, import_class_variance_authority2.cva)(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground"
}
},
defaultVariants: {
variant: "default"
}
}
);
function Badge({ className, variant, ...props }) {
return /* @__PURE__ */ React3.createElement("div", { className: cn(badgeVariants({ variant }), className), ...props });
}
// src/components/ui/card.tsx
var React4 = __toESM(require("react"));
var Card = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
"div",
{
ref,
className: cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
),
...props
}
));
Card.displayName = "Card";
var CardHeader = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
"div",
{
ref,
className: cn("flex flex-col space-y-1.5 p-6", className),
...props
}
));
CardHeader.displayName = "CardHeader";
var CardTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
"h3",
{
ref,
className: cn(
"text-2xl font-semibold leading-none tracking-tight",
className
),
...props
}
));
CardTitle.displayName = "CardTitle";
var CardDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
"p",
{
ref,
className: cn("text-sm text-muted-foreground", className),
...props
}
));
CardDescription.displayName = "CardDescription";
var CardContent = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement("div", { ref, className: cn("p-6 pt-0", className), ...props }));
CardContent.displayName = "CardContent";
var CardFooter = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React4.createElement(
"div",
{
ref,
className: cn("flex items-center p-6 pt-0", className),
...props
}
));
CardFooter.displayName = "CardFooter";
// src/components/ui/input.tsx
var React5 = __toESM(require("react"));
var import_class_variance_authority3 = require("class-variance-authority");
var inputVariants = (0, import_class_variance_authority3.cva)(
"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
{
variants: {
variant: {
default: "border-input",
destructive: "border-destructive focus-visible:ring-destructive",
ghost: "border-transparent bg-transparent hover:bg-accent"
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 px-3 text-sm",
lg: "h-11 px-4 py-3"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
);
var Input = React5.forwardRef(
({ className, variant, size, type, label, error, icon, suffix, ...props }, ref) => {
return /* @__PURE__ */ React5.createElement("div", { className: "grid w-full gap-1.5" }, label && /* @__PURE__ */ React5.createElement("label", { className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" }, label), /* @__PURE__ */ React5.createElement("div", { className: "relative" }, icon && /* @__PURE__ */ React5.createElement("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }, icon), /* @__PURE__ */ React5.createElement(
"input",
{
type,
className: cn(
inputVariants({ variant, size, className }),
icon && "pl-10",
suffix && "pr-10",
error && "border-destructive focus-visible:ring-destructive"
),
ref,
...props
}
), suffix && /* @__PURE__ */ React5.createElement("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground" }, suffix)), error && /* @__PURE__ */ React5.createElement("p", { className: "text-sm text-destructive" }, error));
}
);
Input.displayName = "Input";
// src/components/ui/select.tsx
var React6 = __toESM(require("react"));
var import_lucide_react = require("lucide-react");
var import_class_variance_authority4 = require("class-variance-authority");
var selectVariants = (0, import_class_variance_authority4.cva)(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
{
variants: {
variant: {
default: "border-input",
destructive: "border-destructive focus:ring-destructive"
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 px-3 text-sm",
lg: "h-11 px-4 py-3"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
);
var Select = React6.forwardRef(
({
className,
variant,
size,
options,
value,
onValueChange,
placeholder = "Select an option...",
label,
error,
searchable = false,
disabled,
...props
}, ref) => {
const [isOpen, setIsOpen] = React6.useState(false);
const [searchTerm, setSearchTerm] = React6.useState("");
const dropdownRef = React6.useRef(null);
const selectedOption = options.find((option) => option.value === value);
const filteredOptions = React6.useMemo(() => {
if (!searchable || !searchTerm) return options;
return options.filter(
(option) => option.label.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [options, searchTerm, searchable]);
React6.useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
setSearchTerm("");
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const handleSelect = (optionValue) => {
onValueChange?.(optionValue);
setIsOpen(false);
setSearchTerm("");
};
return /* @__PURE__ */ React6.createElement("div", { className: "grid w-full gap-1.5" }, label && /* @__PURE__ */ React6.createElement("label", { className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" }, label), /* @__PURE__ */ React6.createElement("div", { className: "relative", ref: dropdownRef }, /* @__PURE__ */ React6.createElement(
"button",
{
type: "button",
ref,
className: cn(
selectVariants({ variant, size, className }),
error && "border-destructive focus:ring-destructive"
),
onClick: () => !disabled && setIsOpen(!isOpen),
disabled,
...props
},
/* @__PURE__ */ React6.createElement("span", { className: cn(
"block truncate text-left",
!selectedOption && "text-muted-foreground"
) }, selectedOption ? selectedOption.label : placeholder),
/* @__PURE__ */ React6.createElement(import_lucide_react.ChevronDown, { className: cn(
"h-4 w-4 transition-transform",
isOpen && "rotate-180"
) })
), isOpen && /* @__PURE__ */ React6.createElement("div", { className: "absolute z-50 mt-1 w-full rounded-md border bg-popover p-1 shadow-md" }, searchable && /* @__PURE__ */ React6.createElement("div", { className: "p-1" }, /* @__PURE__ */ React6.createElement(
"input",
{
type: "text",
placeholder: "Search...",
value: searchTerm,
onChange: (e) => setSearchTerm(e.target.value),
className: "w-full rounded-sm border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
}
)), /* @__PURE__ */ React6.createElement("div", { className: "max-h-60 overflow-auto" }, filteredOptions.length === 0 ? /* @__PURE__ */ React6.createElement("div", { className: "py-6 text-center text-sm text-muted-foreground" }, "No options found.") : filteredOptions.map((option) => /* @__PURE__ */ React6.createElement(
"button",
{
key: option.value,
type: "button",
className: cn(
"relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground",
option.disabled && "cursor-not-allowed opacity-50",
value === option.value && "bg-accent text-accent-foreground"
),
onClick: () => !option.disabled && handleSelect(option.value),
disabled: option.disabled
},
/* @__PURE__ */ React6.createElement("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center" }, value === option.value && /* @__PURE__ */ React6.createElement(import_lucide_react.Check, { className: "h-4 w-4" })),
option.label
))))), error && /* @__PURE__ */ React6.createElement("p", { className: "text-sm text-destructive" }, error));
}
);
Select.displayName = "Select";
// src/components/ui/avatar.tsx
var React7 = __toESM(require("react"));
var import_class_variance_authority5 = require("class-variance-authority");
var avatarVariants = (0, import_class_variance_authority5.cva)(
"relative flex shrink-0 overflow-hidden rounded-full",
{
variants: {
size: {
sm: "h-6 w-6",
default: "h-10 w-10",
lg: "h-12 w-12",
xl: "h-16 w-16",
"2xl": "h-20 w-20"
}
},
defaultVariants: {
size: "default"
}
}
);
var avatarImageVariants = (0, import_class_variance_authority5.cva)("aspect-square h-full w-full object-cover");
var avatarFallbackVariants = (0, import_class_variance_authority5.cva)(
"flex h-full w-full items-center justify-center rounded-full bg-muted text-muted-foreground font-medium",
{
variants: {
size: {
sm: "text-xs",
default: "text-sm",
lg: "text-base",
xl: "text-lg",
"2xl": "text-xl"
}
},
defaultVariants: {
size: "default"
}
}
);
var Avatar = React7.forwardRef(
({ className, size, src, alt, fallback, status, ...props }, ref) => {
const [imageLoaded, setImageLoaded] = React7.useState(false);
const [imageFailed, setImageFailed] = React7.useState(false);
const initials = React7.useMemo(() => {
if (fallback) return fallback;
if (alt) {
const names = alt.split(" ");
return names.length > 1 ? `${names[0][0]}${names[names.length - 1][0]}`.toUpperCase() : names[0].slice(0, 2).toUpperCase();
}
return "??";
}, [fallback, alt]);
return /* @__PURE__ */ React7.createElement("div", { className: cn(avatarVariants({ size }), className), ref, ...props }, src && !imageFailed && /* @__PURE__ */ React7.createElement(
"img",
{
src,
alt,
className: cn(avatarImageVariants()),
onLoad: () => setImageLoaded(true),
onError: () => setImageFailed(true)
}
), (!src || imageFailed || !imageLoaded) && /* @__PURE__ */ React7.createElement("div", { className: cn(avatarFallbackVariants({ size })) }, initials), status && /* @__PURE__ */ React7.createElement("div", { className: cn(
"absolute -bottom-0 -right-0 rounded-full border-2 border-background",
size === "sm" && "h-2 w-2",
size === "default" && "h-3 w-3",
size === "lg" && "h-3.5 w-3.5",
size === "xl" && "h-4 w-4",
size === "2xl" && "h-5 w-5",
status === "online" && "bg-green-500",
status === "offline" && "bg-gray-400",
status === "away" && "bg-yellow-500",
status === "busy" && "bg-red-500"
) }));
}
);
Avatar.displayName = "Avatar";
// src/components/ui/table.tsx
var React8 = __toESM(require("react"));
var import_lucide_react2 = require("lucide-react");
var Table = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React8.createElement("div", { className: "relative w-full overflow-auto" }, /* @__PURE__ */ React8.createElement(
"table",
{
ref,
className: cn("w-full caption-bottom text-sm", className),
...props
}
)));
Table.displayName = "Table";
var TableHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React8.createElement("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
TableHeader.displayName = "TableHeader";
var TableBody = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React8.createElement(
"tbody",
{
ref,
className: cn("[&_tr:last-child]:border-0", className),
...props
}
));
TableBody.displayName = "TableBody";
var TableFooter = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React8.createElement(
"tfoot",
{
ref,
className: cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
),
...props
}
));
TableFooter.displayName = "TableFooter";
var TableRow = React8.forwardRef(({ className, hover = true, ...props }, ref) => /* @__PURE__ */ React8.createElement(
"tr",
{
ref,
className: cn(
"border-b transition-colors",
hover && "hover:bg-muted/50",
"data-[state=selected]:bg-muted",
className
),
...props
}
));
TableRow.displayName = "TableRow";
var TableHead = React8.forwardRef(
({ className, sortable, sortDirection, onSort, children, ...props }, ref) => /* @__PURE__ */ React8.createElement(
"th",
{
ref,
className: cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
sortable && "cursor-pointer hover:text-foreground",
className
),
onClick: sortable ? onSort : void 0,
...props
},
/* @__PURE__ */ React8.createElement("div", { className: "flex items-center space-x-2" }, /* @__PURE__ */ React8.createElement("span", null, children), sortable && /* @__PURE__ */ React8.createElement("span", { className: "flex items-center" }, sortDirection === "asc" && /* @__PURE__ */ React8.createElement(import_lucide_react2.ChevronUp, { className: "h-4 w-4" }), sortDirection === "desc" && /* @__PURE__ */ React8.createElement(import_lucide_react2.ChevronDown, { className: "h-4 w-4" }), sortDirection === null && /* @__PURE__ */ React8.createElement(import_lucide_react2.ChevronsUpDown, { className: "h-4 w-4" })))
)
);
TableHead.displayName = "TableHead";
var TableCell = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React8.createElement(
"td",
{
ref,
className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className),
...props
}
));
TableCell.displayName = "TableCell";
var TableCaption = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React8.createElement(
"caption",
{
ref,
className: cn("mt-4 text-sm text-muted-foreground", className),
...props
}
));
TableCaption.displayName = "TableCaption";
function DataTable({
data,
columns,
loading = false,
emptyMessage = "No data available",
onRowClick,
selectable = false,
selectedRows = [],
onSelectionChange
}) {
const [sortConfig, setSortConfig] = React8.useState({ key: null, direction: "asc" });
const sortedData = React8.useMemo(() => {
if (!sortConfig.key) return data;
return [...data].sort((a, b) => {
const aValue = a[sortConfig.key];
const bValue = b[sortConfig.key];
if (aValue < bValue) {
return sortConfig.direction === "asc" ? -1 : 1;
}
if (aValue > bValue) {
return sortConfig.direction === "asc" ? 1 : -1;
}
return 0;
});
}, [data, sortConfig]);
const handleSort = (key) => {
setSortConfig((prev) => ({
key,
direction: prev.key === key && prev.direction === "asc" ? "desc" : "asc"
}));
};
const isSelected = (row) => selectedRows.some((selected) => selected === row);
const toggleSelection = (row) => {
if (!onSelectionChange) return;
const newSelection = isSelected(row) ? selectedRows.filter((selected) => selected !== row) : [...selectedRows, row];
onSelectionChange(newSelection);
};
const toggleAllSelection = () => {
if (!onSelectionChange) return;
const allSelected = selectedRows.length === data.length;
onSelectionChange(allSelected ? [] : [...data]);
};
if (loading) {
return /* @__PURE__ */ React8.createElement("div", { className: "w-full p-8 text-center" }, /* @__PURE__ */ React8.createElement("div", { className: "text-sm text-muted-foreground" }, "Loading..."));
}
return /* @__PURE__ */ React8.createElement(Table, null, /* @__PURE__ */ React8.createElement(TableHeader, null, /* @__PURE__ */ React8.createElement(TableRow, null, selectable && /* @__PURE__ */ React8.createElement(TableHead, { className: "w-12" }, /* @__PURE__ */ React8.createElement(
"input",
{
type: "checkbox",
checked: selectedRows.length === data.length && data.length > 0,
onChange: toggleAllSelection,
className: "rounded"
}
)), columns.map((column) => /* @__PURE__ */ React8.createElement(
TableHead,
{
key: String(column.key),
sortable: column.sortable,
sortDirection: sortConfig.key === column.key ? sortConfig.direction : null,
onSort: () => column.sortable && handleSort(column.key),
style: { width: column.width }
},
column.header
)))), /* @__PURE__ */ React8.createElement(TableBody, null, sortedData.length === 0 ? /* @__PURE__ */ React8.createElement(TableRow, null, /* @__PURE__ */ React8.createElement(TableCell, { colSpan: columns.length + (selectable ? 1 : 0), className: "text-center py-8" }, /* @__PURE__ */ React8.createElement("div", { className: "text-muted-foreground" }, emptyMessage))) : sortedData.map((row, index) => /* @__PURE__ */ React8.createElement(
TableRow,
{
key: index,
className: cn(
onRowClick && "cursor-pointer",
isSelected(row) && "bg-muted/50"
),
onClick: () => onRowClick?.(row)
},
selectable && /* @__PURE__ */ React8.createElement(TableCell, null, /* @__PURE__ */ React8.createElement(
"input",
{
type: "checkbox",
checked: isSelected(row),
onChange: () => toggleSelection(row),
onClick: (e) => e.stopPropagation(),
className: "rounded"
}
)),
columns.map((column) => /* @__PURE__ */ React8.createElement(TableCell, { key: String(column.key) }, column.render ? column.render(row[column.key], row) : String(row[column.key] ?? "")))
))));
}
// src/components/ui/toast.tsx
var React9 = __toESM(require("react"));
var import_lucide_react3 = require("lucide-react");
var import_class_variance_authority6 = require("class-variance-authority");
var toastVariants = (0, import_class_variance_authority6.cva)(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background text-foreground",
success: "border-green-200 bg-green-50 text-green-900 dark:border-green-800 dark:bg-green-950 dark:text-green-50",
error: "border-red-200 bg-red-50 text-red-900 dark:border-red-800 dark:bg-red-950 dark:text-red-50",
warning: "border-yellow-200 bg-yellow-50 text-yellow-900 dark:border-yellow-800 dark:bg-yellow-950 dark:text-yellow-50",
info: "border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-50"
}
},
defaultVariants: {
variant: "default"
}
}
);
var Toast = React9.forwardRef(
({ className, variant, title, description, action, onClose, duration = 5e3, ...props }, ref) => {
const [isVisible, setIsVisible] = React9.useState(true);
React9.useEffect(() => {
if (duration && duration > 0) {
const timer = setTimeout(() => {
setIsVisible(false);
setTimeout(() => onClose?.(), 150);
}, duration);
return () => clearTimeout(timer);
}
}, [duration, onClose]);
const getIcon = () => {
switch (variant) {
case "success":
return /* @__PURE__ */ React9.createElement(import_lucide_react3.CheckCircle, { className: "h-5 w-5 text-green-600 dark:text-green-400" });
case "error":
return /* @__PURE__ */ React9.createElement(import_lucide_react3.AlertCircle, { className: "h-5 w-5 text-red-600 dark:text-red-400" });
case "warning":
return /* @__PURE__ */ React9.createElement(import_lucide_react3.AlertTriangle, { className: "h-5 w-5 text-yellow-600 dark:text-yellow-400" });
case "info":
return /* @__PURE__ */ React9.createElement(import_lucide_react3.Info, { className: "h-5 w-5 text-blue-600 dark:text-blue-400" });
default:
return null;
}
};
if (!isVisible) return null;
return /* @__PURE__ */ React9.createElement(
"div",
{
ref,
className: cn(toastVariants({ variant }), className),
...props
},
/* @__PURE__ */ React9.createElement("div", { className: "flex items-start space-x-3" }, /* @__PURE__ */ React9.createElement("div", { className: "flex-shrink-0 mt-0.5" }, getIcon()), /* @__PURE__ */ React9.createElement("div", { className: "flex-1 min-w-0" }, title && /* @__PURE__ */ React9.createElement("div", { className: "text-sm font-semibold" }, title), description && /* @__PURE__ */ React9.createElement("div", { className: "mt-1 text-sm opacity-90" }, description), action && /* @__PURE__ */ React9.createElement("div", { className: "mt-2" }, action))),
/* @__PURE__ */ React9.createElement(
"button",
{
onClick: () => {
setIsVisible(false);
setTimeout(() => onClose?.(), 150);
},
className: "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100"
},
/* @__PURE__ */ React9.createElement(import_lucide_react3.X, { className: "h-4 w-4" })
)
);
}
);
Toast.displayName = "Toast";
var ToastContainer = ({
position = "top-right"
}) => {
const positionClasses = {
"top-right": "top-0 right-0",
"top-left": "top-0 left-0",
"bottom-right": "bottom-0 right-0",
"bottom-left": "bottom-0 left-0",
"top-center": "top-0 left-1/2 -translate-x-1/2",
"bottom-center": "bottom-0 left-1/2 -translate-x-1/2"
};
return /* @__PURE__ */ React9.createElement(
"div",
{
className: cn(
"fixed z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:flex-col md:max-w-[420px]",
positionClasses[position]
),
id: "toast-container"
}
);
};
var ToastContext = React9.createContext(void 0);
function ToastProvider({ children }) {
const [toasts, setToasts] = React9.useState([]);
const toast2 = React9.useCallback((options) => {
const id = options.id || Math.random().toString(36).substring(7);
setToasts((prev) => [...prev, { ...options, id }]);
return id;
}, []);
const dismiss = React9.useCallback((id) => {
setToasts((prev) => prev.filter((toast3) => toast3.id !== id));
}, []);
const clear = React9.useCallback(() => {
setToasts([]);
}, []);
return /* @__PURE__ */ React9.createElement(ToastContext.Provider, { value: { toasts, toast: toast2, dismiss, clear } }, children, /* @__PURE__ */ React9.createElement(ToastContainer, null), toasts.map(({ id, ...toastProps }) => /* @__PURE__ */ React9.createElement(
Toast,
{
key: id,
...toastProps,
onClose: () => dismiss(id)
}
)));
}
function useToast() {
const context = React9.useContext(ToastContext);
if (!context) {
throw new Error("useToast must be used within a ToastProvider");
}
return context;
}
var toast = {
success: (message, options) => {
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("toast", {
detail: { variant: "success", description: message, ...options }
}));
}
},
error: (message, options) => {
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("toast", {
detail: { variant: "error", description: message, ...options }
}));
}
},
warning: (message, options) => {
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("toast", {
detail: { variant: "warning", description: message, ...options }
}));
}
},
info: (message, options) => {
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("toast", {
detail: { variant: "info", description: message, ...options }
}));
}
}
};
// src/components/ui/modal.tsx
var React10 = __toESM(require("react"));
var import_lucide_react4 = require("lucide-react");
var import_class_variance_authority7 = require("class-variance-authority");
var modalVariants = (0, import_class_variance_authority7.cva)(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
{
variants: {
size: {
sm: "max-w-sm",
default: "max-w-lg",
lg: "max-w-2xl",
xl: "max-w-4xl",
full: "max-w-[95vw] max-h-[95vh]"
}
},
defaultVariants: {
size: "default"
}
}
);
var Modal = React10.forwardRef(
({
className,
size,
isOpen,
onClose,
title,
description,
showCloseButton = true,
closeOnOverlayClick = true,
closeOnEscape = true,
children,
...props
}, ref) => {
const modalRef = React10.useRef(null);
React10.useEffect(() => {
if (!closeOnEscape || !isOpen) return;
const handleEscape = (e) => {
if (e.key === "Escape") {
onClose();
}
};
document.addEventListener("keydown", handleEscape);
return () => document.removeEventListener("keydown", handleEscape);
}, [isOpen, closeOnEscape, onClose]);
const handleOverlayClick = (e) => {
if (closeOnOverlayClick && e.target === e.currentTarget) {
onClose();
}
};
React10.useEffect(() => {
if (isOpen) {
const previousActiveElement = document.activeElement;
modalRef.current?.focus();
return () => {
previousActiveElement?.focus();
};
}
}, [isOpen]);
React10.useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "unset";
};
}
}, [isOpen]);
if (!isOpen) return null;
return /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
"div",
{
className: "fixed inset-0 z-40 bg-black/50 backdrop-blur-sm",
onClick: handleOverlayClick
}
), /* @__PURE__ */ React10.createElement(
"div",
{
ref: (node) => {
modalRef.current = node;
if (typeof ref === "function") {
ref(node);
} else if (ref) {
ref.current = node;
}
},
className: cn(modalVariants({ size }), className),
role: "dialog",
"aria-modal": "true",
"aria-labelledby": title ? "modal-title" : void 0,
"aria-describedby": description ? "modal-description" : void 0,
tabIndex: -1,
...props
},
showCloseButton && /* @__PURE__ */ React10.createElement(
"button",
{
onClick: onClose,
className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
},
/* @__PURE__ */ React10.createElement(import_lucide_react4.X, { className: "h-4 w-4" }),
/* @__PURE__ */ React10.createElement("span", { className: "sr-only" }, "Close")
),
(title || description) && /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col space-y-1.5" }, title && /* @__PURE__ */ React10.createElement("h2", { id: "modal-title", className: "text-lg font-semibold leading-none tracking-tight" }, title), description && /* @__PURE__ */ React10.createElement("p", { id: "modal-description", className: "text-sm text-muted-foreground" }, description)),
/* @__PURE__ */ React10.createElement("div", { className: "flex-1" }, children)
));
}
);
Modal.displayName = "Modal";
var ModalHeader = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React10.createElement(
"div",
{
ref,
className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className),
...props
}
));
ModalHeader.displayName = "ModalHeader";
var ModalTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React10.createElement(
"h3",
{
ref,
className: cn("text-lg font-semibold leading-none tracking-tight", className),
...props
}
));
ModalTitle.displayName = "ModalTitle";
var ModalDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React10.createElement(
"p",
{
ref,
className: cn("text-sm text-muted-foreground", className),
...props
}
));
ModalDescription.displayName = "ModalDescription";
var ModalContent = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React10.createElement("div", { ref, className: cn("py-4", className), ...props }));
ModalContent.displayName = "ModalContent";
var ModalFooter = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React10.createElement(
"div",
{
ref,
className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
...props
}
));
ModalFooter.displayName = "ModalFooter";
function useConfirmModal() {
const [isOpen, setIsOpen] = React10.useState(false);
const [options, setOptions] = React10.useState({});
const resolveRef = React10.useRef(null);
const confirm = React10.useCallback((opts = {}) => {
setOptions({
title: opts.title || "Are you sure?",
description: opts.description || "This action cannot be undone.",
confirmText: opts.confirmText || "Confirm",
cancelText: opts.cancelText || "Cancel",
variant: opts.variant || "default",
...opts
});
setIsOpen(true);
return new Promise((resolve) => {
resolveRef.current = resolve;
});
}, []);
const handleConfirm = () => {
resolveRef.current?.(true);
setIsOpen(false);
};
const handleCancel = () => {
resolveRef.current?.(false);
setIsOpen(false);
};
const ConfirmModal = () => /* @__PURE__ */ React10.createElement(
Modal,
{
isOpen,
onClose: handleCancel,
title: options.title,
description: options.description
},
/* @__PURE__ */ React10.createElement(ModalFooter, null, /* @__PURE__ */ React10.createElement(
"button",
{
onClick: handleCancel,
className: cn(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
"border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2"
)
},
options.cancelText
), /* @__PURE__ */ React10.createElement(
"button",
{
onClick: handleConfirm,
className: cn(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
options.variant === "destructive" ? "bg-destructive text-destructive-foreground hover:bg-destructive/90 h-10 px-4 py-2" : "bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2"
)
},
options.confirmText
))
);
return { confirm, ConfirmModal };
}
// src/components/ui/navbar.tsx
var React11 = __toESM(require("react"));
var import_lucide_react5 = require("lucide-react");
var import_class_variance_authority8 = require("class-variance-authority");
var navbarVariants = (0, import_class_variance_authority8.cva)(
"flex w-full items-center justify-between border-b bg-background px-4",
{
variants: {
size: {
default: "h-16",
sm: "h-12",
lg: "h-20"
},
variant: {
default: "border-border",
minimal: "border-transparent",
elevated: "shadow-sm border-border"
}
},
defaultVariants: {
size: "default",
variant: "default"
}
}
);
var Navbar = React11.forwardRef(
({
className,
size,
variant,
logo,
items = [],
actions,
mobileMenuButton = true,
onMobileMenuToggle,
isMobileMenuOpen = false,
...props
}, ref) => {
const [openDropdown, setOpenDropdown] = React11.useState(null);
const handleDropdownToggle = (label) => {
setOpenDropdown(openDropdown === label ? null : label);
};
const handleItemClick = (item) => {
if (item.disabled) return;
if (item.onClick) {
item.onClick();
} else if (item.href) {
window.location.href = item.href;
}
setOpenDropdown(null);
};
return /* @__PURE__ */ React11.createElement(
"nav",
{
ref,
className: cn(navbarVariants({ size, variant }), className),
...props
},
/* @__PURE__ */ React11.createElement("div", { className: "flex items-center space-x-4" }, logo),
/* @__PURE__ */ React11.createElement("div", { className: "hidden md:flex items-center space-x-1" }, items.map((item) => /* @__PURE__ */ React11.createElement("div", { key: item.label, className: "relative" }, item.items && item.items.length > 0 ? (
// Dropdown Menu
/* @__PURE__ */ React11.createElement("div", { className: "relative" }, /* @__PURE__ */ React11.createElement(
"button",
{
onClick: () => handleDropdownToggle(item.label),
className: cn(
"flex items-center space-x-1 px-3 py-2 text-sm font-medium rounded-md transition-colors",
"hover:bg-accent hover:text-accent-foreground",
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
item.disabled && "opacity-50 cursor-not-allowed",
openDropdown === item.label && "bg-accent text-accent-foreground"
),
disabled: item.disabled
},
item.icon,
/* @__PURE__ */ React11.createElement("span", null, item.label),
item.badge && /* @__PURE__ */ React11.createElement("span", { className: "ml-1 px-2 py-0.5 text-xs bg-primary text-primary-foreground rounded-full" }, item.badge),
/* @__PURE__ */ React11.createElement(import_lucide_react5.ChevronDown, { className: cn(
"h-4 w-4 transition-transform",
openDropdown === item.label && "rotate-180"
) })
), openDropdown === item.label && /* @__PURE__ */ React11.createElement("div", { className: "absolute top-full left-0 mt-1 w-48 rounded-md border bg-popover p-1 shadow-md z-50" }, item.items.map((subItem) => /* @__PURE__ */ React11.createElement(
"button",
{
key: subItem.label,
onClick: () => handleItemClick(subItem),
className: cn(
"flex w-full items-center space-x-2 px-3 py-2 text-sm rounded-sm transition-colors text-left",
"hover:bg-accent hover:text-accent-foreground",
"focus:outline-none focus:ring-2 focus:ring-ring",
subItem.disabled && "opacity-50 cursor-not-allowed"
),
disabled: subItem.disabled
},
subItem.icon,
/* @__PURE__ */ React11.createElement("span", null, subItem.label),
subItem.badge && /* @__PURE__ */ React11.createElement("span", { className: "ml-auto px-2 py-0.5 text-xs bg-primary text-primary-foreground rounded-full" }, subItem.badge)
))))
) : (
// Regular Menu Item
/* @__PURE__ */ React11.createElement(
"button",
{
onClick: () => handleItemClick(item),
className: cn(
"flex items-center space-x-2 px-3 py-2 text-sm font-medium rounded-md transition-colors",
"hover:bg-accent hover:text-accent-foreground",
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
item.disabled && "opacity-50 cursor-not-allowed"
),
disabled: item.disabled
},
item.icon,
/* @__PURE__ */ React11.createElement("span", null, item.label),
item.badge && /* @__PURE__ */ React11.createElement("span", { className: "ml-1 px-2 py-0.5 text-xs bg-primary text-primary-foreground rounded-full" }, item.badge)
)
)))),
/* @__PURE__ */ React11.createElement("div", { className: "flex items-center space-x-2" }, actions, mobileMenuButton && /* @__PURE__ */ React11.createElement(
"button",
{
onClick: onMobileMenuToggle,
className: "md:hidden p-2 rounded-md hover:bg-accent hover:text-accent-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
},
isMobileMenuOpen ? /* @__PURE__ */ React11.createElement(import_lucide_react5.X, { className: "h-5 w-5" }) : /* @__PURE__ */ React11.createElement(import_lucide_react5.Menu, { className: "h-5 w-5" })
)),
isMobileMenuOpen && /* @__PURE__ */ React11.createElement("div", { className: "absolute top-full left-0 right-0 md:hidden border-b bg-background shadow-lg z-40" }, /* @__PURE__ */ React11.createElement("div", { className: "px-4 py-2 space-y-1" }, items.map((item) => /* @__PURE__ */ React11.createElement("div", { key: item.label }, item.items && item.items.length > 0 ? /* @__PURE__ */ React11.createElement("div", null, /* @__PURE__ */ React11.createElement(
"button",
{
onClick: () => handleDropdownToggle(item.label),
className: cn(
"flex w-full items-center justify-between px-3 py-2 text-sm font-medium rounded-md transition-colors",
"hover:bg-accent hover:text-accent-foreground",
item.disabled && "opacity-50 cursor-not-allowed"
),
disabled: item.disabled
},
/* @__PURE__ */ React11.createElement("div", { className: "flex items-center space-x-2" }, item.icon, /* @__PURE__ */ React11.createElement("span", null, item.label), item.badge && /* @__PURE__ */ React11.createElement("span", { className: "px-2 py-0.5 text-xs bg-primary text-primary-foreground rounded-full" }, item.badge)),
/* @__PURE__ */ React11.createElement(import_lucide_react5.ChevronDown, { className: cn(
"h-4 w-4 transition-transform",
openDropdown === item.label && "rotate-180"
) })
), openDropdown === item.label && /* @__PURE__ */ React11.createElement("div", { className: "ml-4 mt-1 space-y-1" }, item.items.map((subItem) => /* @__PURE__ */ React11.createElement(
"button",
{
key: subItem.label,
onClick: () => handleItemClick(subItem),
className: cn(
"flex w-full items-center space-x-2 px-3 py-2 text-sm rounded-md transition-colors text-left",
"hover:bg-accent hover:text-accent-foreground",
subItem.disabled && "opacity-50 cursor-not-allowed"
),
disabled: subItem.disabled
},
subItem.icon,
/* @__PURE__ */ React11.createElement("span", null, subItem.label),
subItem.badge && /* @__PURE__ */ React11.createElement("span", { className: "ml-auto px-2 py-0.5 text-xs bg-primary text-primary-foreground rounded-full" }, subItem.badge)
)))) : /* @__PURE__ */ React11.createElement(
"button",
{
onClick: () => handleItemClick(item),
className: cn(
"flex w-full items-center space-x-2 px-3 py-2 text-sm font-medium rounded-md transition-colors text-left",
"hover:bg-accent hover:text-accent-foreground",
item.disabled && "opacity-50 cursor-not-allowed"
),
disabled: item.disabled
},
item.icon,
/* @__PURE__ */ React11.createElement("span", null, item.label),
item.badge && /* @__PURE__ */ React11.createElement("span", { className: "ml-auto px-2 py-0.5 text-xs bg-primary text-primary-foreground rounded-full" }, item.badge)
)))))
);
}
);
Navbar.displayName = "Navbar";
// src/components/ui/sidebar.tsx
var React12 = __toESM(require("react"));
var import_lucide_react6 = require("lucide-react");
var import_class_variance_authority9 = require("class-variance-authority");
var sidebarVariants = (0, import_class_variance_authority9.cva)(
"flex flex-col border-r bg-background transition-all duration-300 ease-in-out",
{
variants: {
variant: {
default: "border-border",
minimal: "border-transparent",
elevated: "shadow-lg border-border"
},
size: {
sm: "w-16",
default: "w-64",
lg: "w-80"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
);
var Sidebar = React12.forwardRef(
({
className,
variant,
size,
header,
footer,
sections = [],
collapsible = false,
collapsed = false,
onCollapse,
showCollapseButton = true,
...props
}, ref) => {
const [expandedItems, setExpandedItems] = React12.useState(/* @__PURE__ */ new Set());
const [internalCollapsed, setInternalCollapsed] = React12.useState(collapsed);
const isCollapsed = collapsible ? internalCollapsed : false;
React12.useEffect(() => {
setInternalCollapsed(collapsed);
}, [collapsed]);
const toggleCollapse = () => {
const newCollapsed = !internalCollapsed;
setInternalCollapsed(newCollapsed);
onCollapse?.(newCollapsed);
};
const toggleExpanded = (label) => {
const newExpanded = new Set(expandedItems);
if (newExpanded.has(label)) {
newExpanded.delete(label);
} else {
newExpanded.add(label);
}
setExpandedItems(newExpanded);
};
const handleItemClick = (item) => {
if (item.disabled) return;
if (item.items && item.items.length > 0) {
toggleExpanded(item.label);
} else if (item.onClick) {
item.onClick();
} else if (item.href) {
window.location.href = item.href;
}
};
const renderItem = (item, level = 0) => {
const hasChildren = item.items && item.items.length > 0;
const isExpanded = expandedItems.has(item.label);
return /* @__PURE__ */ React12.createElement("div", { key: item.label }, /* @__PURE__ */ React12.createElement(
"button",
{
onClick: () => handleItemClick(item),
className: cn(
"flex w-full items-center justify-between px-3 py-2 text-sm font-medium rounded-lg transition-colors group",
"hover:bg-accent hover:text-accent-foreground",
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
level > 0 && "ml-4 pl-6",
item.active && "bg-accent text-accent-foreground",
item.disabled && "opacity-50 cursor-not-allowed",
isCollapsed && "justify-center px-2"
),
disabled: item.disabled,
title: isCollapsed ? item.label : void 0
},
/* @__PURE__ */ React12.createElement("div", { className: "flex items-center space-x-3" }, item.icon && /* @__PURE__ */ React12.createElement("div", { className: "flex-shrink-0" }, item.icon), !isCollapsed && /* @__PURE__ */ React12.createElement("span", { className: "truncate" }, item.label)),
!isCollapsed && /* @__PURE__ */ React12.createElement("div", { className: "flex items-center space-x-2" }, item.badge && /* @__PURE__ */ React12.createElement("span", { className: "px-2 py-0.5 text-xs bg-primary text-primary-foreground rounded-full" }, item.badge), hasChildren && /* @__PURE__ */ React12.createElement(import_lucide_react6.ChevronDown, { className: cn(
"h-4 w-4 transition-transform",
isExpanded && "rotate-180"
) }))
), hasChildren && isExpanded && !isCollapsed && /* @__PURE__ */ React12.createElement("div", { className: "mt-1 space-y-1" }, item.items.map((subItem) => renderItem(subItem, level + 1))));
};
return /* @__PURE__ */ React12.createElement(
"div",
{
ref,
className: cn(
sidebarVariants({
variant,
size: isCollapsed ? "sm" : size
}),
className
),
...props
},
header && /* @__PURE__ */ React12.createElement("div", { className: cn(
"flex items-center p-4 border-b border-border",
isCollapsed && "justify-center px-2"
) }, header),
/* @__PURE__ */ React12.createElement("div", { className: "flex-1 overflow-y-auto p-4 space-y-2" }, sections.map((section, sectionIndex) => /* @__PURE__ */ React12.createElement("div", { key: `section-${sectionIndex}` }, section.title && !isCollapsed && /* @__PURE__ */ React12.createElement("div", { className: "px-3 mb-2" }, /* @__PURE__ */ React12.createElement("h3", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider" }, section.title)), /* @__PURE__ */ React12.createElement("div", { className: "space-y-1" }, section.items.map((item) => renderItem(item))), sectionIndex < sections.length - 1 && /* @__PURE__ */ React12.createElement("div", { className: "my-4 border-t border-border" })))),
footer && /* @__PURE__ */ React12.createElement("div", { className: cn(
"p-4 border-t border-border",
isCollapsed && "px-2"
) }, footer),
collapsible && showCollapseButton && /* @__PURE__ */ React12.createElement("div", { className: "p-2 border-t border-border" }, /* @__PURE__ */ React12.createElement(
"button",
{
onClick: toggleCollapse,
className: "flex w-full items-center justify-center p-2 rounded-lg hover:bg-accent hover:text-accent-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
title: isCollapsed ? "Expand sidebar" : "Collapse sidebar"
},
isCollapsed ? /* @__PURE__ */ React12.createElement(import_lucide_react6.ChevronRight, { className: "h-4 w-4" }) : /* @__PURE__ */ React12.createElement(import_lucide_react6.ChevronLeft, { className: "h-4 w-4" })
))
);
}
);
Sidebar.displayName = "Sidebar";
function useSidebar(defaultCollapsed = false) {
const [collapsed, setCollapsed] = React12.useState(defaultCollapsed);
const toggle = React12.useCallback(() => {
setCollapsed((prev) => !prev);
}, []);
const expand = React12.useCallback(() => {
setCollapsed(false);
}, []);
const collapse = React12.useCallback(() => {
setCollapsed(true);
}, []);
return {
collapsed,
toggle,
expand,
collapse,
setCollapsed
};
}
// src/components/ui/form.tsx
var React13 = __toESM(require("react"));
var import_class_variance_authority10 = require("class-variance-authority");
var formVariants = (0, import_class_variance_authority10.cva)("space-y-6", {
variants: {
variant: {
default: "",
card: "p-6 border border-border rounded-lg bg-card",
inline: "space-y-0 flex items-end space-x-4"
}
},
defaultVariants: {
variant: "default"
}
});
var FormContext = React13.createContext(void 0);
function useFormContext() {
const context = React13.useContext(FormContext);
if (!context) {
throw new Error("useFormContext must be used within a Form component");
}
return context;
}
var Form = React13.forwardRef(
({
className,
variant,
initialValues = {},
validationRules = {},
onFormSubmit,
validateOnChange = true,
validateOnBlur = true,
children,
...props
}, ref) => {
const [values, setValues] = React13.useState(initialValues);
const [errors, setErrors] = React13.useState({});
const [touched, setTouchedState] = React13.useState({});
const [isSubmitting, setIsSubmitting] = React13.useState(false);
const validateField = React13.useCallback(async (name) => {
const rule = validationRules[name];
if (!rule) return true;
const value = values[name];
let error;
if (rule.required) {
const isRequired = typeof rule.required === "boolean" ? rule.required : true;
const message = typeof rule.required === "string" ? rule.required : `${name} is required`;
if (isRequired && (value === void 0 || value === null || value === "")) {
error = { message, type: "required" };
}
}
if (!error && rule.minLength && typeof value === "string") {
const minLength = typeof rule.minLength === "number" ? rule.minLength : rule.minLength.value;
const message = typeof rule.minLength === "object" ? rule.minLength.message : `${name} must be at least ${minLength} characters`;
if (value.length < minLength) {
error = { message, type: "minLength" };
}
}
if (!error && rule.maxLength && typeof value === "string") {
const maxLength = typeof rule.maxLength === "number" ? rule.maxLength : rule.maxLength.value;
const message = typeof rule.maxLength === "object" ? rule.maxLength.message : `${name} must be no more than ${maxLength} characters`;
if (value.length > maxLength) {
error = { message, type: "maxLength" };
}
}
if (!error && rule.pattern && typeof value === "string") {
const pattern = rule.pattern instanceof RegExp ? rule.pattern : rule.pattern.value;
const message = rule.pattern instanceof RegExp ? `${name} format is invalid` : rule.pattern.message;
if (!pattern.test(value)) {
error = { message, type: "pattern" };
}
}
if (!error && rule.validate) {
const result = await rule.validate(value, values);
if (typeof result === "string") {
error = { message: result, type: "custom" };
} else if (result === false) {
error = { message: `${name} is invalid`, type: "custom" };
}
}
setErrors((prev) => ({ ...prev, [name]: error }));
return !error;
}, [values, validationRules]);
const setValue = React13.useCallback((name, value) => {
setValues((prev) => ({ ...prev, [name]: value }));
if (validateOnChange && touched[name]) {
setTimeout(() => validateField(name), 0);
}
}, [validateOnChange, touched, validateField]);
const setError = React13.useCallback((name, error) => {
setErrors((prev) => ({ ...prev, [name]: error }));
}, []);
const setTouched = React13.useCallback((name, isTouched = true) => {
setTouchedState((prev) => ({ ...prev, [name]: isTouched }));
if (validateOnBlur && isTouched) {
validateField(name);
}
}, [validateOnBlur, validateField]);
const resetForm = React13.useCallback(() => {
setValues(initialValues);
setErrors({});
setTouchedState({});
setIsSubmitting(false);
}, [initialValues]);
const handleSubmit = async (e) => {
e.preventDefault();
if (!onFormSubmit || isSubmitting) return;
setIsSubmitting(true);
const fieldNames = Object.keys(validationRules);
const validationResults = await Promise.all(
fieldNames.map((name) => validateField(name))
);
const hasErrors = validationResults.some((result) => !result);
if (hasErrors) {
setIsSubmitting(false);
return;
}
try {
await onFormSubmit(values, {
setSubmitting: setIsSubmitting,
setErrors: (errors2) => setErrors(errors2)
});
} catch (error) {
console.error("Form submission error:", error);
} finally {
setIsSubmitting(false);
}
};
const contextValue = {
values,
errors,
touched,
isSubmitting,
setValue,
setError,
setTouched,
validateField,
resetForm
};
return /* @__PURE__ */ React13.createElement(FormContext.Provider, { value: contextValue }, /* @__PURE__ */ React13.createElement(
"form",
{
ref,
className: cn(formVariants({ variant }), className),
onSubmit: handleSubmit,
...props
},
children
));
}
);
Form.displayName = "Form";
var FormField = ({ name, children }) => {
const { values, errors, touched, setValue, setTouched } = useFormContext();
const handleChange = (value) => {
setValue(name, value);
};
const handleBlur = () => {
setTouched(name, true);
};
return /* @__PURE__ */ React13.createElement(React13.Fragment, null, children({
value: values[name] ?? "",
onChange: handleChange,
onBlur: handleBlur,
error: errors[name],
touched: !!touched[name]
}));
};
var FormSubmit = React13.forwardRef(
({ children, loadingText = "Submitting...", disabled, ...props }, ref) => {
const { isSubmitting } = useFormContext();
return /* @__PURE__ */ React13.createElement(
"button",
{
ref,
type: "submit",
disabled: disabled || isSubmitting,
...props
},
isSubmitting ? loadingText : children
);
}
);
FormSubmit.displayName = "FormSubmit";
// src/components/ui/dropdown.tsx
var React14 = __toESM(require("react"));
var DropdownMenuPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"));
var import_lucide_react7 = require("lucide-react");
var DropdownMenu = DropdownMenuPrimitive.Root;
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
var DropdownMenuGroup = DropdownMenuPrimitive.Group;
var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
var DropdownMenuSubTrigger = React14.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ React14.createElement(
DropdownMenuPrimitive.SubTrigger,
{
ref,
className: cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "pl-8",
className
),
...props
},
children,
/* @__PURE__ */ React14.createElement(import_lucide_react7.ChevronRight, { className: "ml-auto h-4 w-4" })
));
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
var DropdownMenuSubContent = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React14.createElement(
DropdownMenuPrimitive.SubContent,
{
ref,
className: cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
),
...props
}
));
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
var DropdownMenuContent = React14.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ React14.createElement(DropdownMenuPrimitive.Portal, null, /* @__PURE__ */ React14.createElement(
DropdownMenuPrimitive.Content,
{
ref,
sideOffset,
className: cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
),
...props
}
)));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
var DropdownMenuItem = React14.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ React14.createElement(
DropdownMenuPrimitive.Item,
{
ref,
className: cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
),
...props
}
));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
var DropdownMenuCheckboxItem = React14.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ React14.createElement(
DropdownMenuPrimitive.CheckboxItem,
{
ref,
className: cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
),
checked,
...props
},
/* @__PURE__ */ React14.createElement("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center" }, /* @__PURE__ */ React14.createElement(DropdownMenuPrimitive.ItemIndicator, null, /* @__PURE__ */ React14.createElement(import_lucide_react7.Check, { className: "h-4 w-4" }))),
children
));
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
var DropdownMenuRadioItem = React14.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ React14.createElement(
DropdownMenuPrimitive.RadioItem,
{
ref,
className: cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
),
...props
},
/* @__PURE__ */ React14.createElement("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center" }, /* @__PURE__ */ React14.createElement(DropdownMenuPrimitive.ItemIndicator, null, /* @__PURE__ */ React14.createElement(import_lucide_react7.Circle, { className: "h-2 w-2 fill-current" }))),
children
));
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
var DropdownMenuLabel = React14.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ React14.createElement(
DropdownMenuPrimitive.Label,
{
ref,
className: cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className
),
...props
}
));
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
var DropdownMenuSeparator = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React14.createElement(
DropdownMenuPrimitive.Separator,
{
ref,
className: cn("-mx-1 my-1 h-px bg-muted", className),
...props
}
));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
var DropdownMenuShortcut = ({
className,
...props
}) => {
return /* @__PURE__ */ React14.createElement(
"span",
{
className: cn("ml-auto text-xs tracking-widest opacity-60", className),
...props
}
);
};
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
// src/components/ui/alert.tsx
var React15 = __toESM(require("react"));
var import_class_variance_authority11 = require("class-variance-authority");
var import_lucide_react8 = require("lucide-react");
var alertVariants = (0, import_class_variance_authority11.cva)(
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
warning: "border-yellow-200 bg-yellow-50 text-yellow-800 dark:border-yellow-900/50 dark:bg-yellow-900/20 dark:text-yellow-200 [&>svg]:text-yellow-600 dark:[&>svg]:text-yellow-400",
success: "border-green-200 bg-green-50 text-green-800 dark:border-green-900/50 dark:bg-green-900/20 dark:text-green-200 [&>svg]:text-green-600 dark:[&>svg]:text-green-400",
info: "border-blue-200 bg-blue-50 text-blue-800 dark:border-blue-900/50 dark:bg-blue-900/20 dark:text-blue-200 [&>svg]:text-blue-600 dark:[&>svg]:text-blue-400"
}
},
defaultVariants: {
variant: "default"
}
}
);
var Alert = React15.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ React15.createElement(
"div",
{
ref,
role: "alert",
className: cn(alertVariants({ variant }), className),
...props
}
));
Alert.displayName = "Alert";
var AlertTitle = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React15.createElement(
"h5",
{
ref,
className: cn("mb-1 font-medium leading-none tracking-tight", className),
...props
}
));
AlertTitle.displayName = "AlertTitle";
var AlertDescription = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React15.createElement(
"div",
{
ref,
className: cn("text-sm [&_p]:leading-relaxed", className),
...props
}
));
AlertDescription.displayName = "AlertDescription";
var AlertIcon = ({ variant }) => {
switch (variant) {
case "destructive":
return /* @__PURE__ */ React15.createElement(import_lucide_react8.XCircle, { className: "h-4 w-4" });
case "warning":
return /* @__PURE__ */ React15.createElement(import_lucide_react8.AlertCircle, { className: "h-4 w-4" });
case "success":
return /* @__PURE__ */ React15.createElement(import_lucide_react8.CheckCircle2, { className: "h-4 w-4" });
case "info":
return /* @__PURE__ */ React15.createElement(import_lucide_react8.Info, { className: "h-4 w-4" });
default:
return /* @__PURE__ */ React15.createElement(import_lucide_react8.Info, { className: "h-4 w-4" });
}
};
// src/components/ui/loader.tsx
var React16 = __toESM(require("react"));
var import_class_variance_authority12 = require("class-variance-authority");
var loaderVariants = (0, import_class_variance_authority12.cva)("animate-spin inline-block border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]", {
variants: {
size: {
xs: "h-3 w-3 border-2",
sm: "h-4 w-4 border-2",
md: "h-6 w-6 border-2",
lg: "h-8 w-8 border-[3px]",
xl: "h-12 w-12 border-4"
},
variant: {
default: "text-primary",
secondary: "text-secondary",
muted: "text-muted-foreground",
white: "text-white"
}
},
defaultVariants: {
size: "md",
variant: "default"
}
});
var Loader = React16.forwardRef(
({ className, size, variant, text, ...props }, ref) => {
return /* @__PURE__ */ React16.createElement(
"div",
{
ref,
className: cn("flex items-center gap-2", className),
role: "status",
"aria-label": text || "Loading",
...props
},
/* @__PURE__ */ React16.createElement(
"div",
{
className: cn(loaderVariants({ size, variant }), "rounded-full")
}
),
text && /* @__PURE__ */ React16.createElement("span", { className: "text-sm text-muted-foreground" }, text)
);
}
);
Loader.displayName = "Loader";
var skeletonVariants = (0, import_class_variance_authority12.cva)("animate-pulse bg-muted rounded", {
variants: {
variant: {
default: "bg-muted",
card: "bg-muted/50",
text: "bg-muted/60"
}
},
defaultVariants: {
variant: "default"
}
});
var Skeleton = React16.forwardRef(
({ className, variant, ...props }, ref) => {
return /* @__PURE__ */ React16.createElement(
"div",
{
ref,
className: cn(skeletonVariants({ variant }), className),
...props
}
);
}
);
Skeleton.displayName = "Skeleton";
var PageLoader = ({ text = "Loading...", size = "lg" }) => {
return /* @__PURE__ */ React16.createElement("div", { className: "flex min-h-[400px] w-full items-center justify-center" }, /* @__PURE__ */ React16.createElement(Loader, { size, text }));
};
// src/components/ui/chip.tsx
var React17 = __toESM(require("react"));
var import_class_variance_authority13 = require("class-variance-authority");
var import_lucide_react9 = require("lucide-react");
var chipVariants = (0, import_class_variance_authority13.cva)(
"inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/80",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground border border-input hover:bg-accent hover:text-accent-foreground",
success: "bg-green-100 text-green-800 hover:bg-green-200 dark:bg-green-900/20 dark:text-green-400 dark:hover:bg-green-900/30",
warning: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200 dark:bg-yellow-900/20 dark:text-yellow-400 dark:hover:bg-yellow-900/30",
info: "bg-blue-100 text-blue-800 hover:bg-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:hover:bg-blue-900/30"
},
size: {
sm: "h-5 px-2 text-xs",
md: "h-6 px-2.5 text-xs",
lg: "h-7 px-3 text-sm"
}
},
defaultVariants: {
variant: "default",
size: "md"
}
}
);
var Chip = React17.forwardRef(
({ className, variant, size, onRemove, removable = false, disabled = false, children, ...props }, ref) => {
return /* @__PURE__ */ React17.createElement(
"div",
{
ref,
className: cn(
chipVariants({ variant, size }),
disabled && "opacity-50 cursor-not-allowed",
className
),
...props
},
/* @__PURE__ */ React17.createElement("span", { className: "truncate" }, children),
removable && !disabled && /* @__PURE__ */ React17.createElement(
"button",
{
type: "button",
onClick: onRemove,
className: "ml-1 inline-flex h-3 w-3 items-center justify-center rounded-full hover:bg-black/20 dark:hover:bg-white/20 transition-colors",
"aria-label": "Remove"
},
/* @__PURE__ */ React17.createElement(import_lucide_react9.X, { className: "h-2 w-2" })
)
);
}
);
Chip.displayName = "Chip";
var ChipGroup = React17.forwardRef(
({ className, chips, onRemove, maxVisible, ...props }, ref) => {
const visibleChips = maxVisible ? chips.slice(0, maxVisible) : chips;
const hiddenCount = maxVisible ? Math.max(0, chips.length - maxVisible) : 0;
return /* @__PURE__ */ React17.createElement(
"div",
{
ref,
className: cn("flex flex-wrap items-center gap-1", className),
...props
},
visibleChips.map((chip) => /* @__PURE__ */ React17.createElement(
Chip,
{
key: chip.id,
variant: chip.variant,
removable: chip.removable,
onRemove: () => onRemove?.(chip.id)
},
chip.label
)),
hiddenCount > 0 && /* @__PURE__ */ React17.createElement(Chip, { variant: "outline", size: "sm" }, "+", hiddenCount, " more")
);
}
);
ChipGroup.displayName = "ChipGroup";
// src/components/ui/pagination.tsx
var React18 = __toESM(require("react"));
var import_class_variance_authority14 = require("class-variance-authority");
var import_lucide_react10 = require("lucide-react");
var paginationVariants = (0, import_class_variance_authority14.cva)(
"flex items-center justify-center space-x-2",
{
variants: {
size: {
default: "",
sm: "[&_button]:h-8 [&_button]:px-2 [&_button]:text-xs",
lg: "[&_button]:h-12 [&_button]:px-4 [&_button]:text-base"
}
},
defaultVariants: {
size: "default"
}
}
);
var Pagination = React18.forwardRef(
({
className,
size,
currentPage,
totalPages,
onPageChange,
showFirstLast = true,
showPrevNext = true,
maxVisiblePages = 5,
...props
}, ref) => {
const getVisiblePages = () => {
const half = Math.floor(maxVisiblePages / 2);
let start = Math.max(currentPage - half, 1);
let end = Math.min(start + maxVisiblePages - 1, totalPages);
if (end - start + 1 < maxVisiblePages) {
start = Math.max(end - maxVisiblePages + 1, 1);
}
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
};
const visiblePages = getVisiblePages();
const isFirstPage = currentPage === 1;
const isLastPage = currentPage === totalPages;
return /* @__PURE__ */ React18.createElement(
"nav",
{
ref,
className: cn(paginationVariants({ size }), className),
role: "navigation",
"aria-label": "Pagination",
...props
},
showFirstLast && !isFirstPage && /* @__PURE__ */ React18.createElement(
Button,
{
variant: "outline",
size: size === "sm" ? "sm" : size === "lg" ? "lg" : "default",
onClick: () => onPageChange(1),
"aria-label": "Go to first page"
},
"First"
),
showPrevNext && !isFirstPage && /* @__PURE__ */ React18.createElement(
Button,
{
variant: "outline",
size: size === "sm" ? "sm" : size === "lg" ? "lg" : "default",
onClick: () => onPageChange(currentPage - 1),
"aria-label": "Go to previous page"
},
/* @__PURE__ */ React18.createElement(import_lucide_react10.ChevronLeft, { className: "h-4 w-4" })
),
visiblePages[0] > 1 && /* @__PURE__ */ React18.createElement(React18.Fragment, null, /* @__PURE__ */ React18.createElement(
Button,
{
variant: "outline",
size: size === "sm" ? "sm" : size === "lg" ? "lg" : "default",
onClick: () => onPageChange(1)
},
"1"
), visiblePages[0] > 2 && /* @__PURE__ */ React18.createElement("span", { className: "px-2 text-muted-foreground" }, "...")),
visiblePages.map((page) => /* @__PURE__ */ React18.createElement(
Button,
{
key: page,
variant: page === currentPage ? "default" : "outline",
size: size === "sm" ? "sm" : size === "lg" ? "lg" : "default",
onClick: () => onPageChange(page),
"aria-label": `Go to page ${page}`,
"aria-current": page === currentPage ? "page" : void 0
},
page
)),
visiblePages[visiblePages.length - 1] < totalPages && /* @__PURE__ */ React18.createElement(React18.Fragment, null, visiblePages[visiblePages.length - 1] < totalPages - 1 && /* @__PURE__ */ React18.createElement("span", { className: "px-2 text-muted-foreground" }, "..."), /* @__PURE__ */ React18.createElement(
Button,
{
variant: "outline",
size: size === "sm" ? "sm" : size === "lg" ? "lg" : "default",
onClick: () => onPageChange(totalPages)
},
totalPages
)),
showPrevNext && !isLastPage && /* @__PURE__ */ React18.createElement(
Button,
{
variant: "outline",
size: size === "sm" ? "sm" : size === "lg" ? "lg" : "default",
onClick: () => onPageChange(currentPage + 1),
"aria-label": "Go to next page"
},
/* @__PURE__ */ React18.createElement(import_lucide_react10.ChevronRight, { className: "h-4 w-4" })
),
showFirstLast && !isLastPage && /* @__PURE__ */ React18.createElement(
Button,
{
variant: "outline",
size: size === "sm" ? "sm" : size === "lg" ? "lg" : "default",
onClick: () => onPageChange(totalPages),
"aria-label": "Go to last page"
},
"Last"
)
);
}
);
Pagination.displayName = "Pagination";
var PaginationInfo = ({
currentPage,
totalItems,
itemsPerPage,
className
}) => {
const startItem = (currentPage - 1) * itemsPerPage + 1;
const endItem = Math.min(currentPage * itemsPerPage, totalItems);
return /* @__PURE__ */ React18.createElement("div", { className: cn("text-sm text-muted-foreground", className) }, "Showing ", startItem, " to ", endItem, " of ", totalItems, " results");
};
// src/components/ui/search-bar.tsx
var React19 = __toESM(require("react"));
var import_class_variance_authority15 = require("class-variance-authority");
var import_lucide_react11 = require("lucide-react");
var searchBarVariants = (0, import_class_variance_authority15.cva)(
"flex items-center space-x-2 p-4 bg-background border rounded-lg",
{
variants: {
variant: {
default: "border-border",
elevated: "border-border shadow-md",
ghost: "border-transparent bg-transparent p-2"
},
size: {
sm: "p-2 space-x-1",
md: "p-4 space-x-2",
lg: "p-6 space-x-3"
}
},
defaultVariants: {
variant: "default",
size: "md"
}
}
);
var SearchBar = React19.forwardRef(
({
className,
variant,
size,
searchValue,
onSearchChange,
placeholder = "Search...",
filters = [],
activeFilters = [],
onFilterAdd,
onFilterRemove,
onClearAll,
showFilterButton = true,
showClearButton = true,
...props
}, ref) => {
const [isFilterOpen, setIsFilterOpen] = React19.useState(false);
const [selectedFilterId, setSelectedFilterId] = React19.useState("");
const [filterValue, setFilterValue] = React19.useState("");
const selectedFilter = filters.find((f) => f.id === selectedFilterId);
const handleAddFilter = () => {
if (selectedFilter && filterValue.trim() && onFilterAdd) {
const display = selectedFilter.type === "select" ? selectedFilter.options?.find((opt) => opt.value === filterValue)?.label || filterValue : filterValue;
onFilterAdd({
id: `${selectedFilter.id}-${Date.now()}`,
label: selectedFilter.label,
value: filterValue,
display: `${selectedFilter.label}: ${display}`
});
setSelectedFilterId("");
setFilterValue("");
setIsFilterOpen(false);
}
};
return /* @__PURE__ */ React19.createElement(
"div",
{
ref,
className: cn(searchBarVariants({ variant, size }), "flex-col space-y-3", className),
...props
},
/* @__PURE__ */ React19.createElement("div", { className: "flex items-center space-x-2 w-full" }, /* @__PURE__ */ React19.createElement("div", { className: "relative flex-1" }, /* @__PURE__ */ React19.createElement(import_lucide_react11.Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" }), /* @__PURE__ */ React19.createElement(
Input,
{
value: searchValue,
onChange: (e) => onSearchChange(e.target.value),
placeholder,
className: "pl-10"
}
)), showFilterButton && filters.length > 0 && /* @__PURE__ */ React19.createElement(
Button,
{
variant: "outline",
size: "sm",
onClick: () => setIsFilterOpen(!isFilterOpen),
className: "shrink-0"
},
/* @__PURE__ */ React19.createElement(import_lucide_react11.Filter, { className: "h-4 w-4 mr-2" }),
"Filter"
), showClearButton && (activeFilters.length > 0 || searchValue) && /* @__PURE__ */ React19.createElement(
Button,
{
variant: "ghost",
size: "sm",
onClick: () => {
onSearchChange("");
onClearAll?.();
},
className: "shrink-0"
},
/* @__PURE__ */ React19.createElement(import_lucide_react11.X, { className: "h-4 w-4 mr-2" }),
"Clear"
)),
activeFilters.length > 0 && /* @__PURE__ */ React19.createElement("div", { className: "flex flex-wrap gap-2" }, activeFilters.map((filter) => /* @__PURE__ */ React19.createElement(
Chip,
{
key: filter.id,
variant: "outline",
size: "sm",
removable: true,
onRemove: () => onFilterRemove?.(filter.id)
},
filter.display
))),
isFilterOpen && /* @__PURE__ */ React19.createElement("div", { className: "flex items-end space-x-2 p-3 bg-muted/50 rounded-md" }, /* @__PURE__ */ React19.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-sm font-medium mb-1 block" }, "Filter by"), /* @__PURE__ */ React19.createElement(
Select,
{
options: filters.map((f) => ({ value: f.id, label: f.label })),
value: selectedFilterId,
onValueChange: setSelectedFilterId,
placeholder: "Select filter..."
}
)), selectedFilter && /* @__PURE__ */ React19.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-sm font-medium mb-1 block" }, "Value"), selectedFilter.type === "select" ? /* @__PURE__ */ React19.createElement(
Select,
{
options: selectedFilter.options || [],
value: filterValue,
onValueChange: setFilterValue,
placeholder: selectedFilter.placeholder || "Select value..."
}
) : /* @__PURE__ */ React19.createElement(
Input,
{
type: selectedFilter.type === "date" ? "date" : selectedFilter.type === "number" ? "number" : "text",
value: filterValue,
onChange: (e) => setFilterValue(e.target.value),
placeholder: selectedFilter.placeholder || "Enter value..."
}
)), /* @__PURE__ */ React19.createElement(
Button,
{
onClick: handleAddFilter,
disabled: !selectedFilter || !filterValue.trim(),
size: "sm"
},
"Add Filter"
))
);
}
);
SearchBar.displayName = "SearchBar";
// src/components/ui/stat-card.tsx
var React20 = __toESM(require("react"));
var import_class_variance_authority16 = require("class-variance-authority");
var import_lucide_react12 = require("lucide-react");
var statCardVariants = (0, import_class_variance_authority16.cva)(
"transition-all duration-200 hover:shadow-md",
{
variants: {
variant: {
default: "",
accent: "border-l-4 border-l-primary",
gradient: "bg-gradient-to-br from-primary/5 to-primary/10",
elevated: "shadow-lg border-0"
},
size: {
sm: "[&_.stat-value]:text-xl [&_.stat-label]:text-xs",
md: "[&_.stat-value]:text-2xl [&_.stat-label]:text-sm",
lg: "[&_.stat-value]:text-3xl [&_.stat-label]:text-base"
}
},
defaultVariants: {
variant: "default",
size: "md"
}
}
);
var StatCard = React20.forwardRef(
({
className,
variant,
size,
title,
value,
change,
icon,
trend,
format = "number",
loading = false,
...props
}, ref) => {
const formatValue = (val) => {
const numValue = typeof val === "string" ? parseFloat(val) : val;
if (isNaN(numValue)) return val;
switch (format) {
case "currency":
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 0,
maximumFractionDigits: 2
}).format(numValue);
case "percentage":
return `${numValue}%`;
case "number":
default:
return new Intl.NumberFormat("en-US").format(numValue);
}
};
const getTrendIcon = () => {
if (!change) return null;
const trendType = trend || (change.value > 0 ? "up" : change.value < 0 ? "down" : "neutral");
switch (trendType) {
case "up":
return /* @__PURE__ */ React20.createElement(import_lucide_react12.TrendingUp, { className: "h-4 w-4 text-green-500" });
case "down":
return /* @__PURE__ */ React20.createElement(import_lucide_react12.TrendingDown, { className: "h-4 w-4 text-red-500" });
case "neutral":
default:
return /* @__PURE__ */ React20.createElement(import_lucide_react12.Minus, { className: "h-4 w-4 text-muted-foreground" });
}
};
const getTrendColor = () => {
if (!change) return "text-muted-foreground";
const trendType = trend || (change.value > 0 ? "up" : change.value < 0 ? "down" : "neutral");
switch (trendType) {
case "up":
return "text-green-600";
case "down":
return "text-red-600";
case "neutral":
default:
return "text-muted-foreground";
}
};
if (loading) {
return /* @__PURE__ */ React20.createElement(Card, { ref, className: cn(statCardVariants({ variant, size }), className), ...props }, /* @__PURE__ */ React20.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React20.createElement("div", { className: "animate-pulse bg-muted h-4 w-20 rounded" }), icon && /* @__PURE__ */ React20.createElement("div", { className: "animate-pulse bg-muted h-6 w-6 rounded" })), /* @__PURE__ */ React20.createElement(CardContent, null, /* @__PURE__ */ React20.createElement("div", { className: "animate-pulse bg-muted h-8 w-24 rounded mb-2" }), /* @__PURE__ */ React20.createElement("div", { className: "animate-pulse bg-muted h-3 w-16 rounded" })));
}
return /* @__PURE__ */ React20.createElement(Card, { ref, className: cn(statCardVariants({ variant, size }), className), ...props }, /* @__PURE__ */ React20.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React20.createElement(CardTitle, { className: "stat-label text-sm font-medium" }, title), icon && /* @__PURE__ */ React20.createElement("div", { className: "text-muted-foreground" }, icon)), /* @__PURE__ */ React20.createElement(CardContent, null, /* @__PURE__ */ React20.createElement("div", { className: "stat-value text-2xl font-bold" }, formatValue(value)), change && /* @__PURE__ */ React20.createElement("div", { className: "flex items-center space-x-1 text-xs mt-1" }, getTrendIcon(), /* @__PURE__ */ React20.createElement("span", { className: getTrendColor() }, change.value > 0 ? "+" : "", change.value, change.label && ` ${change.label}`), change.period && /* @__PURE__ */ React20.createElement("span", { className: "text-muted-foreground" }, "from ", change.period))));
}
);
StatCard.displayName = "StatCard";
var MetricCard = React20.forwardRef(
({ type, ...props }, ref) => {
const getIconAndFormat = () => {
switch (type) {
case "revenue":
return { icon: /* @__PURE__ */ React20.createElement(import_lucide_react12.DollarSign, { className: "h-4 w-4" }), format: "currency" };
case "users":
return { icon: /* @__PURE__ */ React20.createElement(import_lucide_react12.Users, { className: "h-4 w-4" }), format: "number" };
case "conversion":
return { icon: /* @__PURE__ */ React20.createElement(import_lucide_react12.Activity, { className: "h-4 w-4" }), format: "percentage" };
case "views":
return { icon: /* @__PURE__ */ React20.createElement(import_lucide_react12.Eye, { className: "h-4 w-4" }), format: "number" };
case "custom":
default:
return { icon: void 0, format: "number" };
}
};
const { icon, format } = getIconAndFormat();
return /* @__PURE__ */ React20.createElement(
StatCard,
{
ref,
icon,
format,
...props
}
);
}
);
MetricCard.displayName = "MetricCard";
// src/components/ui/code-block.tsx
var import_react = require("react");
var import_lucide_react13 = require("lucide-react");
function CodeBlock({ children, className = "" }) {
const [copied, setCopied] = (0, import_react.useState)(false);
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(children);
setCopied(true);
setTimeout(() => setCopied(false), 2e3);
} catch (err) {
console.error("Failed to copy text: ", err);
}
};
return /* @__PURE__ */ React.createElement("div", { className: `relative bg-muted p-4 rounded-lg text-sm overflow-x-auto ${className}` }, /* @__PURE__ */ React.createElement(
Button,
{
variant: "ghost",
size: "sm",
className: "absolute top-2 right-2 h-8 w-8 p-0",
onClick: copyToClipboard
},
copied ? /* @__PURE__ */ React.createElement(import_lucide_react13.Check, { className: "h-4 w-4 text-green-600" }) : /* @__PURE__ */ React.createElement(import_lucide_react13.Copy, { className: "h-4 w-4" })
), /* @__PURE__ */ React.createElement("pre", null, /* @__PURE__ */ React.createElement("code", null, children)));
}
// src/components/theme-provider.tsx
var React21 = __toESM(require("react"));
var import_next_themes = require("next-themes");
function ThemeProvider({ children, ...props }) {
return /* @__PURE__ */ React21.createElement(import_next_themes.ThemeProvider, { ...props }, children);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Alert,
AlertDescription,
AlertIcon,
AlertTitle,
Avatar,
Badge,
Button,
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
Chip,
ChipGroup,
CodeBlock,
DataTable,
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
Form,
FormField,
FormSubmit,
Input,
Loader,
MetricCard,
Modal,
ModalContent,
ModalDescription,
ModalFooter,
ModalHeader,
ModalTitle,
Navbar,
PageLoader,
Pagination,
PaginationInfo,
SearchBar,
Select,
Sidebar,
Skeleton,
StatCard,
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
ThemeProvider,
Toast,
ToastContainer,
ToastProvider,
avatarVariants,
badgeVariants,
buttonVariants,
cn,
formVariants,
inputVariants,
modalVariants,
navbarVariants,
paginationVariants,
searchBarVariants,
selectVariants,
sidebarVariants,
statCardVariants,
toast,
toastVariants,
useConfirmModal,
useFormContext,
useSidebar,
useToast
});
//# sourceMappingURL=index.js.map