react-18-ui-library 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React, { createContext, useEffect, useCallback, useState, useRef, useId, useMemo, Component, useContext } from 'react';
3
3
  import { clsx } from 'clsx';
4
4
  import { twMerge } from 'tailwind-merge';
5
- import { X, Menu, ChevronRight, ChevronLeft, ChevronDown, ChevronsLeft, ChevronsRight, Check, Loader2, EyeOff, Eye, ChevronUp, Minus, Search, Star, Calendar, AlertCircle, Upload, Image as Image$1, FileText, File as File$1, ImageOff, User, ChevronsUpDown, TrendingUp, TrendingDown, Inbox, AlertTriangle, CheckCircle, Info, RefreshCw, Trash2, Copy, ExternalLink } from 'lucide-react';
5
+ import { ChevronDown, X, Menu, PanelLeftOpen, PanelLeftClose, ChevronRight, ChevronsLeft, ChevronLeft, ChevronsRight, Check, Loader2, EyeOff, Eye, ChevronUp, Minus, Search, Star, Calendar, AlertCircle, Upload, Image as Image$1, FileText, File as File$1, ImageOff, User, ChevronsUpDown, TrendingUp, TrendingDown, Inbox, AlertTriangle, CheckCircle, Info, RefreshCw, Trash2, Copy, ExternalLink } from 'lucide-react';
6
6
  import * as RadixTabs from '@radix-ui/react-tabs';
7
7
  import { cva } from 'class-variance-authority';
8
8
  import * as RadixSelect from '@radix-ui/react-select';
@@ -349,21 +349,48 @@ function cn(...inputs) {
349
349
  return twMerge(clsx(inputs));
350
350
  }
351
351
 
