UNPKG

hazo_ui

Version:

Set of UI components for common interaction elements in a SaaS app

1,231 lines (1,230 loc) 64.3 kB
import { jsx, jsxs } from 'react/jsx-runtime'; import * as React6 from 'react'; import { useState, useEffect } from 'react'; import { Slot } from '@radix-ui/react-slot'; import { cva } from 'class-variance-authority'; import { clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { X, ChevronDown, ChevronUp, Check, Circle, Filter, Plus, ChevronsUpDown, ArrowUpDown, Trash2, GripVertical, Calendar as Calendar$1, ChevronRight, ChevronLeft } from 'lucide-react'; import * as PopoverPrimitive from '@radix-ui/react-popover'; import * as SelectPrimitive from '@radix-ui/react-select'; import * as TooltipPrimitive from '@radix-ui/react-tooltip'; import { DayPicker } from 'react-day-picker'; import { format } from 'date-fns'; import * as SwitchPrimitives from '@radix-ui/react-switch'; import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from '@dnd-kit/core'; import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, arrayMove, useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import * as RadioGroupPrimitive from '@radix-ui/react-radio-group'; import * as FaIcons from 'react-icons/fa'; import * as MdIcons from 'react-icons/md'; import * as HiIcons from 'react-icons/hi'; import * as BiIcons from 'react-icons/bi'; import * as AiIcons from 'react-icons/ai'; import * as BsIcons from 'react-icons/bs'; import * as FiIcons from 'react-icons/fi'; import * as IoIcons from 'react-icons/io5'; import * as RiIcons from 'react-icons/ri'; import * as TbIcons from 'react-icons/tb'; import * as CiIcons from 'react-icons/ci'; var ExampleComponent = ({ children, className = "" }) => { return /* @__PURE__ */ jsx("div", { className: `cls_example_component ${className}`, children }); }; function cn(...inputs) { return twMerge(clsx(inputs)); } var buttonVariants = cva( "inline-flex items-center justify-center 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", { 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 = React6.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button"; return /* @__PURE__ */ jsx( Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props } ); } ); Button.displayName = "Button"; var Dialog = DialogPrimitive.Root; var DialogTrigger = DialogPrimitive.Trigger; var DialogPortal = DialogPrimitive.Portal; var DialogOverlay = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( DialogPrimitive.Overlay, { ref, className: cn( "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className ), ...props } )); DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; var DialogContent = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [ /* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs( DialogPrimitive.Content, { ref, className: cn( "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", className ), ...props, children: [ children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, { 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", children: [ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" }) ] }) ] } ) ] })); DialogContent.displayName = DialogPrimitive.Content.displayName; var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx( "div", { className: cn( "flex flex-col space-y-1.5 text-center sm:text-left", className ), ...props } ); DialogHeader.displayName = "DialogHeader"; var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx( "div", { className: cn( "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className ), ...props } ); DialogFooter.displayName = "DialogFooter"; var DialogTitle = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( DialogPrimitive.Title, { ref, className: cn( "text-lg font-semibold leading-none tracking-tight", className ), ...props } )); DialogTitle.displayName = DialogPrimitive.Title.displayName; var DialogDescription = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( DialogPrimitive.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props } )); DialogDescription.displayName = DialogPrimitive.Description.displayName; var Command = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( "div", { ref, className: cn( "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", className ), ...props } )); Command.displayName = "Command"; var CommandInput = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( "input", { ref, className: cn( "flex h-11 w-full rounded-md border border-input bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 px-3", className ), ...props } )); CommandInput.displayName = "CommandInput"; var CommandList = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( "div", { ref, className: cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className), ...props } )); CommandList.displayName = "CommandList"; var CommandEmpty = React6.forwardRef((props, ref) => /* @__PURE__ */ jsx( "div", { ref, className: "py-6 text-center text-sm", ...props } )); CommandEmpty.displayName = "CommandEmpty"; var CommandGroup = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( "div", { ref, className: cn( "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", className ), ...props } )); CommandGroup.displayName = "CommandGroup"; var CommandItem = React6.forwardRef(({ className, onSelect, value, ...props }, ref) => { const handleClick = () => { if (onSelect && value) { onSelect(value); } }; return /* @__PURE__ */ jsx( "div", { ref, className: cn( "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className ), onClick: handleClick, ...props } ); }); CommandItem.displayName = "CommandItem"; var Popover = PopoverPrimitive.Root; var PopoverTrigger = PopoverPrimitive.Trigger; var PopoverContent = React6.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx( PopoverPrimitive.Content, { ref, align, sideOffset, className: cn( "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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 } ) })); PopoverContent.displayName = PopoverPrimitive.Content.displayName; var Input = React6.forwardRef( ({ className, type, ...props }, ref) => { return /* @__PURE__ */ jsx( "input", { type, className: cn( "flex h-10 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", className ), ref, ...props } ); } ); Input.displayName = "Input"; var Select = SelectPrimitive.Root; var SelectValue = SelectPrimitive.Value; var SelectTrigger = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs( SelectPrimitive.Trigger, { ref, className: cn( "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 [&>span]:line-clamp-1", className ), ...props, children: [ children, /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) }) ] } )); SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; var SelectScrollUpButton = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( SelectPrimitive.ScrollUpButton, { ref, className: cn( "flex cursor-default items-center justify-center py-1", className ), ...props, children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" }) } )); SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; var SelectScrollDownButton = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( SelectPrimitive.ScrollDownButton, { ref, className: cn( "flex cursor-default items-center justify-center py-1", className ), ...props, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }) } )); SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName; var SelectContent = React6.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs( SelectPrimitive.Content, { ref, className: cn( "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover 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", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className ), position, ...props, children: [ /* @__PURE__ */ jsx(SelectScrollUpButton, {}), /* @__PURE__ */ jsx( SelectPrimitive.Viewport, { className: cn( "p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" ), children } ), /* @__PURE__ */ jsx(SelectScrollDownButton, {}) ] } ) })); SelectContent.displayName = SelectPrimitive.Content.displayName; var SelectLabel = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( SelectPrimitive.Label, { ref, className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className), ...props } )); SelectLabel.displayName = SelectPrimitive.Label.displayName; var SelectItem = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs( SelectPrimitive.Item, { ref, className: cn( "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className ), ...props, children: [ /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }), /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children }) ] } )); SelectItem.displayName = SelectPrimitive.Item.displayName; var SelectSeparator = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( SelectPrimitive.Separator, { ref, className: cn("-mx-1 my-1 h-px bg-muted", className), ...props } )); SelectSeparator.displayName = SelectPrimitive.Separator.displayName; var TooltipProvider = TooltipPrimitive.Provider; var Tooltip = TooltipPrimitive.Root; var TooltipTrigger = TooltipPrimitive.Trigger; var TooltipContent = React6.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx( TooltipPrimitive.Content, { ref, sideOffset, className: cn( "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 } )); TooltipContent.displayName = TooltipPrimitive.Content.displayName; function Calendar({ className, classNames, showOutsideDays = true, ...props }) { return /* @__PURE__ */ jsx( DayPicker, { showOutsideDays, className: cn("p-3", className), classNames: { months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0", month: "space-y-4", caption: "flex justify-center pt-1 relative items-center", caption_label: "text-sm font-medium", nav: "space-x-1 flex items-center", nav_button: cn( "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100" ), nav_button_previous: "absolute left-1", nav_button_next: "absolute right-1", table: "w-full border-collapse space-y-1", head_row: "flex", head_cell: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]", row: "flex w-full mt-2", cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20", day: cn( "h-9 w-9 p-0 font-normal aria-selected:opacity-100" ), day_range_end: "day-range-end", day_selected: "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", day_today: "bg-accent text-accent-foreground", day_outside: "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30", day_disabled: "text-muted-foreground opacity-50", day_range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground", day_hidden: "invisible", ...classNames }, components: { IconLeft: () => /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }), IconRight: () => /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) }, ...props } ); } Calendar.displayName = "Calendar"; function FilterFieldItem({ filterConfig, fieldConfig, onValueChange, onOperatorChange, onDelete }) { const [isCalendarOpen, setIsCalendarOpen] = useState(false); const renderInput = () => { switch (fieldConfig.type) { case "text": return /* @__PURE__ */ jsx( Input, { type: "text", value: filterConfig.value || "", onChange: (e) => { const value = e.target.value; if (fieldConfig.textConfig?.maxLength && value.length > fieldConfig.textConfig.maxLength) { return; } onValueChange(value); }, placeholder: "Enter text...", minLength: fieldConfig.textConfig?.minLength, maxLength: fieldConfig.textConfig?.maxLength, className: "cls_filter_text_input w-full min-w-0" } ); case "number": const numberOperators = [ { value: "equals", label: "Equals" }, { value: "not_equals", label: "Not Equals" }, { value: "greater_than", label: "Greater Than" }, { value: "less_than", label: "Less Than" }, { value: "greater_equal", label: "Greater or Equal" }, { value: "less_equal", label: "Less or Equal" } ]; return /* @__PURE__ */ jsxs("div", { className: "cls_number_filter_container flex flex-col sm:flex-row items-stretch sm:items-center gap-2 w-full", children: [ /* @__PURE__ */ jsxs( Select, { value: filterConfig.operator || "equals", onValueChange: (value) => onOperatorChange?.(value), children: [ /* @__PURE__ */ jsx(SelectTrigger, { className: "cls_operator_select w-full sm:w-[140px] shrink-0", children: /* @__PURE__ */ jsx(SelectValue, {}) }), /* @__PURE__ */ jsx(SelectContent, { children: numberOperators.map((op) => /* @__PURE__ */ jsx(SelectItem, { value: op.value, children: op.label }, op.value)) }) ] } ), /* @__PURE__ */ jsx( Input, { type: "number", value: filterConfig.value !== void 0 && filterConfig.value !== null ? filterConfig.value : "", onChange: (e) => { const value = e.target.value; if (value === "") { onValueChange(""); return; } let numValue = fieldConfig.numberConfig?.allowDecimal ? parseFloat(value) : parseInt(value, 10); if (isNaN(numValue)) { return; } if (fieldConfig.numberConfig?.allowDecimal && fieldConfig.numberConfig?.decimalLength) { const decimalPlaces = value.split(".")[1]?.length || 0; if (decimalPlaces > fieldConfig.numberConfig.decimalLength) { return; } } if (fieldConfig.numberConfig?.min !== void 0 && numValue < fieldConfig.numberConfig.min) { numValue = fieldConfig.numberConfig.min; } if (fieldConfig.numberConfig?.max !== void 0 && numValue > fieldConfig.numberConfig.max) { numValue = fieldConfig.numberConfig.max; } onValueChange(numValue); }, placeholder: "Enter number...", min: fieldConfig.numberConfig?.min, max: fieldConfig.numberConfig?.max, step: fieldConfig.numberConfig?.allowDecimal ? 0.01 : 1, className: "cls_filter_number_input flex-1 min-w-0" } ) ] }); case "combobox": return /* @__PURE__ */ jsxs( Select, { value: filterConfig.value || "", onValueChange: (value) => onValueChange(value), children: [ /* @__PURE__ */ jsx(SelectTrigger, { className: "cls_filter_combobox_select w-full min-w-0", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select option..." }) }), /* @__PURE__ */ jsx(SelectContent, { children: fieldConfig.comboboxOptions?.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) }) ] } ); case "boolean": const trueLabel = fieldConfig.booleanLabels?.trueLabel || "On"; const falseLabel = fieldConfig.booleanLabels?.falseLabel || "Off"; return /* @__PURE__ */ jsxs( Select, { value: filterConfig.value !== void 0 && filterConfig.value !== null ? String(filterConfig.value) : "", onValueChange: (value) => onValueChange(value === "true"), children: [ /* @__PURE__ */ jsx(SelectTrigger, { className: "cls_filter_boolean_select w-full min-w-0", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select..." }) }), /* @__PURE__ */ jsxs(SelectContent, { children: [ /* @__PURE__ */ jsx(SelectItem, { value: "true", children: trueLabel }), /* @__PURE__ */ jsx(SelectItem, { value: "false", children: falseLabel }) ] }) ] } ); case "date": const dateOperators = [ { value: "equals", label: "Equals" }, { value: "not_equals", label: "Not Equals" }, { value: "greater_than", label: "Greater Than" }, { value: "less_than", label: "Less Than" }, { value: "greater_equal", label: "Greater or Equal" }, { value: "less_equal", label: "Less or Equal" } ]; const selectedDate = filterConfig.value ? typeof filterConfig.value === "string" ? new Date(filterConfig.value) : filterConfig.value : void 0; return /* @__PURE__ */ jsxs("div", { className: "cls_date_filter_container flex flex-col sm:flex-row items-stretch sm:items-center gap-2 w-full", children: [ /* @__PURE__ */ jsxs( Select, { value: filterConfig.operator || "equals", onValueChange: (value) => onOperatorChange?.(value), children: [ /* @__PURE__ */ jsx(SelectTrigger, { className: "cls_operator_select w-full sm:w-[140px] shrink-0", children: /* @__PURE__ */ jsx(SelectValue, {}) }), /* @__PURE__ */ jsx(SelectContent, { children: dateOperators.map((op) => /* @__PURE__ */ jsx(SelectItem, { value: op.value, children: op.label }, op.value)) }) ] } ), /* @__PURE__ */ jsxs(Popover, { open: isCalendarOpen, onOpenChange: setIsCalendarOpen, children: [ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs( Button, { variant: "outline", className: cn( "cls_date_picker_trigger w-full justify-start text-left font-normal min-w-0", !selectedDate && "text-muted-foreground" ), children: [ /* @__PURE__ */ jsx(Calendar$1, { className: "cls_calendar_icon mr-2 h-4 w-4 shrink-0" }), /* @__PURE__ */ jsx("span", { className: "cls_date_text truncate", children: selectedDate ? format(selectedDate, "MMM d, yyyy") : "Pick a date" }) ] } ) }), /* @__PURE__ */ jsx(PopoverContent, { className: "cls_calendar_popover w-auto p-0", align: "start", children: /* @__PURE__ */ jsx( Calendar, { mode: "single", selected: selectedDate, onSelect: (date) => { onValueChange(date); setIsCalendarOpen(false); }, initialFocus: true } ) }) ] }) ] }); default: return null; } }; return /* @__PURE__ */ jsxs("div", { className: "cls_filter_field_item flex flex-col sm:flex-row items-start sm:items-center gap-3 p-3 border rounded-md bg-card", children: [ /* @__PURE__ */ jsx("span", { className: "cls_field_label text-sm font-medium min-w-[120px] sm:min-w-[100px] truncate", children: fieldConfig.label }), /* @__PURE__ */ jsx("div", { className: "cls_field_input_container flex-1 min-w-0 w-full sm:w-auto", children: renderInput() }), /* @__PURE__ */ jsx( Button, { variant: "ghost", size: "sm", onClick: onDelete, className: "cls_delete_btn h-8 w-8 p-0 text-destructive hover:text-destructive shrink-0", "aria-label": `Remove ${fieldConfig.label} filter`, children: /* @__PURE__ */ jsx(Trash2, { className: "cls_delete_icon h-4 w-4" }) } ) ] }); } function HazoUiMultiFilterDialog({ availableFields, onFilterChange, initialFilters = [] }) { const [isOpen, setIsOpen] = useState(false); const [filterFields, setFilterFields] = useState(initialFilters); const [isComboboxOpen, setIsComboboxOpen] = useState(false); useEffect(() => { if (isOpen) { setFilterFields(initialFilters); } }, [isOpen, initialFilters]); const handleAddField = (fieldValue) => { if (filterFields.some((ff) => ff.field === fieldValue)) { return; } const fieldConfig = availableFields.find((af) => af.value === fieldValue); if (!fieldConfig) { return; } let defaultValue = ""; if (fieldConfig.type === "boolean") { defaultValue = false; } else if (fieldConfig.type === "number") { defaultValue = fieldConfig.numberConfig?.min || 0; } else if (fieldConfig.type === "date") { defaultValue = /* @__PURE__ */ new Date(); } const newFilter = { field: fieldValue, operator: fieldConfig.type === "number" || fieldConfig.type === "date" ? "equals" : void 0, value: defaultValue }; setFilterFields([...filterFields, newFilter]); setIsComboboxOpen(false); }; const handleDeleteField = (fieldValue) => { setFilterFields(filterFields.filter((ff) => ff.field !== fieldValue)); }; const handleValueChange = (fieldValue, value) => { setFilterFields( filterFields.map( (ff) => ff.field === fieldValue ? { ...ff, value } : ff ) ); }; const handleOperatorChange = (fieldValue, operator) => { setFilterFields( filterFields.map( (ff) => ff.field === fieldValue ? { ...ff, operator } : ff ) ); }; const handleApply = () => { onFilterChange([...filterFields]); setIsOpen(false); }; const handleCancel = () => { setFilterFields(initialFilters); setIsOpen(false); }; const handleClearAll = () => { setFilterFields([]); }; const availableFieldsToAdd = availableFields.filter( (af) => !filterFields.some((ff) => ff.field === af.value) ); const getFieldConfig = (fieldValue) => { return availableFields.find((af) => af.value === fieldValue); }; const hasActiveFilters = initialFilters.length > 0; const tooltipContent = hasActiveFilters ? /* @__PURE__ */ jsxs("div", { className: "cls_filter_tooltip_content space-y-1", children: [ /* @__PURE__ */ jsx("div", { className: "cls_tooltip_title text-xs font-semibold mb-1", children: "Active Filters:" }), initialFilters.map((filterConfig) => { const fieldConfig = getFieldConfig(filterConfig.field); if (!fieldConfig) return null; let displayValue = String(filterConfig.value); if (fieldConfig.type === "boolean") { displayValue = filterConfig.value ? fieldConfig.booleanLabels?.trueLabel || "On" : fieldConfig.booleanLabels?.falseLabel || "Off"; } else if (fieldConfig.type === "combobox") { const option = fieldConfig.comboboxOptions?.find((opt) => opt.value === filterConfig.value); displayValue = option?.label || displayValue; } else if (fieldConfig.type === "date") { const dateValue = filterConfig.value instanceof Date ? filterConfig.value : new Date(filterConfig.value); if (!isNaN(dateValue.getTime())) { displayValue = format(dateValue, "MMM d, yyyy"); } } const operatorLabel = filterConfig.operator ? filterConfig.operator.replace("_", " ").replace(/\b\w/g, (l) => l.toUpperCase()) + " " : ""; return /* @__PURE__ */ jsxs("div", { className: "cls_tooltip_item text-xs", children: [ fieldConfig.label, ": ", operatorLabel, displayValue ] }, filterConfig.field); }) ] }) : /* @__PURE__ */ jsx("div", { className: "cls_filter_tooltip_content text-xs", children: "No active filters" }); return /* @__PURE__ */ jsxs(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: [ /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs( Button, { variant: "outline", size: "sm", className: "cls_filter_button", "aria-label": "Open filter dialog", children: [ /* @__PURE__ */ jsx( Filter, { className: cn( "cls_filter_icon h-4 w-4 mr-2", hasActiveFilters && "text-blue-600" ) } ), "Filter" ] } ) }) }), /* @__PURE__ */ jsx(TooltipContent, { children: tooltipContent }) ] }) }), /* @__PURE__ */ jsxs(DialogContent, { className: "cls_filter_dialog_content max-w-lg w-full max-h-[90vh] overflow-y-auto", children: [ /* @__PURE__ */ jsxs(DialogHeader, { children: [ /* @__PURE__ */ jsx(DialogTitle, { children: "Filter Images" }), /* @__PURE__ */ jsx(DialogDescription, { children: "Add multiple fields to filter by. Select a field and set its filter value." }) ] }), /* @__PURE__ */ jsxs("div", { className: "cls_filter_dialog_body space-y-4 py-4", children: [ /* @__PURE__ */ jsx("div", { className: "cls_add_field_section", children: /* @__PURE__ */ jsxs(Popover, { open: isComboboxOpen, onOpenChange: setIsComboboxOpen, children: [ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs( Button, { variant: "outline", role: "combobox", "aria-expanded": isComboboxOpen, className: "cls_add_field_combobox w-full justify-between", children: [ /* @__PURE__ */ jsxs("div", { className: "cls_combobox_content flex items-center", children: [ /* @__PURE__ */ jsx(Plus, { className: "cls_plus_icon h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", { children: "Add field" }) ] }), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "cls_chevron_icon ml-2 h-4 w-4 shrink-0 opacity-50" }) ] } ) }), /* @__PURE__ */ jsx(PopoverContent, { className: "cls_combobox_popover w-full p-0", children: /* @__PURE__ */ jsxs(Command, { children: [ /* @__PURE__ */ jsx(CommandInput, { placeholder: "Search fields...", className: "cls_command_input" }), /* @__PURE__ */ jsx(CommandList, { children: availableFieldsToAdd.length === 0 ? /* @__PURE__ */ jsx(CommandEmpty, { children: "No fields found." }) : /* @__PURE__ */ jsx(CommandGroup, { children: availableFieldsToAdd.map((field) => /* @__PURE__ */ jsxs( CommandItem, { value: field.label, onSelect: () => handleAddField(field.value), className: "cls_command_item", children: [ /* @__PURE__ */ jsx( Check, { className: cn( "cls_check_icon mr-2 h-4 w-4", "opacity-0" ) } ), field.label ] }, field.value )) }) }) ] }) }) ] }) }), filterFields.length > 0 ? /* @__PURE__ */ jsx("div", { className: "cls_filter_fields_list space-y-2", children: filterFields.map((filterConfig) => { const fieldConfig = getFieldConfig(filterConfig.field); if (!fieldConfig) return null; return /* @__PURE__ */ jsx( FilterFieldItem, { filterConfig, fieldConfig, onValueChange: (value) => handleValueChange(filterConfig.field, value), onOperatorChange: (operator) => handleOperatorChange(filterConfig.field, operator), onDelete: () => handleDeleteField(filterConfig.field) }, filterConfig.field ); }) }) : /* @__PURE__ */ jsx("div", { className: "cls_empty_filter_fields text-center py-8 text-sm text-muted-foreground", children: 'No filter fields added. Click "Add field" to add filtering criteria.' }) ] }), /* @__PURE__ */ jsxs(DialogFooter, { children: [ filterFields.length > 0 && /* @__PURE__ */ jsxs( Button, { variant: "outline", onClick: handleClearAll, className: "cls_clear_all_btn", children: [ /* @__PURE__ */ jsx(X, { className: "cls_clear_all_icon h-4 w-4 mr-2" }), "Clear All" ] } ), /* @__PURE__ */ jsx( Button, { onClick: handleApply, className: "cls_apply_btn", children: "Apply" } ), /* @__PURE__ */ jsx( Button, { variant: "outline", onClick: handleCancel, className: "cls_cancel_btn", children: "Cancel" } ) ] }) ] }) ] }); } var Switch = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx( SwitchPrimitives.Root, { className: cn( "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", className ), ...props, ref, children: /* @__PURE__ */ jsx( SwitchPrimitives.Thumb, { className: cn( "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0" ) } ) } )); Switch.displayName = SwitchPrimitives.Root.displayName; var Label2 = React6.forwardRef( ({ className, ...props }, ref) => { return /* @__PURE__ */ jsx( "label", { ref, className: cn( "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", className ), ...props } ); } ); Label2.displayName = "Label"; function SortableSortFieldItem({ sortConfig, fieldLabel, onDirectionChange, onDelete }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: sortConfig.field }); const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.5 : 1 }; return /* @__PURE__ */ jsxs( "div", { ref: setNodeRef, style, className: "cls_sortable_sort_field_item flex items-center gap-3 p-3 border rounded-md bg-card", children: [ /* @__PURE__ */ jsx( "div", { ...attributes, ...listeners, className: "cls_drag_handle cursor-grab active:cursor-grabbing text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(GripVertical, { className: "cls_drag_icon h-4 w-4" }) } ), /* @__PURE__ */ jsx("span", { className: "cls_field_label flex-1 text-sm font-medium", children: fieldLabel }), /* @__PURE__ */ jsxs("div", { className: "cls_direction_control flex items-center gap-2", children: [ /* @__PURE__ */ jsx(Label2, { htmlFor: `switch-${sortConfig.field}`, className: "cls_direction_label text-xs text-muted-foreground", children: sortConfig.direction === "asc" ? "Ascending" : "Descending" }), /* @__PURE__ */ jsx( Switch, { id: `switch-${sortConfig.field}`, checked: sortConfig.direction === "desc", onCheckedChange: (checked) => onDirectionChange(checked ? "desc" : "asc"), "aria-label": `Toggle sort direction for ${fieldLabel}` } ) ] }), /* @__PURE__ */ jsx( Button, { variant: "ghost", size: "sm", onClick: onDelete, className: "cls_delete_btn h-8 w-8 p-0 text-destructive hover:text-destructive", "aria-label": `Remove ${fieldLabel} from sort`, children: /* @__PURE__ */ jsx(Trash2, { className: "cls_delete_icon h-4 w-4" }) } ) ] } ); } function HazoUiMultiSortDialog({ availableFields, onSortChange, initialSortFields = [] }) { const [isOpen, setIsOpen] = useState(false); const [sortFields, setSortFields] = useState(initialSortFields); const [isComboboxOpen, setIsComboboxOpen] = useState(false); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); useEffect(() => { if (isOpen) { setSortFields(initialSortFields); } }, [isOpen, initialSortFields]); const handleAddField = (fieldValue) => { if (sortFields.some((sf) => sf.field === fieldValue)) { return; } const newField = { field: fieldValue, direction: "asc" }; setSortFields([...sortFields, newField]); setIsComboboxOpen(false); }; const handleDeleteField = (fieldValue) => { setSortFields(sortFields.filter((sf) => sf.field !== fieldValue)); }; const handleDirectionChange = (fieldValue, direction) => { setSortFields( sortFields.map( (sf) => sf.field === fieldValue ? { ...sf, direction } : sf ) ); }; const handleDragEnd = (event) => { const { active, over } = event; if (!over || active.id === over.id) { return; } const oldIndex = sortFields.findIndex((sf) => sf.field === active.id); const newIndex = sortFields.findIndex((sf) => sf.field === over.id); if (oldIndex !== -1 && newIndex !== -1) { setSortFields(arrayMove(sortFields, oldIndex, newIndex)); } }; const handleApply = () => { onSortChange([...sortFields]); setIsOpen(false); }; const handleCancel = () => { setSortFields(initialSortFields); setIsOpen(false); }; const handleClearAll = () => { setSortFields([]); }; const availableFieldsToAdd = availableFields.filter( (af) => !sortFields.some((sf) => sf.field === af.value) ); const getFieldLabel = (fieldValue) => { return availableFields.find((af) => af.value === fieldValue)?.label || fieldValue; }; const hasActiveSorts = initialSortFields.length > 0; const tooltipContent = hasActiveSorts ? /* @__PURE__ */ jsxs("div", { className: "cls_sort_tooltip_content space-y-1", children: [ /* @__PURE__ */ jsx("div", { className: "cls_tooltip_title text-xs font-semibold mb-1", children: "Active Sorts:" }), initialSortFields.map((sortConfig, index) => /* @__PURE__ */ jsxs("div", { className: "cls_tooltip_item text-xs", children: [ index + 1, ". ", getFieldLabel(sortConfig.field), " (", sortConfig.direction === "asc" ? "Asc" : "Desc", ")" ] }, sortConfig.field)) ] }) : /* @__PURE__ */ jsx("div", { className: "cls_sort_tooltip_content text-xs", children: "No active sorts" }); return /* @__PURE__ */ jsxs(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: [ /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs( Button, { variant: "outline", size: "sm", className: "cls_sort_button", "aria-label": "Open sort dialog", children: [ /* @__PURE__ */ jsx( ArrowUpDown, { className: cn( "cls_sort_icon h-4 w-4 mr-2", hasActiveSorts && "text-blue-600" ) } ), "Sort" ] } ) }) }), /* @__PURE__ */ jsx(TooltipContent, { children: tooltipContent }) ] }) }), /* @__PURE__ */ jsxs(DialogContent, { className: "cls_sort_dialog_content max-w-lg", children: [ /* @__PURE__ */ jsxs(DialogHeader, { children: [ /* @__PURE__ */ jsx(DialogTitle, { children: "Sort Images" }), /* @__PURE__ */ jsx(DialogDescription, { children: "Add multiple fields to sort by and reorder them. Use the switch to toggle between ascending and descending." }) ] }), /* @__PURE__ */ jsxs("div", { className: "cls_sort_dialog_body space-y-4 py-4", children: [ /* @__PURE__ */ jsx("div", { className: "cls_add_field_section", children: /* @__PURE__ */ jsxs(Popover, { open: isComboboxOpen, onOpenChange: setIsComboboxOpen, children: [ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs( Button, { variant: "outline", role: "combobox", "aria-expanded": isComboboxOpen, className: "cls_add_field_combobox w-full justify-between", children: [ /* @__PURE__ */ jsxs("div", { className: "cls_combobox_content flex items-center", children: [ /* @__PURE__ */ jsx(Plus, { className: "cls_plus_icon h-4 w-4 mr-2" }), /* @__PURE__ */ jsx("span", { children: "Add field" }) ] }), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "cls_chevron_icon ml-2 h-4 w-4 shrink-0 opacity-50" }) ] } ) }), /* @__PURE__ */ jsx(PopoverContent, { className: "cls_combobox_popover w-full p-0", children: /* @__PURE__ */ jsxs(Command, { children: [ /* @__PURE__ */ jsx(CommandInput, { placeholder: "Search fields...", className: "cls_command_input" }), /* @__PURE__ */ jsx(CommandList, { children: availableFieldsToAdd.length === 0 ? /* @__PURE__ */ jsx(CommandEmpty, { children: "No fields found." }) : /* @__PURE__ */ jsx(CommandGroup, { children: availableFieldsToAdd.map((field) => /* @__PURE__ */ jsxs( CommandItem, { value: field.label, onSelect: () => handleAddField(field.value), className: "cls_command_item", children: [ /* @__PURE__ */ jsx( Check, { className: cn( "cls_check_icon mr-2 h-4 w-4", "opacity-0" ) } ), field.label ] }, field.value )) }) }) ] }) }) ] }) }), sortFields.length > 0 ? /* @__PURE__ */ jsx("div", { className: "cls_sort_fields_list space-y-2", children: /* @__PURE__ */ jsx( DndContext, { sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx( SortableContext, { items: sortFields.map((sf) => sf.field), strategy: verticalListSortingStrategy, children: sortFields.map((sortConfig) => /* @__PURE__ */ jsx( SortableSortFieldItem, { sortConfig, fieldLabel: getFieldLabel(sortConfig.field), onDirectionChange: (direction) => handleDirectionChange(sortConfig.field, direction), onDelete: () => handleDeleteField(sortConfig.field) }, sortConfig.field )) } ) } ) }) : /* @__PURE__ */ jsx("div", { className: "cls_empty_sort_fields text-center py-8 text-sm text-muted-foreground", children: 'No sort fields added. Click "Add field" to add sorting criteria.' }) ] }), /* @__PURE__ */ jsxs(DialogFooter, { children: [ sortFields.length > 0 && /* @__PURE__ */ jsxs( Button, { variant: "outline", onClick: handleClearAll, className: "cls_clear_all_btn", children: [ /* @__PURE__ */ jsx(Trash2, { className: "cls_clear_all_icon h-4 w-4 mr-2" }), "Clear All" ] } ), /* @__PURE__ */ jsx( Button, { onClick: handleApply, className: "cls_apply_btn", children: "Apply" } ), /* @__PURE__ */ jsx( Button, { variant: "outline", onClick: handleCancel, className: "cls_cancel_btn", children: "Cancel" } ) ] }) ] }) ] }); } var RadioGroup = React6.forwardRef(({ className, ...props }, ref) => { return /* @__PURE__ */ jsx( RadioGroupPrimitive.Root, { className: cn("grid gap-2", className), ...props, ref } ); }); RadioGroup.displayName = RadioGroupPrimitive.Root.displayName; var RadioGroupItem = React6.forwardRef(({ className, ...props }, ref) => { return /* @__PURE__ */ jsx( RadioGroupPrimitive.Item, { ref, className: cn( "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", className ), ...props, children: /* @__PURE__ */ jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(Circle, { className: "h-2.5 w-2.5 fill-current text-current" }) }) } ); }); RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName; var iconSetMap = { fa: FaIcons, md: MdIcons, hi: HiIcons, bi: BiIcons, ai: AiIcons, bs: BsIcons, fi: FiIcons, io: IoIcons, io5: IoIcons, ri: RiIcons, tb: TbIcons, ci: CiIcons }; function getIconLibrary(iconSet) { if (!iconSet) return null; const normalizedSet = iconSet.toLowerCase(); return iconSetMap[normalizedSet] || null; } function getIconComponent(iconSet, iconName) { if (!iconSet || !iconName) return null; const iconLibrary = getIconLibrary(iconSet); if (!iconLibrary) return null; const IconComponent = iconLibrary[iconName]; return IconComponent || null; } function HazoUiFlexRadio({ layout = "horizontal", style = "radio", display_label = true, icon_set, data, selection = "single", value, onChange, className, compressed = false }) { const handleSingleSelection = (selectedValue) => { onChange(selectedValue); }; const handleMultiSelection = (selectedValue) => { const currentValues = Array.isArray(value) ? value : []; const isSelected2 = currentValues.includes(selectedValue); if (isSelected2) { onChange(curre