352
- function Navbar({ logo, links = [], actions, sticky = false, fixed = false, bordered = true, className, onMenuToggle, }) {
352
+ function getInitials$1(name) {
353
+ return name
354
+ .split(' ')
355
+ .filter(Boolean)
356
+ .slice(0, 2)
357
+ .map((n) => n[0].toUpperCase())
358
+ .join('');
359
+ }
360
+ function Navbar({ logo, links = [], actions, profile, sticky = false, fixed = false, bordered = true, className, onMenuToggle, }) {
353
361
  const [mobileOpen, setMobileOpen] = useState(false);
362
+ const [profileOpen, setProfileOpen] = useState(false);
363
+ const profileRef = useRef(null);
364
+ useEffect(() => {
365
+ if (!profileOpen)
366
+ return;
367
+ const handler = (e) => {
368
+ if (!profileRef.current?.contains(e.target)) {
369
+ setProfileOpen(false);
370
+ }
371
+ };
372
+ document.addEventListener('mousedown', handler);
373
+ return () => document.removeEventListener('mousedown', handler);
374
+ }, [profileOpen]);
354
375
  const handleMenuToggle = () => {
355
376
  const next = !mobileOpen;
356
377
  setMobileOpen(next);
357
378
  onMenuToggle?.(next);
358
379
  };
359
- return (jsxs("header", { className: cn('w-full bg-navbar font-base', sticky && 'sticky top-0', fixed && 'fixed top-0 left-0 right-0', bordered && 'border-b border-border', 'z-navbar', className), style: { height: 'var(--navbar-height)' }, children: [jsxs("div", { className: "flex items-center justify-between h-full px-4 md:px-6", children: [logo && jsx("div", { className: "flex-shrink-0", children: logo }), links.length > 0 && (jsx("nav", { className: "hidden md:flex items-center gap-1 ml-6", children: links.map((link, i) => (jsxs("a", { href: link.href, onClick: link.onClick, className: cn('flex items-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors cursor-pointer', link.active
380
+ return (jsxs("header", { className: cn('w-full bg-navbar font-base', sticky && 'sticky top-0', fixed && 'fixed top-0 left-0 right-0', bordered && 'border-b border-border', 'z-navbar', className), style: { height: 'var(--navbar-height)' }, children: [jsxs("div", { className: "flex items-center justify-between h-full px-4 md:px-6", children: [logo && jsx("div", { className: "flex-shrink-0", children: logo }), links.length > 0 && (jsx("nav", { className: "hidden md:flex items-center gap-1 ml-6", children: links.map((link) => (jsxs("a", { href: link.href, onClick: link.onClick, "aria-current": link.active ? 'page' : undefined, className: cn('flex items-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors cursor-pointer', link.active
360
381
  ? 'bg-primary text-primary-foreground'
361
- : 'text-text-muted hover:text-text hover:bg-surface-hover'), children: [link.icon && jsx("span", { className: "flex-shrink-0", children: link.icon }), link.label] }, i))) })), jsx("div", { className: "flex-1" }), actions && (jsx("div", { className: "hidden md:flex items-center gap-2", children: actions })), jsx("button", { type: "button", className: "md:hidden p-2 rounded-md text-text-muted hover:text-text hover:bg-surface-hover transition-colors", onClick: handleMenuToggle, "aria-label": "Toggle menu", children: mobileOpen ? jsx(X, { size: 20 }) : jsx(Menu, { size: 20 }) })] }), mobileOpen && (jsxs("div", { className: "md:hidden border-t border-border bg-navbar px-4 pb-4", children: [jsx("nav", { className: "flex flex-col gap-1 mt-2", children: links.map((link, i) => (jsxs("a", { href: link.href, onClick: () => {
382
+ : 'text-text-muted hover:text-text hover:bg-surface-hover'), children: [link.icon && jsx("span", { className: "flex-shrink-0", children: link.icon }), link.label] }, `${link.label}-${link.href ?? ''}`))) })), jsx("div", { className: "flex-1" }), actions && (jsx("div", { className: "hidden md:flex items-center gap-2", children: actions })), profile && (jsxs("div", { ref: profileRef, className: "hidden md:flex items-center ml-2 relative", children: [jsxs("button", { type: "button", onClick: () => setProfileOpen((v) => !v), "aria-expanded": profileOpen, "aria-haspopup": "menu", className: cn('flex items-center gap-2.5 px-2 py-1.5 rounded-lg transition-colors', 'hover:bg-surface-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-border-focus'), children: [profile.avatarUrl ? (jsx("img", { src: profile.avatarUrl, alt: profile.name, className: "w-8 h-8 rounded-full object-cover flex-shrink-0 ring-2 ring-border" })) : (jsx("span", { className: "w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-xs font-semibold flex-shrink-0 ring-2 ring-border", children: getInitials$1(profile.name) })), jsxs("div", { className: "flex flex-col items-start leading-tight", children: [jsx("span", { className: "text-sm font-medium text-text max-w-[120px] truncate", children: profile.name }), profile.email && (jsx("span", { className: "text-xs text-text-muted max-w-[120px] truncate", children: profile.email }))] }), profile.menuItems && profile.menuItems.length > 0 && (jsx(ChevronDown, { size: 14, className: cn('text-text-muted transition-transform flex-shrink-0', profileOpen && 'rotate-180') }))] }), profileOpen && profile.menuItems && profile.menuItems.length > 0 && (jsx("div", { role: "menu", className: cn('absolute right-0 top-full mt-1.5 min-w-[180px] bg-surface border border-border rounded-lg shadow-lg py-1 z-dropdown', 'animate-in fade-in-0 zoom-in-95'), children: profile.menuItems.map((item) => (jsx("a", { href: item.href, role: "menuitem", onClick: () => {
383
+ item.onClick?.();
384
+ setProfileOpen(false);
385
+ }, className: "flex items-center px-3 py-2 text-sm text-text hover:bg-surface-hover transition-colors cursor-pointer", children: item.name }, item.name))) }))] })), jsx("button", { type: "button", className: "md:hidden p-2 rounded-md text-text-muted hover:text-text hover:bg-surface-hover transition-colors", onClick: handleMenuToggle, "aria-label": "Toggle menu", children: mobileOpen ? jsx(X, { size: 20 }) : jsx(Menu, { size: 20 }) })] }), mobileOpen && (jsxs("div", { className: "md:hidden border-t border-border bg-navbar px-4 pb-4", children: [jsx("nav", { className: "flex flex-col gap-1 mt-2", children: links.map((link) => (jsxs("a", { href: link.href, "aria-current": link.active ? 'page' : undefined, onClick: () => {
362
386
  link.onClick?.();
363
387
  setMobileOpen(false);
364
388
  }, className: cn('flex items-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors cursor-pointer', link.active
365
389
  ? 'bg-primary text-primary-foreground'
366
- : 'text-text-muted hover:text-text hover:bg-surface-hover'), children: [link.icon && jsx("span", { className: "flex-shrink-0", children: link.icon }), link.label] }, i))) }), actions && jsx("div", { className: "flex items-center gap-2 mt-3", children: actions })] }))] }));
390
+ : 'text-text-muted hover:text-text hover:bg-surface-hover'), children: [link.icon && jsx("span", { className: "flex-shrink-0", children: link.icon }), link.label] }, `mobile-${link.label}-${link.href ?? ''}`))) }), actions && jsx("div", { className: "flex items-center gap-2 mt-3", children: actions }), profile && (jsxs("div", { className: "mt-3 pt-3 border-t border-border", children: [jsxs("div", { className: "flex items-center gap-3 px-1 mb-2", children: [profile.avatarUrl ? (jsx("img", { src: profile.avatarUrl, alt: profile.name, className: "w-9 h-9 rounded-full object-cover flex-shrink-0 ring-2 ring-border" })) : (jsx("span", { className: "w-9 h-9 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-xs font-semibold flex-shrink-0 ring-2 ring-border", children: getInitials$1(profile.name) })), jsxs("div", { className: "flex flex-col leading-tight min-w-0", children: [jsx("span", { className: "text-sm font-medium text-text truncate", children: profile.name }), profile.email && (jsx("span", { className: "text-xs text-text-muted truncate", children: profile.email }))] })] }), profile.menuItems && profile.menuItems.length > 0 && (jsx("div", { className: "flex flex-col gap-0.5", children: profile.menuItems.map((item) => (jsx("a", { href: item.href, onClick: () => {
391
+ item.onClick?.();
392
+ setMobileOpen(false);
393
+ }, className: "flex items-center px-3 py-2 text-sm text-text-muted hover:text-text hover:bg-surface-hover rounded-md transition-colors cursor-pointer", children: item.name }, item.name))) }))] }))] }))] }));
367
394
  }
368
395
 
369
396
  function SidebarItemComponent({ item, collapsed, depth = 0, }) {
@@ -374,9 +401,13 @@ function SidebarItemComponent({ item, collapsed, depth = 0, }) {
374
401
  setExpanded((v) => !v);
375
402
  item.onClick?.();
376
403
  };
377
- return (jsxs("li", { children: [jsxs("a", { href: item.href, onClick: handleClick, title: collapsed ? item.label : undefined, className: cn('flex items-center gap-3 px-3 py-2 rounded-md cursor-pointer transition-colors text-sm', 'text-sidebar-text hover:bg-sidebar-active-bg hover:text-white', item.active && 'bg-sidebar-active-bg text-white', depth > 0 && 'ml-4', collapsed && 'justify-center px-2'), children: [item.icon && (jsx("span", { className: "flex-shrink-0 w-5 h-5 flex items-center justify-center", children: item.icon })), !collapsed && (jsxs(Fragment, { children: [jsx("span", { className: "flex-1 truncate", children: item.label }), item.badge !== undefined && (jsx("span", { className: "ml-auto text-xs bg-primary text-primary-foreground rounded-full px-1.5 py-0.5 min-w-[1.25rem] text-center", children: item.badge })), hasChildren && (jsx("span", { className: "ml-auto", children: expanded ? jsx(ChevronDown, { size: 14 }) : jsx(ChevronRight, { size: 14 }) }))] }))] }), hasChildren && expanded && !collapsed && (jsx("ul", { className: "mt-1 space-y-1", children: item.children.map((child) => (jsx(SidebarItemComponent, { item: child, collapsed: collapsed, depth: depth + 1 }, child.id))) }))] }));
404
+ return (jsxs("li", { className: "relative", children: [jsxs("a", { href: item.href, onClick: handleClick, title: collapsed ? item.label : undefined, "aria-current": item.active ? 'page' : undefined, className: cn('group relative flex items-center gap-3 rounded-lg cursor-pointer text-sm font-medium', 'transition-all duration-150 outline-none', collapsed
405
+ ? 'justify-center w-10 h-10 mx-auto'
406
+ : 'px-3 py-2.5', item.active
407
+ ? 'bg-white/10 text-white'
408
+ : 'text-sidebar-text hover:bg-white/8 hover:text-white', depth > 0 && !collapsed && 'ml-3 text-xs'), children: [item.active && !collapsed && (jsx("span", { className: "absolute left-0 top-1/2 -translate-y-1/2 w-0.5 h-5 rounded-full bg-primary" })), item.icon && (jsx("span", { className: cn('flex-shrink-0 flex items-center justify-center transition-colors', collapsed ? 'w-5 h-5' : 'w-4 h-4', item.active ? 'text-primary' : 'text-sidebar-icon group-hover:text-white'), children: item.icon })), !collapsed && (jsxs(Fragment, { children: [jsx("span", { className: "flex-1 truncate", children: item.label }), item.badge !== undefined && (jsx("span", { className: "ml-auto text-[10px] font-semibold bg-primary/20 text-primary rounded-full px-1.5 py-0.5 min-w-[1.25rem] text-center leading-4", children: item.badge })), hasChildren && (jsx(ChevronDown, { size: 13, className: cn('ml-auto flex-shrink-0 text-sidebar-icon transition-transform duration-200', expanded && 'rotate-180') }))] })), collapsed && (jsxs("span", { role: "tooltip", className: cn('pointer-events-none absolute left-full ml-3 z-50', 'whitespace-nowrap rounded-md bg-gray-900 px-2.5 py-1.5 text-xs font-medium text-white shadow-lg', 'opacity-0 group-hover:opacity-100 transition-opacity duration-150', 'before:absolute before:right-full before:top-1/2 before:-translate-y-1/2', 'before:border-4 before:border-transparent before:border-r-gray-900'), children: [item.label, item.badge !== undefined && (jsx("span", { className: "ml-1.5 bg-primary/30 text-primary rounded-full px-1.5 py-0.5 text-[10px]", children: item.badge }))] }))] }), hasChildren && expanded && !collapsed && (jsx("ul", { className: "mt-0.5 ml-3 pl-3 border-l border-white/10 space-y-0.5", children: item.children.map((child) => (jsx(SidebarItemComponent, { item: child, collapsed: collapsed, depth: depth + 1 }, child.id))) }))] }));
378
409
  }
379
- function Sidebar({ items = [], collapsed: controlledCollapsed, defaultCollapsed = false, onCollapsedChange, header, footer, overlay = false, open = true, onClose, className, }) {
410
+ function Sidebar({ items = [], collapsed: controlledCollapsed, defaultCollapsed = false, onCollapsedChange, header, collapsedIcon, footer, overlay = false, open = true, onClose, className, }) {
380
411
  const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
381
412
  const collapsed = controlledCollapsed ?? internalCollapsed;
382
413
  const toggleCollapsed = () => {
@@ -384,15 +415,17 @@ function Sidebar({ items = [], collapsed: controlledCollapsed, defaultCollapsed
384
415
  setInternalCollapsed(next);
385
416
  onCollapsedChange?.(next);
386
417
  };
387
- return (jsxs(Fragment, { children: [overlay && open && (jsx("div", { className: "fixed inset-0 bg-overlay md:hidden", style: { zIndex: 'calc(var(--z-sidebar) - 1)' }, onClick: onClose })), jsxs("aside", { className: cn('flex flex-col h-full bg-sidebar transition-all duration-300', collapsed
418
+ return (jsxs(Fragment, { children: [overlay && open && (jsx("div", { className: "fixed inset-0 bg-overlay md:hidden", style: { zIndex: 'calc(var(--z-sidebar) - 1)' }, onClick: onClose })), jsxs("aside", { className: cn('relative flex flex-col h-full bg-sidebar', 'transition-[width] duration-300 ease-in-out will-change-[width]', collapsed
388
419
  ? 'w-[var(--sidebar-collapsed-width)]'
389
- : 'w-[var(--sidebar-width)]', overlay && 'fixed top-0 left-0 bottom-0', overlay && !open && '-translate-x-full', 'z-sidebar', className), children: [header && (jsx("div", { className: cn('flex-shrink-0 p-4', collapsed && 'px-2'), children: header })), jsx("nav", { className: "flex-1 overflow-y-auto py-2 px-2", children: jsx("ul", { className: "space-y-1", children: items.map((item) => (jsx(SidebarItemComponent, { item: item, collapsed: collapsed }, item.id))) }) }), footer && (jsx("div", { className: cn('flex-shrink-0 p-4 border-t border-border/20', collapsed && 'px-2'), children: footer })), jsx("button", { type: "button", onClick: toggleCollapsed, className: "flex-shrink-0 flex items-center justify-center h-10 border-t border-border/20 text-sidebar-text hover:text-white hover:bg-sidebar-active-bg transition-colors", "aria-label": collapsed ? 'Expand sidebar' : 'Collapse sidebar', children: collapsed ? jsx(ChevronRight, { size: 16 }) : jsx(ChevronLeft, { size: 16 }) })] })] }));
420
+ : 'w-[var(--sidebar-width)]', overlay && 'fixed top-0 left-0 bottom-0', overlay && !open && '-translate-x-full', 'z-sidebar', className), children: [(header || collapsedIcon) && (jsxs("div", { className: cn('flex-shrink-0 flex items-center border-b border-white/8', collapsed ? 'justify-center h-16 px-0' : 'px-4 h-16'), children: [!collapsed && (jsx("div", { className: "flex-1 min-w-0 overflow-hidden", children: header })), collapsed && (jsx("div", { className: "flex items-center justify-center w-9 h-9 rounded-xl bg-white/8", children: collapsedIcon }))] })), jsx("nav", { className: "flex-1 overflow-y-auto overflow-x-hidden py-3 px-2", children: jsx("ul", { className: cn('space-y-0.5', collapsed && 'flex flex-col items-center'), children: items.map((item) => (jsx(SidebarItemComponent, { item: item, collapsed: collapsed }, item.id))) }) }), footer && (jsx("div", { className: cn('flex-shrink-0 border-t border-white/8 transition-all duration-300', collapsed ? 'px-2 py-3' : 'px-4 py-3'), children: jsx("div", { className: cn('overflow-hidden transition-all duration-300', collapsed ? 'opacity-0 h-0 pointer-events-none' : 'opacity-100'), children: footer }) })), jsx("button", { type: "button", onClick: toggleCollapsed, "aria-label": collapsed ? 'Expand sidebar' : 'Collapse sidebar', className: cn('absolute -right-3', (header || collapsedIcon) ? 'top-[4.5rem]' : 'top-3', 'z-10 flex items-center justify-center', 'w-6 h-6 rounded-full', 'bg-sidebar border border-white/15 shadow-md', 'text-sidebar-text hover:text-white hover:bg-primary hover:border-primary', 'transition-all duration-200 hover:scale-110', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary'), children: collapsed
421
+ ? jsx(PanelLeftOpen, { size: 12 })
422
+ : jsx(PanelLeftClose, { size: 12 }) })] })] }));
390
423
  }
391
424
 
392
425
  function AppShell({ navbar, sidebar, children, defaultSidebarCollapsed = false, className, contentClassName, }) {
393
426
  const [sidebarCollapsed, setSidebarCollapsed] = useState(defaultSidebarCollapsed);
394
427
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
395
- return (jsxs("div", { className: cn('flex flex-col h-screen bg-background', className), children: [navbar && (jsx(Navbar, { ...navbar, sticky: true, onMenuToggle: (open) => setMobileSidebarOpen(open) })), jsxs("div", { className: "flex flex-1 overflow-hidden", children: [sidebar && (jsxs(Fragment, { children: [jsx("div", { className: "hidden md:flex flex-shrink-0", children: jsx(Sidebar, { ...sidebar, collapsed: sidebarCollapsed, onCollapsedChange: setSidebarCollapsed }) }), jsx("div", { className: "md:hidden", children: jsx(Sidebar, { ...sidebar, overlay: true, open: mobileSidebarOpen, onClose: () => setMobileSidebarOpen(false) }) })] })), jsx("main", { className: cn('flex-1 overflow-auto bg-background', contentClassName), children: children })] })] }));
428
+ return (jsxs("div", { className: cn('flex flex-col h-screen bg-background', className), children: [navbar && (jsx(Navbar, { ...navbar, sticky: true, onMenuToggle: (open) => setMobileSidebarOpen(open) })), jsxs("div", { className: "flex flex-1 overflow-hidden", children: [sidebar && (jsxs(Fragment, { children: [jsx("div", { className: "hidden md:block flex-shrink-0 relative", children: jsx(Sidebar, { ...sidebar, collapsed: sidebarCollapsed, onCollapsedChange: setSidebarCollapsed }) }), jsx("div", { className: "md:hidden", children: jsx(Sidebar, { ...sidebar, overlay: true, open: mobileSidebarOpen, onClose: () => setMobileSidebarOpen(false) }) })] })), jsx("main", { className: cn('flex-1 overflow-auto bg-background', contentClassName), children: children })] })] }));
396
429
  }
397
430
 
398
431
  const sizeClasses$g = {
@@ -557,7 +590,7 @@ function Pagination({ page, pageSize = 10, total, siblingCount = 1, showFirstLas
557
590
  const btnActive = 'bg-primary text-primary-foreground';
558
591
  const btnInactive = 'text-text-muted hover:bg-surface-hover hover:text-text';
559
592
  const btnDisabled = 'opacity-40 cursor-not-allowed pointer-events-none';
560
- return (jsxs("nav", { "aria-label": "Pagination", className: cn('flex items-center gap-1', className), children: [showFirstLast && (jsx("button", { type: "button", onClick: () => onPageChange(1), disabled: page === 1, className: cn(btnBase, page === 1 ? btnDisabled : btnInactive), "aria-label": "First page", children: jsx(ChevronsLeft, { size: 14 }) })), jsx("button", { type: "button", onClick: () => onPageChange(page - 1), disabled: page === 1, className: cn(btnBase, page === 1 ? btnDisabled : btnInactive), "aria-label": "Previous page", children: jsx(ChevronLeft, { size: 14 }) }), pages.map((p, i) => p === '...' ? (jsx("span", { className: "flex items-center justify-center w-8 h-8 text-text-muted text-sm", children: "\u2026" }, `dots-${i}`)) : (jsx("button", { type: "button", onClick: () => onPageChange(p), className: cn(btnBase, page === p ? btnActive : btnInactive), "aria-label": `Page ${p}`, "aria-current": page === p ? 'page' : undefined, children: p }, p))), jsx("button", { type: "button", onClick: () => onPageChange(page + 1), disabled: page === totalPages, className: cn(btnBase, page === totalPages ? btnDisabled : btnInactive), "aria-label": "Next page", children: jsx(ChevronRight, { size: 14 }) }), showFirstLast && (jsx("button", { type: "button", onClick: () => onPageChange(totalPages), disabled: page === totalPages, className: cn(btnBase, page === totalPages ? btnDisabled : btnInactive), "aria-label": "Last page", children: jsx(ChevronsRight, { size: 14 }) }))] }));
593
+ return (jsxs("nav", { "aria-label": "Pagination", className: cn('flex items-center gap-1', className), children: [showFirstLast && (jsx("button", { type: "button", onClick: () => onPageChange(1), disabled: page === 1, className: cn(btnBase, page === 1 ? btnDisabled : btnInactive), "aria-label": "First page", children: jsx(ChevronsLeft, { size: 14 }) })), jsx("button", { type: "button", onClick: () => onPageChange(page - 1), disabled: page === 1, className: cn(btnBase, page === 1 ? btnDisabled : btnInactive), "aria-label": "Previous page", children: jsx(ChevronLeft, { size: 14 }) }), pages.map((p, i) => p === '...' ? (jsx("span", { className: "flex items-center justify-center w-8 h-8 text-text-muted text-sm", children: "\u2026" }, `dots-${i === 1 ? 'left' : 'right'}`)) : (jsx("button", { type: "button", onClick: () => onPageChange(p), className: cn(btnBase, page === p ? btnActive : btnInactive), "aria-label": `Page ${p}`, "aria-current": page === p ? 'page' : undefined, children: p }, p))), jsx("button", { type: "button", onClick: () => onPageChange(page + 1), disabled: page === totalPages, className: cn(btnBase, page === totalPages ? btnDisabled : btnInactive), "aria-label": "Next page", children: jsx(ChevronRight, { size: 14 }) }), showFirstLast && (jsx("button", { type: "button", onClick: () => onPageChange(totalPages), disabled: page === totalPages, className: cn(btnBase, page === totalPages ? btnDisabled : btnInactive), "aria-label": "Last page", children: jsx(ChevronsRight, { size: 14 }) }))] }));
561
594
  }
562
595
 
563
596
  function StepIndicator({ steps, currentStep, orientation = 'horizontal', className, }) {
@@ -679,18 +712,24 @@ const TextArea = React.forwardRef(({ label, helperText, error, prefixIcon, prefi
679
712
  const generatedId = useId();
680
713
  const id = externalId ?? generatedId;
681
714
  const internalRef = useRef(null);
682
- const resolvedRef = ref ?? internalRef;
715
+ const setRef = useCallback((el) => {
716
+ internalRef.current = el;
717
+ if (typeof ref === 'function')
718
+ ref(el);
719
+ else if (ref)
720
+ ref.current = el;
721
+ }, [ref]);
683
722
  const charCount = value !== undefined ? String(value).length : 0;
684
723
  const hasPrefix = !!(prefixIcon || prefixImage);
685
724
  const hasSuffix = !!(suffixIcon || suffixImage);
686
725
  useEffect(() => {
687
- if (!autoResize || !resolvedRef.current)
726
+ if (!autoResize || !internalRef.current)
688
727
  return;
689
- const el = resolvedRef.current;
728
+ const el = internalRef.current;
690
729
  el.style.height = 'auto';
691
730
  el.style.height = `${el.scrollHeight}px`;
692
- }, [value, autoResize, resolvedRef]);
693
- return (jsxs("div", { className: cn('flex flex-col gap-1', fullWidth && 'w-full', containerClassName), children: [label && (jsxs("label", { htmlFor: id, className: "text-sm font-medium text-text", children: [label, required && jsx("span", { className: "text-error ml-1", children: "*" })] })), jsxs("div", { className: cn('relative', fullWidth && 'w-full'), children: [hasPrefix && (jsx("div", { className: "absolute top-2.5 left-0 flex items-center justify-center w-9 text-text-muted", children: prefixImage ? (jsx("img", { src: prefixImage, alt: "", className: "w-4 h-4 object-contain" })) : (jsx("span", { className: "flex items-center justify-center w-4 h-4", children: prefixIcon })) })), jsx("textarea", { ref: resolvedRef, id: id, value: value, onChange: onChange, disabled: disabled, required: required, rows: rows, maxLength: maxLength, className: cn('w-full rounded-md border bg-surface text-text placeholder:text-text-muted text-sm', 'transition-colors outline-none resize-y py-2 px-3', 'focus:border-border-focus focus:ring-2 focus:ring-border-focus/20', 'disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-surface-hover', error ? 'border-error focus:border-error focus:ring-error/20' : 'border-border', hasPrefix && 'pl-9', hasSuffix && 'pr-9', autoResize && 'resize-none overflow-hidden', textareaClassName), "aria-invalid": !!error, "aria-describedby": error ? `${id}-error` : helperText ? `${id}-helper` : undefined, ...props }), hasSuffix && (jsx("div", { className: "absolute top-2.5 right-2 flex items-center justify-center text-text-muted", children: suffixImage ? (jsx("img", { src: suffixImage, alt: "", className: "w-4 h-4 object-contain" })) : (jsx("span", { className: "flex items-center justify-center w-4 h-4", children: suffixIcon })) }))] }), jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { children: [error && (jsx("p", { id: `${id}-error`, className: "text-xs text-error", role: "alert", children: error })), !error && helperText && (jsx("p", { id: `${id}-helper`, className: "text-xs text-text-muted", children: helperText }))] }), (showCharCount || showMaxLength) && (jsxs("p", { className: cn('text-xs text-text-muted ml-auto', maxLength && charCount >= maxLength && 'text-error'), children: [charCount, maxLength ? `/${maxLength}` : ''] }))] })] }));
731
+ }, [value, autoResize]);
732
+ return (jsxs("div", { className: cn('flex flex-col gap-1', fullWidth && 'w-full', containerClassName), children: [label && (jsxs("label", { htmlFor: id, className: "text-sm font-medium text-text", children: [label, required && jsx("span", { className: "text-error ml-1", children: "*" })] })), jsxs("div", { className: cn('relative', fullWidth && 'w-full'), children: [hasPrefix && (jsx("div", { className: "absolute top-2.5 left-0 flex items-center justify-center w-9 text-text-muted", children: prefixImage ? (jsx("img", { src: prefixImage, alt: "", className: "w-4 h-4 object-contain" })) : (jsx("span", { className: "flex items-center justify-center w-4 h-4", children: prefixIcon })) })), jsx("textarea", { ref: setRef, id: id, value: value, onChange: onChange, disabled: disabled, required: required, rows: rows, maxLength: maxLength, className: cn('w-full rounded-md border bg-surface text-text placeholder:text-text-muted text-sm', 'transition-colors outline-none resize-y py-2 px-3', 'focus:border-border-focus focus:ring-2 focus:ring-border-focus/20', 'disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-surface-hover', error ? 'border-error focus:border-error focus:ring-error/20' : 'border-border', hasPrefix && 'pl-9', hasSuffix && 'pr-9', autoResize && 'resize-none overflow-hidden', textareaClassName), "aria-invalid": !!error, "aria-describedby": error ? `${id}-error` : helperText ? `${id}-helper` : undefined, ...props }), hasSuffix && (jsx("div", { className: "absolute top-2.5 right-2 flex items-center justify-center text-text-muted", children: suffixImage ? (jsx("img", { src: suffixImage, alt: "", className: "w-4 h-4 object-contain" })) : (jsx("span", { className: "flex items-center justify-center w-4 h-4", children: suffixIcon })) }))] }), jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { children: [error && (jsx("p", { id: `${id}-error`, className: "text-xs text-error", role: "alert", children: error })), !error && helperText && (jsx("p", { id: `${id}-helper`, className: "text-xs text-text-muted", children: helperText }))] }), (showCharCount || showMaxLength) && (jsxs("p", { className: cn('text-xs text-text-muted ml-auto', maxLength && charCount >= maxLength && 'text-error'), children: [charCount, maxLength ? `/${maxLength}` : ''] }))] })] }));
694
733
  });
695
734
  TextArea.displayName = 'TextArea';
696
735
 
@@ -777,24 +816,25 @@ function SearchSelect(props) {
777
816
  .filter((g) => g.options.length > 0)
778
817
  : [];
779
818
  const isSelected = (val) => selectedValues.includes(val);
819
+ const propsRef = React.useRef(props);
820
+ propsRef.current = props;
780
821
  const toggle = useCallback((val) => {
781
- if (props.multiple) {
782
- const current = props.value ?? [];
822
+ const p = propsRef.current;
823
+ if (p.multiple) {
824
+ const current = p.value ?? [];
783
825
  const next = current.includes(val)
784
826
  ? current.filter((v) => v !== val)
785
827
  : maxSelected && current.length >= maxSelected
786
828
  ? current
787
829
  : [...current, val];
788
- props.onChange?.(next);
830
+ p.onChange?.(next);
789
831
  }
790
832
  else {
791
- props.onChange?.(val);
833
+ p.onChange?.(val);
792
834
  setOpen(false);
793
835
  setQuery('');
794
836
  }
795
- },
796
- // eslint-disable-next-line react-hooks/exhaustive-deps
797
- [props.multiple, props.value, maxSelected]);
837
+ }, [maxSelected]);
798
838
  const removeChip = (val, e) => {
799
839
  e.stopPropagation();
800
840
  if (props.multiple) {
@@ -1042,7 +1082,7 @@ const thumbSize = {
1042
1082
  };
1043
1083
  function Slider({ value, defaultValue = [0], onValueChange, onValueCommit, min = 0, max = 100, step = 1, disabled = false, orientation = 'horizontal', size = 'md', showTooltip = false, showMarks = false, formatValue = (v) => String(v), label, helperText, className, }) {
1044
1084
  const [internalValue, setInternalValue] = React.useState(value ?? defaultValue);
1045
- const [showTip, setShowTip] = React.useState(false);
1085
+ const [hoveredThumb, setHoveredThumb] = React.useState(null);
1046
1086
  const currentValue = value ?? internalValue;
1047
1087
  const handleValueChange = (v) => {
1048
1088
  setInternalValue(v);
@@ -1054,7 +1094,7 @@ function Slider({ value, defaultValue = [0], onValueChange, onValueCommit, min =
1054
1094
  const count = Math.floor((max - min) / step);
1055
1095
  return Array.from({ length: count + 1 }, (_, i) => min + i * step);
1056
1096
  }, [showMarks, min, max, step]);
1057
- return (jsxs("div", { className: cn('flex flex-col gap-1.5', className), children: [label && (jsxs("div", { className: "flex items-center justify-between", children: [jsx("span", { className: "text-sm font-medium text-text", children: label }), jsx("span", { className: "text-sm text-text-muted", children: currentValue.map(formatValue).join(' – ') })] })), jsxs(RadixSlider.Root, { className: cn('relative flex items-center select-none touch-none', orientation === 'horizontal' ? 'w-full' : 'flex-col h-40 w-5', disabled && 'opacity-50 cursor-not-allowed'), value: currentValue, defaultValue: defaultValue, onValueChange: handleValueChange, onValueCommit: onValueCommit, min: min, max: max, step: step, disabled: disabled, orientation: orientation, children: [jsx(RadixSlider.Track, { className: cn('relative grow rounded-full bg-surface-hover overflow-hidden', orientation === 'horizontal' ? `w-full ${trackSize[size]}` : `h-full w-${size === 'sm' ? '1' : size === 'md' ? '1.5' : '2'}`), children: jsx(RadixSlider.Range, { className: "absolute bg-primary rounded-full h-full" }) }), currentValue.map((_, i) => (jsx(RadixSlider.Thumb, { className: cn('block rounded-full bg-white border-2 border-primary shadow-md', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/40', 'transition-transform hover:scale-110', 'cursor-grab active:cursor-grabbing', thumbSize[size], disabled && 'cursor-not-allowed hover:scale-100'), onMouseEnter: () => showTooltip && setShowTip(true), onMouseLeave: () => showTooltip && setShowTip(false), "aria-label": `Slider thumb ${i + 1}`, children: showTooltip && showTip && (jsx("div", { className: "absolute -top-8 left-1/2 -translate-x-1/2 bg-text text-background text-xs px-2 py-0.5 rounded whitespace-nowrap pointer-events-none", children: formatValue(currentValue[i]) })) }, i)))] }), showMarks && (jsx("div", { className: "relative flex justify-between mt-1", children: marks.map((mark) => (jsx("span", { className: "text-xs text-text-muted", children: formatValue(mark) }, mark))) })), helperText && jsx("p", { className: "text-xs text-text-muted", children: helperText })] }));
1097
+ return (jsxs("div", { className: cn('flex flex-col gap-1.5', className), children: [label && (jsxs("div", { className: "flex items-center justify-between", children: [jsx("span", { className: "text-sm font-medium text-text", children: label }), jsx("span", { className: "text-sm text-text-muted", children: currentValue.map(formatValue).join(' – ') })] })), jsxs(RadixSlider.Root, { className: cn('relative flex items-center select-none touch-none', orientation === 'horizontal' ? 'w-full' : 'flex-col h-40 w-5', disabled && 'opacity-50 cursor-not-allowed'), value: currentValue, defaultValue: defaultValue, onValueChange: handleValueChange, onValueCommit: onValueCommit, min: min, max: max, step: step, disabled: disabled, orientation: orientation, children: [jsx(RadixSlider.Track, { className: cn('relative grow rounded-full bg-surface-hover overflow-hidden', orientation === 'horizontal' ? `w-full ${trackSize[size]}` : `h-full w-${size === 'sm' ? '1' : size === 'md' ? '1.5' : '2'}`), children: jsx(RadixSlider.Range, { className: "absolute bg-primary rounded-full h-full" }) }), currentValue.map((val, i) => (jsx(RadixSlider.Thumb, { className: cn('block rounded-full bg-white border-2 border-primary shadow-md', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/40', 'transition-transform hover:scale-110', 'cursor-grab active:cursor-grabbing', thumbSize[size], disabled && 'cursor-not-allowed hover:scale-100'), onMouseEnter: () => showTooltip && setHoveredThumb(i), onMouseLeave: () => showTooltip && setHoveredThumb(null), "aria-label": `Slider thumb ${i + 1}`, children: showTooltip && hoveredThumb === i && (jsx("div", { className: "absolute -top-8 left-1/2 -translate-x-1/2 bg-text text-background text-xs px-2 py-0.5 rounded whitespace-nowrap pointer-events-none", children: formatValue(val) })) }, `thumb-${i}`)))] }), showMarks && (jsx("div", { className: "relative flex justify-between mt-1", children: marks.map((mark) => (jsx("span", { className: "text-xs text-text-muted", children: formatValue(mark) }, mark))) })), helperText && jsx("p", { className: "text-xs text-text-muted", children: helperText })] }));
1058
1098
  }
1059
1099
 
1060
1100
  const sizeClasses$d = {
@@ -1067,6 +1107,7 @@ const NumberInput = React.forwardRef(({ value: controlledValue, defaultValue, on
1067
1107
  const id = externalId ?? generatedId;
1068
1108
  const [internalValue, setInternalValue] = useState(defaultValue);
1069
1109
  const [inputStr, setInputStr] = useState(defaultValue !== undefined ? String(defaultValue) : '');
1110
+ const [isFocused, setIsFocused] = useState(false);
1070
1111
  const value = controlledValue !== undefined ? controlledValue : internalValue;
1071
1112
  const sz = sizeClasses$d[size];
1072
1113
  const clamp = (v) => {
@@ -1105,7 +1146,9 @@ const NumberInput = React.forwardRef(({ value: controlledValue, defaultValue, on
1105
1146
  onChange?.(parsed);
1106
1147
  }
1107
1148
  };
1149
+ const handleFocus = useCallback(() => setIsFocused(true), []);
1108
1150
  const handleBlur = () => {
1151
+ setIsFocused(false);
1109
1152
  if (value !== undefined)
1110
1153
  commit(clamp(value));
1111
1154
  else
@@ -1139,12 +1182,12 @@ const NumberInput = React.forwardRef(({ value: controlledValue, defaultValue, on
1139
1182
  if (!isDigit && !isMinus && !isDot)
1140
1183
  e.preventDefault();
1141
1184
  };
1142
- const displayValue = document.activeElement?.id === id
1185
+ const displayValue = isFocused
1143
1186
  ? inputStr
1144
1187
  : value !== undefined
1145
1188
  ? (formatValue ? formatValue(value) : String(value))
1146
1189
  : '';
1147
- return (jsxs("div", { className: cn('flex flex-col gap-1', fullWidth && 'w-full', containerClassName), children: [label && (jsxs("label", { htmlFor: id, className: "text-sm font-medium text-text", children: [label, required && jsx("span", { className: "text-error ml-1", children: "*" })] })), jsxs("div", { className: cn('relative flex items-center', fullWidth && 'w-full'), children: [prefix && (jsx("span", { className: "absolute left-0 flex items-center justify-center h-full px-2.5 border-r border-border bg-surface-hover rounded-l-md text-sm text-text-muted select-none", children: prefix })), jsx("input", { ref: ref, id: id, type: "text", inputMode: "decimal", value: displayValue, onChange: handleChange, onBlur: handleBlur, onKeyDown: handleKeyDown, disabled: disabled, required: required, "aria-invalid": !!error, className: cn('w-full rounded-md border bg-surface text-text placeholder:text-text-muted', 'transition-colors outline-none', 'focus:border-border-focus focus:ring-2 focus:ring-border-focus/20', 'disabled:opacity-50 disabled:cursor-not-allowed', error ? 'border-error focus:border-error focus:ring-error/20' : 'border-border', sz.input, prefix && 'pl-10', (suffix || !hideControls) && 'pr-10', className), ...props }), suffix && hideControls && (jsx("span", { className: "absolute right-0 flex items-center justify-center h-full px-2.5 border-l border-border bg-surface-hover rounded-r-md text-sm text-text-muted select-none", children: suffix })), !hideControls && (jsxs("div", { className: "absolute right-0 flex flex-col h-full border-l border-border rounded-r-md overflow-hidden", children: [jsx("button", { type: "button", tabIndex: -1, onClick: increment, disabled: disabled || (max !== undefined && (value ?? -Infinity) >= max), className: cn('flex items-center justify-center flex-1 bg-surface-hover hover:bg-surface-active transition-colors disabled:opacity-40 disabled:cursor-not-allowed', sz.btn), "aria-label": "Increment", children: jsx(ChevronUp, { size: 10 }) }), jsx("button", { type: "button", tabIndex: -1, onClick: decrement, disabled: disabled || (min !== undefined && (value ?? Infinity) <= min), className: cn('flex items-center justify-center flex-1 bg-surface-hover hover:bg-surface-active transition-colors border-t border-border disabled:opacity-40 disabled:cursor-not-allowed', sz.btn), "aria-label": "Decrement", children: jsx(ChevronDown, { size: 10 }) })] }))] }), jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { children: [error && jsx("p", { className: "text-xs text-error", role: "alert", children: error }), !error && helperText && jsx("p", { className: "text-xs text-text-muted", children: helperText })] }), showMaxLength && props.maxLength !== undefined && (jsxs("p", { className: cn('text-xs text-text-muted ml-auto', displayValue.length >= (props.maxLength ?? Infinity) && 'text-error'), children: [displayValue.length, "/", props.maxLength] }))] })] }));
1190
+ return (jsxs("div", { className: cn('flex flex-col gap-1', fullWidth && 'w-full', containerClassName), children: [label && (jsxs("label", { htmlFor: id, className: "text-sm font-medium text-text", children: [label, required && jsx("span", { className: "text-error ml-1", children: "*" })] })), jsxs("div", { className: cn('relative flex items-center', fullWidth && 'w-full'), children: [prefix && (jsx("span", { className: "absolute left-0 flex items-center justify-center h-full px-2.5 border-r border-border bg-surface-hover rounded-l-md text-sm text-text-muted select-none", children: prefix })), jsx("input", { ref: ref, id: id, type: "text", inputMode: "decimal", value: displayValue, onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, disabled: disabled, required: required, "aria-invalid": !!error, className: cn('w-full rounded-md border bg-surface text-text placeholder:text-text-muted', 'transition-colors outline-none', 'focus:border-border-focus focus:ring-2 focus:ring-border-focus/20', 'disabled:opacity-50 disabled:cursor-not-allowed', error ? 'border-error focus:border-error focus:ring-error/20' : 'border-border', sz.input, prefix && 'pl-10', (suffix || !hideControls) && 'pr-10', className), ...props }), suffix && hideControls && (jsx("span", { className: "absolute right-0 flex items-center justify-center h-full px-2.5 border-l border-border bg-surface-hover rounded-r-md text-sm text-text-muted select-none", children: suffix })), !hideControls && (jsxs("div", { className: "absolute right-0 flex flex-col h-full border-l border-border rounded-r-md overflow-hidden", children: [jsx("button", { type: "button", tabIndex: -1, onClick: increment, disabled: disabled || (max !== undefined && (value ?? -Infinity) >= max), className: cn('flex items-center justify-center flex-1 bg-surface-hover hover:bg-surface-active transition-colors disabled:opacity-40 disabled:cursor-not-allowed', sz.btn), "aria-label": "Increment", children: jsx(ChevronUp, { size: 10 }) }), jsx("button", { type: "button", tabIndex: -1, onClick: decrement, disabled: disabled || (min !== undefined && (value ?? Infinity) <= min), className: cn('flex items-center justify-center flex-1 bg-surface-hover hover:bg-surface-active transition-colors border-t border-border disabled:opacity-40 disabled:cursor-not-allowed', sz.btn), "aria-label": "Decrement", children: jsx(ChevronDown, { size: 10 }) })] }))] }), jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { children: [error && jsx("p", { className: "text-xs text-error", role: "alert", children: error }), !error && helperText && jsx("p", { className: "text-xs text-text-muted", children: helperText })] }), showMaxLength && props.maxLength !== undefined && (jsxs("p", { className: cn('text-xs text-text-muted ml-auto', displayValue.length >= (props.maxLength ?? Infinity) && 'text-error'), children: [displayValue.length, "/", props.maxLength] }))] })] }));
1148
1191
  });
1149
1192
  NumberInput.displayName = 'NumberInput';
1150
1193
 
@@ -1196,6 +1239,16 @@ const PhoneInput = React.forwardRef(({ value: controlledValue, defaultCountry =
1196
1239
  const defaultCC = countryCodes.find((c) => c.code === defaultCountry) ?? countryCodes[0];
1197
1240
  const [selectedCountry, setSelectedCountry] = useState(controlledValue?.countryCode ?? defaultCC);
1198
1241
  const [phoneNumber, setPhoneNumber] = useState(controlledValue?.number ?? '');
1242
+ useEffect(() => {
1243
+ if (controlledValue?.countryCode) {
1244
+ setSelectedCountry(controlledValue.countryCode);
1245
+ }
1246
+ }, [controlledValue?.countryCode]);
1247
+ useEffect(() => {
1248
+ if (controlledValue?.number !== undefined) {
1249
+ setPhoneNumber(controlledValue.number);
1250
+ }
1251
+ }, [controlledValue?.number]);
1199
1252
  const activeCountry = controlledValue?.countryCode ?? selectedCountry;
1200
1253
  const activeNumber = controlledValue?.number ?? phoneNumber;
1201
1254
  const buildValue = (country, number) => ({
@@ -1243,10 +1296,12 @@ function OTPInput({ length = 6, value: controlledValue, onChange, onComplete, si
1243
1296
  const id = useId();
1244
1297
  const [internalValue, setInternalValue] = useState('');
1245
1298
  const inputsRef = useRef([]);
1246
- const value = controlledValue !== undefined ? controlledValue : internalValue;
1299
+ const isControlled = controlledValue !== undefined;
1300
+ const value = isControlled ? controlledValue : internalValue;
1247
1301
  const digits = value.split('').slice(0, length);
1248
1302
  const update = (newVal) => {
1249
- setInternalValue(newVal);
1303
+ if (!isControlled)
1304
+ setInternalValue(newVal);
1250
1305
  onChange?.(newVal);
1251
1306
  if (newVal.length === length)
1252
1307
  onComplete?.(newVal);
@@ -1321,7 +1376,7 @@ function Rating({ value: controlledValue, defaultValue = 0, onChange, max = 5, s
1321
1376
  return (jsxs("div", { className: cn('flex flex-col gap-1', className), children: [label && jsx("span", { className: "text-sm font-medium text-text", children: label }), jsx("div", { className: cn('flex items-center gap-0.5', interactive && 'cursor-pointer'), onMouseLeave: () => interactive && setHoverValue(null), role: interactive ? 'slider' : 'img', "aria-label": `Rating: ${value} out of ${max}`, "aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max, children: Array.from({ length: max }, (_, i) => {
1322
1377
  const star = i + 1;
1323
1378
  const fill = getStarFill(star);
1324
- return (jsx("button", { type: "button", onClick: () => handleClick(star), onMouseEnter: () => interactive && setHoverValue(star), disabled: disabled, className: cn('relative transition-transform focus:outline-none', interactive && 'hover:scale-110', disabled && 'cursor-not-allowed opacity-50'), "aria-label": `Rate ${star} out of ${max}`, tabIndex: interactive ? 0 : -1, children: fill === 'empty' ? (emptyIcon ?? (jsx(Star, { size: iconSize, className: "text-border fill-transparent transition-colors" }))) : fill === 'full' ? (icon ?? (jsx(Star, { size: iconSize, className: cn('transition-colors', hoverValue !== null ? 'text-warning fill-warning' : 'text-warning fill-warning') }))) : (jsxs("span", { className: "relative inline-block", children: [jsx(Star, { size: iconSize, className: "text-border fill-transparent" }), jsx("span", { className: "absolute inset-0 overflow-hidden w-1/2", children: jsx(Star, { size: iconSize, className: "text-warning fill-warning" }) })] })) }, star));
1379
+ return (jsx("button", { type: "button", onClick: () => handleClick(star), onMouseEnter: () => interactive && setHoverValue(star), disabled: disabled, className: cn('relative transition-transform focus:outline-none', interactive && 'hover:scale-110', disabled && 'cursor-not-allowed opacity-50'), "aria-label": `Rate ${star} out of ${max}`, tabIndex: interactive ? 0 : -1, children: fill === 'empty' ? (emptyIcon ?? (jsx(Star, { size: iconSize, className: "text-border fill-transparent transition-colors" }))) : fill === 'full' ? (icon ?? (jsx(Star, { size: iconSize, className: "text-warning fill-warning transition-colors" }))) : (jsxs("span", { className: "relative inline-block", children: [jsx(Star, { size: iconSize, className: "text-border fill-transparent" }), jsx("span", { className: "absolute inset-0 overflow-hidden w-1/2", children: jsx(Star, { size: iconSize, className: "text-warning fill-warning" }) })] })) }, star));
1325
1380
  }) }), helperText && jsx("p", { className: "text-xs text-text-muted", children: helperText })] }));
1326
1381
  }
1327
1382
 
@@ -1365,10 +1420,13 @@ function MultiSelect({ options = [], groups = [], value: controlledValue, defaul
1365
1420
  update([]);
1366
1421
  };
1367
1422
  useEffect(() => {
1368
- if (open)
1369
- setTimeout(() => searchRef.current?.focus(), 10);
1370
- else
1423
+ if (open) {
1424
+ const raf = requestAnimationFrame(() => searchRef.current?.focus());
1425
+ return () => cancelAnimationFrame(raf);
1426
+ }
1427
+ else {
1371
1428
  setSearch('');
1429
+ }
1372
1430
  }, [open]);
1373
1431
  useEffect(() => {
1374
1432
  const handler = (e) => {
@@ -1567,6 +1625,41 @@ function FormField({ label, helperText, error, required, disabled, htmlFor, chil
1567
1625
  return (jsxs("div", { className: cn('flex flex-col gap-1', disabled && 'opacity-60', className), children: [label && (jsxs("label", { htmlFor: id, className: "text-sm font-medium text-text", children: [label, required && jsx("span", { className: "text-error ml-1", "aria-hidden": "true", children: "*" })] })), children, error && (jsx("p", { className: "text-xs text-error", role: "alert", children: error })), !error && helperText && (jsx("p", { className: "text-xs text-text-muted", children: helperText }))] }));
1568
1626
  }
1569
1627
 
1628
+ function buildRules(field) {
1629
+ if (field.validator) {
1630
+ const compiled = field.validator.compile();
1631
+ return {
1632
+ required: field.required
1633
+ ? `${field.label ?? field.name} is required`
1634
+ : compiled.required,
1635
+ minLength: compiled.minLength,
1636
+ maxLength: compiled.maxLength,
1637
+ min: compiled.min,
1638
+ max: compiled.max,
1639
+ pattern: compiled.pattern,
1640
+ validate: compiled.validate,
1641
+ };
1642
+ }
1643
+ return {
1644
+ required: field.required ? `${field.label ?? field.name} is required` : false,
1645
+ minLength: field.validation?.minLength
1646
+ ? { value: field.validation.minLength, message: `Minimum ${field.validation.minLength} characters` }
1647
+ : undefined,
1648
+ maxLength: field.validation?.maxLength
1649
+ ? { value: field.validation.maxLength, message: `Maximum ${field.validation.maxLength} characters` }
1650
+ : undefined,
1651
+ min: field.validation?.min
1652
+ ? { value: field.validation.min, message: `Minimum value is ${field.validation.min}` }
1653
+ : undefined,
1654
+ max: field.validation?.max
1655
+ ? { value: field.validation.max, message: `Maximum value is ${field.validation.max}` }
1656
+ : undefined,
1657
+ pattern: field.validation?.pattern
1658
+ ? { value: field.validation.pattern, message: 'Invalid format' }
1659
+ : undefined,
1660
+ validate: field.validation?.validate,
1661
+ };
1662
+ }
1570
1663
  function JSONForm({ schema, defaultValues, onSubmit, onCancel, submitLabel = 'Submit', cancelLabel = 'Cancel', loading = false, columns = 1, className, actionsClassName, }) {
1571
1664
  const { control, handleSubmit, formState: { errors }, } = useForm({
1572
1665
  defaultValues: defaultValues ?? Object.fromEntries(schema.map((f) => [f.name, f.defaultValue ?? ''])),
@@ -1587,41 +1680,7 @@ function JSONForm({ schema, defaultValues, onSubmit, onCancel, submitLabel = 'Su
1587
1680
  }
1588
1681
  const errorMsg = errors[field.name]?.message;
1589
1682
  const colSpan = field.colSpan ?? 1;
1590
- return (jsx("div", { className: cn(colSpanClasses[colSpan]), children: jsx(Controller, { name: field.name, control: control, rules: (() => {
1591
- if (field.validator) {
1592
- const compiled = field.validator.compile();
1593
- return {
1594
- required: field.required
1595
- ? `${field.label ?? field.name} is required`
1596
- : compiled.required,
1597
- minLength: compiled.minLength,
1598
- maxLength: compiled.maxLength,
1599
- min: compiled.min,
1600
- max: compiled.max,
1601
- pattern: compiled.pattern,
1602
- validate: compiled.validate,
1603
- };
1604
- }
1605
- return {
1606
- required: field.required ? `${field.label ?? field.name} is required` : false,
1607
- minLength: field.validation?.minLength
1608
- ? { value: field.validation.minLength, message: `Minimum ${field.validation.minLength} characters` }
1609
- : undefined,
1610
- maxLength: field.validation?.maxLength
1611
- ? { value: field.validation.maxLength, message: `Maximum ${field.validation.maxLength} characters` }
1612
- : undefined,
1613
- min: field.validation?.min
1614
- ? { value: field.validation.min, message: `Minimum value is ${field.validation.min}` }
1615
- : undefined,
1616
- max: field.validation?.max
1617
- ? { value: field.validation.max, message: `Maximum value is ${field.validation.max}` }
1618
- : undefined,
1619
- pattern: field.validation?.pattern
1620
- ? { value: field.validation.pattern, message: 'Invalid format' }
1621
- : undefined,
1622
- validate: field.validation?.validate,
1623
- };
1624
- })(), render: ({ field: f }) => {
1683
+ return (jsx("div", { className: cn(colSpanClasses[colSpan]), children: jsx(Controller, { name: field.name, control: control, rules: buildRules(field), render: ({ field: f }) => {
1625
1684
  const commonProps = {
1626
1685
  label: field.label,
1627
1686
  helperText: field.helperText,
@@ -2296,18 +2355,23 @@ function formatBytes(bytes) {
2296
2355
  const i = Math.floor(Math.log(bytes) / Math.log(k));
2297
2356
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
2298
2357
  }
2299
- let fileIdCounter = 0;
2358
+ function generateFileId() {
2359
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
2360
+ return crypto.randomUUID();
2361
+ }
2362
+ return `file-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
2363
+ }
2300
2364
  function FileUpload({ accept, maxFiles, maxSize, multiple = true, disabled = false, label, helperText, error, files = [], onFilesChange, onFileRemove, showPreview = true, className, }) {
2301
2365
  const onDrop = useCallback((acceptedFiles, rejectedFiles) => {
2302
2366
  const newFiles = acceptedFiles.map((file) => ({
2303
2367
  file,
2304
- id: `file-${++fileIdCounter}`,
2368
+ id: generateFileId(),
2305
2369
  preview: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,
2306
2370
  progress: 0,
2307
2371
  }));
2308
2372
  const rejectedMapped = rejectedFiles.map(({ file, errors }) => ({
2309
2373
  file,
2310
- id: `file-${++fileIdCounter}`,
2374
+ id: generateFileId(),
2311
2375
  error: errors[0]?.message ?? 'File rejected',
2312
2376
  }));
2313
2377
  onFilesChange?.([...files, ...newFiles, ...rejectedMapped]);
@@ -2468,7 +2532,7 @@ function AvatarGroup({ avatars, max = 4, size = 'md', className }) {
2468
2532
  const visible = avatars.slice(0, max);
2469
2533
  const overflow = avatars.length - max;
2470
2534
  const sz = sizeClasses$8[size];
2471
- return (jsxs("div", { className: cn('flex items-center', className), children: [visible.map((avatar, i) => (jsx("div", { className: "-ml-2 first:ml-0 ring-2 ring-surface rounded-full", children: jsx(Avatar, { ...avatar, size: size }) }, i))), overflow > 0 && (jsxs("div", { className: cn('-ml-2 flex items-center justify-center rounded-full bg-surface-hover border-2 border-surface text-text-muted font-medium', sz.container, sz.text), children: ["+", overflow] }))] }));
2535
+ return (jsxs("div", { className: cn('flex items-center', className), children: [visible.map((avatar, i) => (jsx("div", { className: "-ml-2 first:ml-0 ring-2 ring-surface rounded-full", children: jsx(Avatar, { ...avatar, size: size }) }, avatar.src ?? avatar.name ?? i))), overflow > 0 && (jsxs("div", { className: cn('-ml-2 flex items-center justify-center rounded-full bg-surface-hover border-2 border-surface text-text-muted font-medium', sz.container, sz.text), children: ["+", overflow] }))] }));
2472
2536
  }
2473
2537
 
2474
2538
  const badgeVariants = cva('inline-flex items-center justify-center font-medium rounded-full select-none', {
@@ -2563,6 +2627,7 @@ function SVG({ src, children, width, height, size, color, className, title, role
2563
2627
  return (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: resolvedWidth, height: resolvedHeight, viewBox: viewBox, fill: fill, stroke: stroke, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", role: role, className: cn('flex-shrink-0', className), style: { color }, "aria-label": props['aria-label'], "aria-hidden": props['aria-hidden'], children: [title && jsx("title", { children: title }), children] }));
2564
2628
  }
2565
2629
 
2630
+ const SKELETON_WIDTHS = [65, 80, 55, 75, 90, 60, 70, 85, 50, 78];
2566
2631
  function Table({ columns, data, keyExtractor, selectable = false, selectedKeys = [], onSelectionChange, sortKey, sortDirection, onSortChange, loading = false, emptyMessage = 'No data available', stickyHeader = false, striped = false, hoverable = true, bordered = false, compact = false, className, }) {
2567
2632
  const [internalSort, setInternalSort] = useState({
2568
2633
  key: '',
@@ -2615,7 +2680,7 @@ function Table({ columns, data, keyExtractor, selectable = false, selectedKeys =
2615
2680
  }
2616
2681
  };
2617
2682
  const cellPad = compact ? 'px-3 py-2' : 'px-4 py-3';
2618
- return (jsx("div", { className: cn('w-full overflow-auto rounded-lg', bordered && 'border border-border', className), children: jsxs("table", { className: "w-full text-sm border-collapse", children: [jsx("thead", { className: cn(stickyHeader && 'sticky top-0 z-10'), children: jsxs("tr", { className: "bg-surface-hover border-b border-border", children: [selectable && (jsx("th", { className: cn('w-10', cellPad), children: jsx(Checkbox, { checked: allSelected ? true : someSelected ? 'indeterminate' : false, onCheckedChange: toggleAll, "aria-label": "Select all rows" }) })), columns.map((col) => (jsx("th", { className: cn('text-left font-semibold text-text-muted whitespace-nowrap', cellPad, col.align === 'center' && 'text-center', col.align === 'right' && 'text-right', col.sortable && 'cursor-pointer select-none hover:text-text', col.className), style: { width: col.width }, onClick: col.sortable ? () => handleSort(col.key) : undefined, children: jsxs("span", { className: "inline-flex items-center gap-1", children: [col.header, col.sortable && (jsx("span", { className: "flex-shrink-0", children: effectiveSortKey === col.key ? (effectiveSortDir === 'asc' ? (jsx(ChevronUp, { size: 14 })) : effectiveSortDir === 'desc' ? (jsx(ChevronDown, { size: 14 })) : (jsx(ChevronsUpDown, { size: 14, className: "opacity-40" }))) : (jsx(ChevronsUpDown, { size: 14, className: "opacity-40" })) }))] }) }, col.key)))] }) }), jsx("tbody", { children: loading ? (Array.from({ length: 5 }).map((_, i) => (jsxs("tr", { className: "border-b border-border", children: [selectable && jsx("td", { className: cellPad, children: jsx("div", { className: "h-4 w-4 rounded bg-surface-hover animate-pulse" }) }), columns.map((col) => (jsx("td", { className: cellPad, children: jsx("div", { className: "h-4 rounded bg-surface-hover animate-pulse", style: { width: `${60 + Math.random() * 40}%` } }) }, col.key)))] }, i)))) : sortedData.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: columns.length + (selectable ? 1 : 0), className: "text-center py-12 text-text-muted", children: emptyMessage }) })) : (sortedData.map((row, i) => {
2683
+ return (jsx("div", { className: cn('w-full overflow-auto rounded-lg', bordered && 'border border-border', className), children: jsxs("table", { className: "w-full text-sm border-collapse", children: [jsx("thead", { className: cn(stickyHeader && 'sticky top-0 z-10'), children: jsxs("tr", { className: "bg-surface-hover border-b border-border", children: [selectable && (jsx("th", { className: cn('w-10', cellPad), children: jsx(Checkbox, { checked: allSelected ? true : someSelected ? 'indeterminate' : false, onCheckedChange: toggleAll, "aria-label": "Select all rows" }) })), columns.map((col) => (jsx("th", { className: cn('text-left font-semibold text-text-muted whitespace-nowrap', cellPad, col.align === 'center' && 'text-center', col.align === 'right' && 'text-right', col.sortable && 'cursor-pointer select-none hover:text-text', col.className), style: { width: col.width }, onClick: col.sortable ? () => handleSort(col.key) : undefined, children: jsxs("span", { className: "inline-flex items-center gap-1", children: [col.header, col.sortable && (jsx("span", { className: "flex-shrink-0", children: effectiveSortKey === col.key ? (effectiveSortDir === 'asc' ? (jsx(ChevronUp, { size: 14 })) : effectiveSortDir === 'desc' ? (jsx(ChevronDown, { size: 14 })) : (jsx(ChevronsUpDown, { size: 14, className: "opacity-40" }))) : (jsx(ChevronsUpDown, { size: 14, className: "opacity-40" })) }))] }) }, col.key)))] }) }), jsx("tbody", { children: loading ? (Array.from({ length: 5 }).map((_, i) => (jsxs("tr", { className: "border-b border-border", children: [selectable && jsx("td", { className: cellPad, children: jsx("div", { className: "h-4 w-4 rounded bg-surface-hover animate-pulse" }) }), columns.map((col, ci) => (jsx("td", { className: cellPad, children: jsx("div", { className: "h-4 rounded bg-surface-hover animate-pulse", style: { width: `${SKELETON_WIDTHS[(i * columns.length + ci) % SKELETON_WIDTHS.length]}%` } }) }, col.key)))] }, `skeleton-row-${i}`)))) : sortedData.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: columns.length + (selectable ? 1 : 0), className: "text-center py-12 text-text-muted", children: emptyMessage }) })) : (sortedData.map((row, i) => {
2619
2684
  const key = keyExtractor(row, i);
2620
2685
  const isSelected = selectedKeys.includes(key);
2621
2686
  return (jsxs("tr", { className: cn('border-b border-border transition-colors', striped && i % 2 === 1 && 'bg-surface-hover/50', hoverable && 'hover:bg-surface-hover', isSelected && 'bg-primary/5'), children: [selectable && (jsx("td", { className: cellPad, children: jsx(Checkbox, { checked: isSelected, onCheckedChange: () => toggleRow(key), "aria-label": `Select row ${i + 1}` }) })), columns.map((col) => (jsx("td", { className: cn('text-text', cellPad, col.align === 'center' && 'text-center', col.align === 'right' && 'text-right', col.className), children: col.accessor
@@ -3150,21 +3215,24 @@ function Popover({ content, children, placement = 'bottom', open, defaultOpen, o
3150
3215
  }
3151
3216
 
3152
3217
  const itemBase = cn('flex items-center gap-2 px-2 py-1.5 text-sm rounded-md cursor-pointer outline-none select-none', 'transition-colors', 'data-[highlighted]:bg-surface-hover data-[highlighted]:text-text', 'data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none');
3218
+ function stableKey(item, i) {
3219
+ return `${item.type ?? 'item'}-${item.label ?? ''}-${i}`;
3220
+ }
3153
3221
  function renderItems(items) {
3154
3222
  return items.map((item, i) => {
3155
3223
  if (item.type === 'separator') {
3156
- return jsx(RadixContextMenu.Separator, { className: "my-1 h-px bg-border" }, i);
3224
+ return jsx(RadixContextMenu.Separator, { className: "my-1 h-px bg-border" }, `separator-${i}`);
3157
3225
  }
3158
3226
  if (item.type === 'label') {
3159
- return (jsx(RadixContextMenu.Label, { className: "px-2 py-1 text-xs font-semibold text-text-muted uppercase tracking-wide", children: item.label }, i));
3227
+ return (jsx(RadixContextMenu.Label, { className: "px-2 py-1 text-xs font-semibold text-text-muted uppercase tracking-wide", children: item.label }, stableKey(item, i)));
3160
3228
  }
3161
3229
  if (item.type === 'checkbox') {
3162
- return (jsxs(RadixContextMenu.CheckboxItem, { checked: item.checked, onCheckedChange: item.onCheckedChange, disabled: item.disabled, className: cn(itemBase, 'pl-7 relative', item.destructive && 'text-error'), children: [jsx(RadixContextMenu.ItemIndicator, { className: "absolute left-2", children: jsx(Check, { size: 12 }) }), item.label] }, i));
3230
+ return (jsxs(RadixContextMenu.CheckboxItem, { checked: item.checked, onCheckedChange: item.onCheckedChange, disabled: item.disabled, className: cn(itemBase, 'pl-7 relative', item.destructive && 'text-error'), children: [jsx(RadixContextMenu.ItemIndicator, { className: "absolute left-2", children: jsx(Check, { size: 12 }) }), item.label] }, stableKey(item, i)));
3163
3231
  }
3164
3232
  if (item.children && item.children.length > 0) {
3165
- return (jsxs(RadixContextMenu.Sub, { children: [jsxs(RadixContextMenu.SubTrigger, { disabled: item.disabled, className: cn(itemBase, item.destructive && 'text-error'), children: [item.icon && jsx("span", { className: "w-4 h-4 flex items-center", children: item.icon }), jsx("span", { className: "flex-1", children: item.label }), jsx(ChevronRight, { size: 12, className: "ml-auto text-text-muted" })] }), jsx(RadixContextMenu.Portal, { children: jsx(RadixContextMenu.SubContent, { className: "z-dropdown bg-surface border border-border rounded-lg shadow-lg p-1 min-w-[10rem]", sideOffset: 2, alignOffset: -4, children: renderItems(item.children) }) })] }, i));
3233
+ return (jsxs(RadixContextMenu.Sub, { children: [jsxs(RadixContextMenu.SubTrigger, { disabled: item.disabled, className: cn(itemBase, item.destructive && 'text-error'), children: [item.icon && jsx("span", { className: "w-4 h-4 flex items-center", children: item.icon }), jsx("span", { className: "flex-1", children: item.label }), jsx(ChevronRight, { size: 12, className: "ml-auto text-text-muted" })] }), jsx(RadixContextMenu.Portal, { children: jsx(RadixContextMenu.SubContent, { className: "z-dropdown bg-surface border border-border rounded-lg shadow-lg p-1 min-w-[10rem]", sideOffset: 2, alignOffset: -4, children: renderItems(item.children) }) })] }, stableKey(item, i)));
3166
3234
  }
3167
- return (jsxs(RadixContextMenu.Item, { disabled: item.disabled, onSelect: item.onClick, className: cn(itemBase, item.destructive ? 'text-error' : 'text-text'), children: [item.icon && jsx("span", { className: "w-4 h-4 flex items-center", children: item.icon }), jsx("span", { className: "flex-1", children: item.label }), item.shortcut && (jsx("span", { className: "ml-auto text-xs text-text-muted", children: item.shortcut }))] }, i));
3235
+ return (jsxs(RadixContextMenu.Item, { disabled: item.disabled, onSelect: item.onClick, className: cn(itemBase, item.destructive ? 'text-error' : 'text-text'), children: [item.icon && jsx("span", { className: "w-4 h-4 flex items-center", children: item.icon }), jsx("span", { className: "flex-1", children: item.label }), item.shortcut && (jsx("span", { className: "ml-auto text-xs text-text-muted", children: item.shortcut }))] }, stableKey(item, i)));
3168
3236
  });
3169
3237
  }
3170
3238
  function ContextMenu({ items, children, className }) {
@@ -3204,6 +3272,7 @@ const variantConfig = {
3204
3272
  },
3205
3273
  };
3206
3274
  function ConfirmDialogProvider({ children }) {
3275
+ const descId = useId();
3207
3276
  const [state, setState] = useState({
3208
3277
  open: false,
3209
3278
  resolve: null,
@@ -3228,12 +3297,13 @@ function ConfirmDialogProvider({ children }) {
3228
3297
  const confirmLabel = state.confirmLabel ?? config.defaultConfirm;
3229
3298
  const cancelLabel = state.cancelLabel ?? 'Cancel';
3230
3299
  return (jsxs(ConfirmDialogContext.Provider, { value: { confirm }, children: [children, jsx(RadixDialog.Root, { open: state.open, onOpenChange: (open) => { if (!open)
3231
- handleResponse(false); }, children: jsxs(RadixDialog.Portal, { children: [jsx(RadixDialog.Overlay, { className: "fixed inset-0 z-[1000] bg-black/40 backdrop-blur-sm animate-in fade-in-0" }), jsxs(RadixDialog.Content, { className: cn('fixed z-[1001] top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2', 'w-full max-w-md bg-surface rounded-xl shadow-2xl border border-border p-6', 'animate-in fade-in-0 zoom-in-95'), "aria-describedby": description ? 'confirm-desc' : undefined, children: [jsxs("div", { className: "flex items-start gap-4", children: [jsx("div", { className: cn('flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center', config.iconBg), children: config.icon }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx(RadixDialog.Title, { className: "text-base font-semibold text-text", children: title }), description && (jsx("p", { id: "confirm-desc", className: "mt-1 text-sm text-text-muted", children: description }))] }), jsx("button", { onClick: () => handleResponse(false), className: "flex-shrink-0 text-text-muted hover:text-text transition-colors focus:outline-none", "aria-label": "Close", children: jsx(X, { size: 16 }) })] }), jsxs("div", { className: "flex items-center justify-end gap-3 mt-6", children: [jsx(Button, { variant: "outline", size: "sm", onClick: () => handleResponse(false), children: cancelLabel }), jsx(Button, { variant: config.confirmVariant, size: "sm", className: config.confirmClassName, onClick: () => handleResponse(true), children: confirmLabel })] })] })] }) })] }));
3300
+ handleResponse(false); }, children: jsxs(RadixDialog.Portal, { children: [jsx(RadixDialog.Overlay, { className: "fixed inset-0 z-[1000] bg-black/40 backdrop-blur-sm animate-in fade-in-0" }), jsxs(RadixDialog.Content, { className: cn('fixed z-[1001] top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2', 'w-full max-w-md bg-surface rounded-xl shadow-2xl border border-border p-6', 'animate-in fade-in-0 zoom-in-95'), "aria-describedby": description ? descId : undefined, children: [jsxs("div", { className: "flex items-start gap-4", children: [jsx("div", { className: cn('flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center', config.iconBg), children: config.icon }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx(RadixDialog.Title, { className: "text-base font-semibold text-text", children: title }), description && (jsx("p", { id: descId, className: "mt-1 text-sm text-text-muted", children: description }))] }), jsx("button", { onClick: () => handleResponse(false), className: "flex-shrink-0 text-text-muted hover:text-text transition-colors focus:outline-none", "aria-label": "Close", children: jsx(X, { size: 16 }) })] }), jsxs("div", { className: "flex items-center justify-end gap-3 mt-6", children: [jsx(Button, { variant: "outline", size: "sm", onClick: () => handleResponse(false), children: cancelLabel }), jsx(Button, { variant: config.confirmVariant, size: "sm", className: config.confirmClassName, onClick: () => handleResponse(true), children: confirmLabel })] })] })] }) })] }));
3232
3301
  }
3233
3302
  function ConfirmDialog({ open, onConfirm, onCancel, title, description, confirmLabel, cancelLabel, variant = 'default', }) {
3303
+ const staticDescId = useId();
3234
3304
  const config = variantConfig[variant];
3235
3305
  return (jsx(RadixDialog.Root, { open: open, onOpenChange: (o) => { if (!o)
3236
- onCancel(); }, children: jsxs(RadixDialog.Portal, { children: [jsx(RadixDialog.Overlay, { className: "fixed inset-0 z-[1000] bg-black/40 backdrop-blur-sm animate-in fade-in-0" }), jsxs(RadixDialog.Content, { className: cn('fixed z-[1001] top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2', 'w-full max-w-md bg-surface rounded-xl shadow-2xl border border-border p-6', 'animate-in fade-in-0 zoom-in-95'), "aria-describedby": description ? 'confirm-desc-static' : undefined, children: [jsxs("div", { className: "flex items-start gap-4", children: [jsx("div", { className: cn('flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center', config.iconBg), children: config.icon }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx(RadixDialog.Title, { className: "text-base font-semibold text-text", children: title ?? config.defaultTitle }), description && (jsx("p", { id: "confirm-desc-static", className: "mt-1 text-sm text-text-muted", children: description }))] }), jsx("button", { onClick: onCancel, className: "flex-shrink-0 text-text-muted hover:text-text transition-colors focus:outline-none", "aria-label": "Close", children: jsx(X, { size: 16 }) })] }), jsxs("div", { className: "flex items-center justify-end gap-3 mt-6", children: [jsx(Button, { variant: "outline", size: "sm", onClick: onCancel, children: cancelLabel ?? 'Cancel' }), jsx(Button, { variant: config.confirmVariant, size: "sm", className: config.confirmClassName, onClick: onConfirm, children: confirmLabel ?? config.defaultConfirm })] })] })] }) }));
3306
+ onCancel(); }, children: jsxs(RadixDialog.Portal, { children: [jsx(RadixDialog.Overlay, { className: "fixed inset-0 z-[1000] bg-black/40 backdrop-blur-sm animate-in fade-in-0" }), jsxs(RadixDialog.Content, { className: cn('fixed z-[1001] top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2', 'w-full max-w-md bg-surface rounded-xl shadow-2xl border border-border p-6', 'animate-in fade-in-0 zoom-in-95'), "aria-describedby": description ? staticDescId : undefined, children: [jsxs("div", { className: "flex items-start gap-4", children: [jsx("div", { className: cn('flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center', config.iconBg), children: config.icon }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx(RadixDialog.Title, { className: "text-base font-semibold text-text", children: title ?? config.defaultTitle }), description && (jsx("p", { id: staticDescId, className: "mt-1 text-sm text-text-muted", children: description }))] }), jsx("button", { onClick: onCancel, className: "flex-shrink-0 text-text-muted hover:text-text transition-colors focus:outline-none", "aria-label": "Close", children: jsx(X, { size: 16 }) })] }), jsxs("div", { className: "flex items-center justify-end gap-3 mt-6", children: [jsx(Button, { variant: "outline", size: "sm", onClick: onCancel, children: cancelLabel ?? 'Cancel' }), jsx(Button, { variant: config.confirmVariant, size: "sm", className: config.confirmClassName, onClick: onConfirm, children: confirmLabel ?? config.defaultConfirm })] })] })] }) }));
3237
3307
  }
3238
3308
 
3239
3309
  const sizeClasses$4 = {
@@ -3333,11 +3403,15 @@ function CommandPalette({ open, onClose, items = [], groups = [], placeholder =
3333
3403
  return null;
3334
3404
  if (typeof document === 'undefined')
3335
3405
  return null;
3336
- let globalIndex = 0;
3406
+ // Build a flat ordered list for index-based rendering — stable, not mutated during render
3407
+ const orderedItems = [
3408
+ ...filteredItems,
3409
+ ...filteredGroups.flatMap((g) => g.items),
3410
+ ];
3337
3411
  const renderItem = (item) => {
3338
- const idx = globalIndex++;
3412
+ const idx = orderedItems.indexOf(item);
3339
3413
  const isActive = idx === activeIndex;
3340
- return (jsxs("div", { "data-active": isActive, role: "option", "aria-selected": isActive, "aria-disabled": item.disabled, onClick: () => !item.disabled && handleSelect(item), onMouseEnter: () => setActiveIndex(idx), className: cn('flex items-center gap-3 px-3 py-2.5 rounded-md cursor-pointer transition-colors', isActive ? 'bg-primary text-primary-foreground' : 'text-text hover:bg-surface-hover', item.disabled && 'opacity-40 cursor-not-allowed pointer-events-none'), children: [item.icon && (jsx("span", { className: cn('flex-shrink-0', isActive ? 'text-primary-foreground' : 'text-text-muted'), children: item.icon })), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("div", { className: "text-sm font-medium truncate", children: highlight(item.label, query) }), item.description && (jsx("div", { className: cn('text-xs truncate', isActive ? 'text-primary-foreground/70' : 'text-text-muted'), children: highlight(item.description, query) }))] }), item.shortcut && (jsx("div", { className: "flex items-center gap-1 flex-shrink-0", children: item.shortcut.map((k, i) => (jsx(Kbd, { size: "sm", children: k }, i))) })), jsx(ChevronRight, { size: 14, className: cn('flex-shrink-0', isActive ? 'text-primary-foreground/70' : 'text-text-muted') })] }, item.id));
3414
+ return (jsxs("div", { "data-active": isActive, role: "option", "aria-selected": isActive, "aria-disabled": item.disabled, onClick: () => !item.disabled && handleSelect(item), onMouseEnter: () => setActiveIndex(idx), className: cn('flex items-center gap-3 px-3 py-2.5 rounded-md cursor-pointer transition-colors', isActive ? 'bg-primary text-primary-foreground' : 'text-text hover:bg-surface-hover', item.disabled && 'opacity-40 cursor-not-allowed pointer-events-none'), children: [item.icon && (jsx("span", { className: cn('flex-shrink-0', isActive ? 'text-primary-foreground' : 'text-text-muted'), children: item.icon })), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("div", { className: "text-sm font-medium truncate", children: highlight(item.label, query) }), item.description && (jsx("div", { className: cn('text-xs truncate', isActive ? 'text-primary-foreground/70' : 'text-text-muted'), children: highlight(item.description, query) }))] }), item.shortcut && (jsx("div", { className: "flex items-center gap-1 flex-shrink-0", children: item.shortcut.map((k) => (jsx(Kbd, { size: "sm", children: k }, k))) })), jsx(ChevronRight, { size: 14, className: cn('flex-shrink-0', isActive ? 'text-primary-foreground/70' : 'text-text-muted') })] }, item.id));
3341
3415
  };
3342
3416
  return createPortal(jsxs("div", { className: "fixed inset-0 z-[9999] flex items-start justify-center pt-[15vh] px-4", role: "dialog", "aria-modal": "true", "aria-label": "Command palette", onClick: (e) => e.target === e.currentTarget && onClose(), children: [jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm", onClick: onClose }), jsxs("div", { className: cn('relative w-full max-w-lg bg-surface rounded-xl shadow-2xl border border-border overflow-hidden', className), onKeyDown: handleKeyDown, children: [jsxs("div", { className: "flex items-center gap-3 px-4 py-3 border-b border-border", children: [loading ? (jsx(Loader2, { size: 18, className: "text-text-muted animate-spin flex-shrink-0" })) : (jsx(Search, { size: 18, className: "text-text-muted flex-shrink-0" })), jsx("input", { ref: inputRef, id: `${id}-input`, type: "text", role: "combobox", "aria-expanded": true, "aria-controls": `${id}-list`, "aria-autocomplete": "list", value: query, onChange: (e) => setQuery(e.target.value), placeholder: placeholder, className: "flex-1 bg-transparent text-text placeholder:text-text-muted outline-none text-sm" }), query && (jsx("button", { type: "button", onClick: () => setQuery(''), className: "text-text-muted hover:text-text transition-colors", "aria-label": "Clear search", children: jsx(X, { size: 16 }) })), jsx(Kbd, { size: "sm", children: "Esc" })] }), jsx("div", { ref: listRef, id: `${id}-list`, role: "listbox", className: "overflow-y-auto p-2", style: { maxHeight }, children: loading && !hasResults ? (jsxs("div", { className: "flex items-center justify-center py-8 text-text-muted text-sm gap-2", children: [jsx(Loader2, { size: 16, className: "animate-spin" }), "Searching\u2026"] })) : !hasResults ? (jsx("div", { className: "py-8 text-center text-text-muted text-sm", children: emptyMessage })) : (jsxs(Fragment, { children: [filteredItems.map(renderItem), filteredGroups.map((group) => (jsxs("div", { children: [jsx("div", { className: "px-3 py-1.5 text-xs font-semibold text-text-muted uppercase tracking-wider", children: group.label }), group.items.map(renderItem)] }, group.id)))] })) }), jsxs("div", { className: "flex items-center gap-3 px-4 py-2 border-t border-border text-xs text-text-muted", children: [jsxs("span", { className: "flex items-center gap-1", children: [jsx(Kbd, { size: "sm", children: "\u2191" }), jsx(Kbd, { size: "sm", children: "\u2193" }), " Navigate"] }), jsxs("span", { className: "flex items-center gap-1", children: [jsx(Kbd, { size: "sm", children: "\u21B5" }), " Select"] }), jsxs("span", { className: "flex items-center gap-1", children: [jsx(Kbd, { size: "sm", children: "Esc" }), " Close"] })] })] })] }), document.body);
3343
3417
  }