@hyddenlabs/hydn-ui 0.3.0-alpha.2 → 0.3.0-alpha.99

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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import React3, { createContext, useState, useRef, useEffect, isValidElement, cloneElement, useCallback, useMemo, createElement, useContext, useLayoutEffect } from 'react';
2
+ import React5, { createContext, useId, useState, useRef, useEffect, isValidElement, cloneElement, useCallback, useLayoutEffect, useMemo, createElement, useContext } from 'react';
3
3
  import { IconX, IconChevronDown, IconCheck, IconCalendar, IconMenu2, IconTrash, IconChevronRight, IconChevronLeft, IconSelector, IconChevronUp } from '@tabler/icons-react';
4
4
  import { Link, NavLink } from 'react-router-dom';
5
5
  import { createPortal } from 'react-dom';
@@ -263,7 +263,8 @@ function Radio({
263
263
  success: "border-success focus:ring-success",
264
264
  warning: "border-warning focus:ring-warning"
265
265
  };
266
- const inputId = id || `radio-${value || Math.random().toString(36).slice(2)}`;
266
+ const generatedId = useId();
267
+ const inputId = id || `radio-${value || generatedId}`;
267
268
  return /* @__PURE__ */ jsxs(
268
269
  "div",
269
270
  {
@@ -277,7 +278,7 @@ function Radio({
277
278
  onChange: handleChange,
278
279
  disabled,
279
280
  "aria-label": ariaLabel,
280
- "aria-invalid": validationState === "error",
281
+ "aria-invalid": validationState === "error" ? "true" : void 0,
281
282
  id: inputId,
282
283
  name,
283
284
  value,
@@ -473,7 +474,13 @@ function MultiSelect({
473
474
  selectedValues.length > 0 ? /* @__PURE__ */ jsx(Fragment, { children: getSelectedLabels().map((label, index) => /* @__PURE__ */ jsxs(
474
475
  "span",
475
476
  {
476
- className: `inline-flex items-center gap-1 bg-primary/10 text-primary rounded-md font-medium ${currentSize.chip}`,
477
+ className: `
478
+ inline-flex items-center gap-1
479
+ bg-primary/10 text-primary rounded-md font-medium
480
+ animate-scaleIn origin-left
481
+ transition-all duration-200
482
+ ${currentSize.chip}
483
+ `.trim(),
477
484
  children: [
478
485
  label,
479
486
  /* @__PURE__ */ jsx(
@@ -481,7 +488,7 @@ function MultiSelect({
481
488
  {
482
489
  type: "button",
483
490
  onClick: (e) => handleRemoveValue(selectedValues[index], e),
484
- className: "hover:bg-primary/20 rounded-sm transition-colors",
491
+ className: "hover:bg-primary/20 rounded-sm transition-colors duration-150",
485
492
  "aria-label": `Remove ${label}`,
486
493
  tabIndex: -1,
487
494
  children: /* @__PURE__ */ jsx(IconX, { size: currentSize.icon - 4 })
@@ -541,7 +548,7 @@ function MultiSelect({
541
548
  onClick: () => !isDisabled && handleToggleOption(option.value),
542
549
  className: `
543
550
  w-full px-3 py-2 flex items-center justify-between gap-2
544
- transition-colors text-left ${currentSize.text}
551
+ transition-all duration-200 text-left ${currentSize.text}
545
552
  ${isFocused ? "bg-muted" : ""}
546
553
  ${isSelected ? "bg-primary/10 text-primary font-medium" : "hover:bg-muted"}
547
554
  ${isDisabled ? "opacity-50 cursor-not-allowed" : ""}
@@ -551,7 +558,13 @@ function MultiSelect({
551
558
  disabled: isDisabled,
552
559
  children: [
553
560
  /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: option.label }),
554
- isSelected && /* @__PURE__ */ jsx(IconCheck, { size: currentSize.icon, className: "flex-shrink-0" })
561
+ isSelected && /* @__PURE__ */ jsx(
562
+ IconCheck,
563
+ {
564
+ size: currentSize.icon,
565
+ className: "flex-shrink-0 animate-scaleIn"
566
+ }
567
+ )
555
568
  ]
556
569
  },
557
570
  option.value
@@ -880,11 +893,36 @@ function FormField({
880
893
  FormField.displayName = "FormField";
881
894
  var form_field_default = FormField;
882
895
  function InputGroup({ children, prefix, suffix, className = "" }) {
883
- return /* @__PURE__ */ jsxs("div", { className: `flex items-stretch ${className}`, children: [
884
- prefix && /* @__PURE__ */ jsx("div", { className: "flex items-center px-3 bg-muted border border-r-0 border-input rounded-l-md", children: prefix }),
885
- children,
886
- suffix && /* @__PURE__ */ jsx("div", { className: "flex items-center px-3 bg-muted border border-l-0 border-input rounded-r-md", children: suffix })
887
- ] });
896
+ const isTextSuffix = typeof suffix === "string" || typeof suffix === "number";
897
+ return /* @__PURE__ */ jsxs(
898
+ "div",
899
+ {
900
+ className: [
901
+ "inline-flex items-stretch rounded-lg border border-input shadow-sm bg-background",
902
+ "focus-within:ring-2 focus-within:ring-ring/20 focus-within:border-ring",
903
+ "transition-colors duration-150 overflow-hidden",
904
+ // Use CSS nesting to style child input without cloning
905
+ "[&>input]:border-0 [&>input]:shadow-none [&>input]:rounded-none [&>input]:ring-0",
906
+ "[&>input]:focus:ring-0 [&>input]:focus:border-0 [&>input]:bg-transparent",
907
+ "[&>input]:flex-1 [&>input]:min-w-0",
908
+ // Style child buttons (both direct and nested)
909
+ "[&_button]:rounded-none [&_button]:border-0 [&_button]:shadow-none [&_button]:m-0",
910
+ "[&_button]:h-full",
911
+ className
912
+ ].filter(Boolean).join(" "),
913
+ children: [
914
+ prefix && /* @__PURE__ */ jsx("div", { className: "flex items-center px-3 bg-muted/50 text-muted-foreground text-sm shrink-0", children: prefix }),
915
+ children,
916
+ suffix && /* @__PURE__ */ jsx(
917
+ "div",
918
+ {
919
+ className: isTextSuffix ? "flex items-center px-3 bg-muted/50 text-muted-foreground text-sm shrink-0" : "flex items-stretch shrink-0",
920
+ children: suffix
921
+ }
922
+ )
923
+ ]
924
+ }
925
+ );
888
926
  }
889
927
  InputGroup.displayName = "InputGroup";
890
928
  var input_group_default = InputGroup;
@@ -1176,8 +1214,31 @@ function DatePicker({
1176
1214
  }
1177
1215
  DatePicker.displayName = "DatePicker";
1178
1216
  var date_picker_default = DatePicker;
1179
- function Nav({ children, className = "", ariaLabel = "Main navigation" }) {
1180
- return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className: `flex items-center ${className}`, children });
1217
+ function Nav({
1218
+ children,
1219
+ className = "",
1220
+ ariaLabel = "Main navigation",
1221
+ direction = "horizontal",
1222
+ spacing = "md",
1223
+ align = "center"
1224
+ }) {
1225
+ const spacingClasses = {
1226
+ none: "gap-0",
1227
+ sm: "gap-2",
1228
+ md: "gap-4",
1229
+ lg: "gap-6",
1230
+ xl: "gap-8"
1231
+ };
1232
+ const alignClasses = {
1233
+ start: "items-start",
1234
+ center: "items-center",
1235
+ end: "items-end",
1236
+ stretch: "items-stretch"
1237
+ };
1238
+ const directionClass = direction === "horizontal" ? "flex-row" : "flex-col";
1239
+ const spacingClass = spacingClasses[spacing];
1240
+ const alignClass = alignClasses[align];
1241
+ return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className: `flex ${directionClass} ${spacingClass} ${alignClass} ${className}`, children });
1181
1242
  }
1182
1243
  Nav.displayName = "Nav";
1183
1244
  var nav_default = Nav;
@@ -1186,7 +1247,9 @@ function Container({
1186
1247
  className = "",
1187
1248
  size = "lg",
1188
1249
  padding = "lg",
1189
- align = "center"
1250
+ align = "center",
1251
+ minWidth,
1252
+ minHeight
1190
1253
  }) {
1191
1254
  const sizeClasses = {
1192
1255
  sm: "max-w-screen-sm",
@@ -1207,7 +1270,54 @@ function Container({
1207
1270
  center: "mx-auto",
1208
1271
  end: "ml-auto"
1209
1272
  };
1210
- return /* @__PURE__ */ jsx("div", { className: `px-4 ${sizeClasses[size]} ${paddingClasses[padding]} ${alignClasses[align]} ${className}`, children });
1273
+ const minWidthClasses = {
1274
+ xs: "min-w-[20rem]",
1275
+ // 320px
1276
+ sm: "min-w-[24rem]",
1277
+ // 384px
1278
+ md: "min-w-[28rem]",
1279
+ // 448px
1280
+ lg: "min-w-[32rem]",
1281
+ // 512px
1282
+ xl: "min-w-[36rem]",
1283
+ // 576px
1284
+ "2xl": "min-w-[42rem]",
1285
+ // 672px
1286
+ "3xl": "min-w-[48rem]",
1287
+ // 768px
1288
+ full: "min-w-full"
1289
+ };
1290
+ const minHeightClasses = {
1291
+ xs: "min-h-[10rem]",
1292
+ // 160px
1293
+ sm: "min-h-[15rem]",
1294
+ // 240px
1295
+ md: "min-h-[20rem]",
1296
+ // 320px
1297
+ lg: "min-h-[25rem]",
1298
+ // 400px
1299
+ xl: "min-h-[30rem]",
1300
+ // 480px
1301
+ "2xl": "min-h-[35rem]",
1302
+ // 560px
1303
+ "3xl": "min-h-[40rem]",
1304
+ // 640px
1305
+ screen: "min-h-screen"
1306
+ };
1307
+ const minWidthClass = minWidth && minWidthClasses[minWidth] ? minWidthClasses[minWidth] : "";
1308
+ const minHeightClass = minHeight && minHeightClasses[minHeight] ? minHeightClasses[minHeight] : "";
1309
+ const inlineStyles = {
1310
+ ...minWidth && !minWidthClasses[minWidth] && { minWidth },
1311
+ ...minHeight && !minHeightClasses[minHeight] && { minHeight }
1312
+ };
1313
+ return /* @__PURE__ */ jsx(
1314
+ "div",
1315
+ {
1316
+ className: `px-4 ${sizeClasses[size]} ${paddingClasses[padding]} ${alignClasses[align]} ${minWidthClass} ${minHeightClass} ${className}`,
1317
+ style: Object.keys(inlineStyles).length > 0 ? inlineStyles : void 0,
1318
+ children
1319
+ }
1320
+ );
1211
1321
  }
1212
1322
  Container.displayName = "Container";
1213
1323
  var container_default = Container;
@@ -1317,8 +1427,8 @@ var PageTransition = ({
1317
1427
  type = "fade",
1318
1428
  className = ""
1319
1429
  }) => {
1320
- const [isVisible, setIsVisible] = React3.useState(false);
1321
- React3.useEffect(() => {
1430
+ const [isVisible, setIsVisible] = React5.useState(false);
1431
+ React5.useEffect(() => {
1322
1432
  requestAnimationFrame(() => {
1323
1433
  requestAnimationFrame(() => {
1324
1434
  setIsVisible(true);
@@ -1440,20 +1550,34 @@ function Dropdown({
1440
1550
  document.addEventListener("mousedown", handleClickOutside);
1441
1551
  return () => document.removeEventListener("mousedown", handleClickOutside);
1442
1552
  }, [isOpen, close]);
1443
- useEffect(() => {
1553
+ useLayoutEffect(() => {
1554
+ let raf;
1444
1555
  if (isOpen) {
1445
1556
  const itemEls = itemsRef.current.filter(Boolean);
1446
1557
  if (itemEls.length) {
1447
- setActiveIndex(0);
1448
- itemEls[0]?.focus();
1558
+ raf = requestAnimationFrame(() => {
1559
+ setActiveIndex(0);
1560
+ itemEls[0]?.focus();
1561
+ });
1449
1562
  }
1450
1563
  } else {
1451
- setActiveIndex(-1);
1564
+ raf = requestAnimationFrame(() => setActiveIndex(-1));
1452
1565
  }
1566
+ return () => {
1567
+ if (raf) cancelAnimationFrame(raf);
1568
+ };
1453
1569
  }, [isOpen]);
1454
- const registerItem = (el, index) => {
1455
- itemsRef.current[index] = el;
1456
- };
1570
+ const registerItem = useCallback((el, index) => {
1571
+ if (typeof index === "number" && index >= 0) {
1572
+ itemsRef.current[index] = el;
1573
+ return;
1574
+ }
1575
+ if (el === null) {
1576
+ itemsRef.current = itemsRef.current.filter((x) => x !== el && x != null);
1577
+ return;
1578
+ }
1579
+ if (!itemsRef.current.includes(el)) itemsRef.current.push(el);
1580
+ }, []);
1457
1581
  return /* @__PURE__ */ jsxs("div", { ref: dropdownRef, className: `relative ${className}`, children: [
1458
1582
  /* @__PURE__ */ jsx(
1459
1583
  "button",
@@ -1468,7 +1592,7 @@ function Dropdown({
1468
1592
  children: trigger
1469
1593
  }
1470
1594
  ),
1471
- isOpen && /* @__PURE__ */ jsx(DropdownContext.Provider, { value: { requestClose: close, autoClose }, children: /* @__PURE__ */ jsx(
1595
+ isOpen && /* @__PURE__ */ jsx(DropdownContext.Provider, { value: { requestClose: close, autoClose, registerItem }, children: /* @__PURE__ */ jsx(
1472
1596
  "div",
1473
1597
  {
1474
1598
  id: "dropdown-menu",
@@ -1477,10 +1601,7 @@ function Dropdown({
1477
1601
  role: "menu",
1478
1602
  "aria-orientation": "vertical",
1479
1603
  tabIndex: -1,
1480
- children: React3.Children.map(children, (child, i) => {
1481
- if (!React3.isValidElement(child)) return child;
1482
- return React3.cloneElement(child, { __dropdownIndex: i, __registerItem: registerItem, size });
1483
- })
1604
+ children
1484
1605
  }
1485
1606
  ) })
1486
1607
  ] });
@@ -1576,6 +1697,22 @@ Pagination.displayName = "Pagination";
1576
1697
  var pagination_default = Pagination;
1577
1698
  function Sidebar({ children, className = "", width = "16rem" }) {
1578
1699
  const widthClass = width === "16rem" ? "w-64" : width === "4rem" ? "w-16" : "";
1700
+ const enhancedChildren = React5.Children.map(children, (child) => {
1701
+ if (!React5.isValidElement(child)) return child;
1702
+ const childProps = child.props || {};
1703
+ if ("href" in childProps) {
1704
+ const existing = typeof childProps.className === "string" ? childProps.className : "";
1705
+ const sidebarItemClasses = "flex items-center w-full justify-start gap-2 px-2 py-1.5 rounded hover:bg-muted";
1706
+ const childInner = child.props.children;
1707
+ const wrappedChildren = /* @__PURE__ */ jsx("span", { className: "flex items-center gap-2", children: childInner });
1708
+ const newProps = {
1709
+ ...child.props,
1710
+ className: `${existing} ${sidebarItemClasses}`.trim()
1711
+ };
1712
+ return React5.cloneElement(child, newProps, wrappedChildren);
1713
+ }
1714
+ return child;
1715
+ });
1579
1716
  return /* @__PURE__ */ jsx(
1580
1717
  "nav",
1581
1718
  {
@@ -1588,7 +1725,7 @@ function Sidebar({ children, className = "", width = "16rem" }) {
1588
1725
  `.replace(/\s+/g, " "),
1589
1726
  style: !widthClass ? { width } : void 0,
1590
1727
  "aria-label": "Sidebar navigation",
1591
- children: /* @__PURE__ */ jsx("div", { className: "flex-1 px-4 py-3 space-y-2", children })
1728
+ children: /* @__PURE__ */ jsx("div", { className: "flex-1 px-4 py-3 flex flex-col gap-2", children: enhancedChildren })
1592
1729
  }
1593
1730
  );
1594
1731
  }
@@ -1756,6 +1893,12 @@ function Toast({ message, children, type = "info", onClose, className = "", dura
1756
1893
  warning: "bg-warning text-warning-foreground",
1757
1894
  error: "bg-destructive text-destructive-foreground"
1758
1895
  };
1896
+ const handleClose = useCallback(() => {
1897
+ setIsClosing(true);
1898
+ setTimeout(() => {
1899
+ onClose?.();
1900
+ }, 300);
1901
+ }, [onClose]);
1759
1902
  useEffect(() => {
1760
1903
  if (duration > 0) {
1761
1904
  const timer = setTimeout(() => {
@@ -1764,25 +1907,20 @@ function Toast({ message, children, type = "info", onClose, className = "", dura
1764
1907
  return () => clearTimeout(timer);
1765
1908
  }
1766
1909
  return void 0;
1767
- }, [duration]);
1768
- const handleClose = () => {
1769
- setIsClosing(true);
1770
- setTimeout(() => {
1771
- onClose?.();
1772
- }, 300);
1773
- };
1774
- return /* @__PURE__ */ jsxs(
1910
+ }, [duration, handleClose]);
1911
+ const toast = /* @__PURE__ */ jsxs(
1775
1912
  "div",
1776
1913
  {
1777
1914
  role: "alert",
1778
1915
  "aria-live": "polite",
1779
- className: `fixed bottom-4 right-4 px-4 py-3 rounded-md shadow-lg ${typeClasses[type]} transition-all duration-300 ease-out ${isClosing ? "opacity-0 translate-x-full" : "opacity-100 translate-x-0 animate-slideInRight"} ${className}`,
1916
+ className: `fixed bottom-4 right-4 px-4 py-3 rounded-md shadow-lg z-[9999] ${typeClasses[type]} transition-all duration-300 ease-out ${isClosing ? "opacity-0 translate-x-full" : "opacity-100 translate-x-0 animate-slideInRight"} ${className}`,
1780
1917
  children: [
1781
1918
  /* @__PURE__ */ jsx("span", { children: children || message }),
1782
1919
  onClose && /* @__PURE__ */ jsx("button", { onClick: handleClose, className: "ml-4 font-bold hover:opacity-70 transition-opacity", "aria-label": "Close", children: "\xD7" })
1783
1920
  ]
1784
1921
  }
1785
1922
  );
1923
+ return typeof document !== "undefined" ? createPortal(toast, document.body) : toast;
1786
1924
  }
1787
1925
  Toast.displayName = "Toast";
1788
1926
  var toast_default = Toast;
@@ -1908,6 +2046,19 @@ function Tooltip({
1908
2046
  }
1909
2047
  Tooltip.displayName = "Tooltip";
1910
2048
  var tooltip_default = Tooltip;
2049
+
2050
+ // src/utils/portal.ts
2051
+ function getPortalRoot(id = "hydn-ui-portal") {
2052
+ if (typeof document === "undefined") return null;
2053
+ let root = document.getElementById(id);
2054
+ if (!root) {
2055
+ root = document.createElement("div");
2056
+ root.id = id;
2057
+ document.body.appendChild(root);
2058
+ }
2059
+ return root;
2060
+ }
2061
+ var portal_default = getPortalRoot;
1911
2062
  function useOverlay(options) {
1912
2063
  const {
1913
2064
  isOpen,
@@ -1923,9 +2074,10 @@ function useOverlay(options) {
1923
2074
  const [shouldRender, setShouldRender] = useState(isOpen);
1924
2075
  const [phase, setPhase] = useState("mount");
1925
2076
  useLayoutEffect(() => {
1926
- if (isOpen) {
2077
+ if (isOpen && !shouldRender) {
1927
2078
  setShouldRender(true);
1928
2079
  setPhase("mount");
2080
+ } else if (isOpen && shouldRender && phase === "mount") {
1929
2081
  requestAnimationFrame(() => {
1930
2082
  setPhase("animating-in");
1931
2083
  let frame = 0;
@@ -1937,12 +2089,16 @@ function useOverlay(options) {
1937
2089
  requestAnimationFrame(step);
1938
2090
  }
1939
2091
  };
1940
- requestAnimationFrame(step);
2092
+ if (animationFrames > 0) {
2093
+ requestAnimationFrame(step);
2094
+ } else {
2095
+ setPhase("visible");
2096
+ }
1941
2097
  });
1942
- } else if (!isOpen && shouldRender) {
2098
+ } else if (!isOpen && shouldRender && phase !== "animating-out") {
1943
2099
  setPhase("animating-out");
1944
2100
  }
1945
- }, [isOpen, shouldRender, animationFrames]);
2101
+ }, [isOpen, shouldRender, phase, animationFrames]);
1946
2102
  useEffect(() => {
1947
2103
  if (phase === "animating-out" && unmountOnExit) {
1948
2104
  const timeout = setTimeout(() => {
@@ -2027,7 +2183,8 @@ function Modal({
2027
2183
  actions,
2028
2184
  className = "",
2029
2185
  ariaLabel,
2030
- align = "center"
2186
+ align = "center",
2187
+ portalRoot = portal_default()
2031
2188
  }) {
2032
2189
  const {
2033
2190
  phase,
@@ -2041,7 +2198,7 @@ function Modal({
2041
2198
  animationFrames: 2,
2042
2199
  restoreFocus: true
2043
2200
  });
2044
- React3.useEffect(() => {
2201
+ React5.useEffect(() => {
2045
2202
  if (!isOpen) return;
2046
2203
  const handleEscape = (e) => {
2047
2204
  if (e.key === "Escape") {
@@ -2059,7 +2216,7 @@ function Modal({
2059
2216
  const backdropOpacity = phase === "visible" || phase === "animating-in" ? "opacity-100" : "opacity-0 transition-opacity delay-50";
2060
2217
  const hasStructured = title || description || content || actions;
2061
2218
  const alignmentClasses = align === "center" ? "grid place-items-center" : "flex items-start justify-center pt-20";
2062
- return /* @__PURE__ */ jsx(
2219
+ const panel = /* @__PURE__ */ jsx(
2063
2220
  "div",
2064
2221
  {
2065
2222
  "data-phase": phase,
@@ -2093,6 +2250,7 @@ function Modal({
2093
2250
  )
2094
2251
  }
2095
2252
  );
2253
+ return portalRoot ? createPortal(panel, portalRoot) : panel;
2096
2254
  }
2097
2255
  Modal.displayName = "Modal";
2098
2256
  var modal_default = Modal;
@@ -2209,12 +2367,13 @@ function Popover({ trigger, children, content, position = "bottom", className =
2209
2367
  }
2210
2368
  Popover.displayName = "Popover";
2211
2369
  var popover_default = Popover;
2212
- function Alert({ children, type = "info", dismissible = false, onClose, className = "" }) {
2370
+ function Alert({ children, type = "info", dismissible = false, onClose, className = "", position = "relative", duration = 0 }) {
2371
+ const [isClosing, setIsClosing] = useState(false);
2213
2372
  const typeClasses = {
2214
- info: "bg-info/10 text-foreground border-info/30",
2215
- success: "bg-success/10 text-foreground border-success/30",
2216
- warning: "bg-warning/10 text-foreground border-warning/30",
2217
- error: "bg-destructive/10 text-foreground border-destructive/30"
2373
+ info: "bg-info/20 text-foreground border-info/50 backdrop-blur-sm",
2374
+ success: "bg-success/20 text-foreground border-success/50 backdrop-blur-sm",
2375
+ warning: "bg-warning/20 text-foreground border-warning/50 backdrop-blur-sm",
2376
+ error: "bg-destructive/20 text-foreground border-destructive/50 backdrop-blur-sm"
2218
2377
  };
2219
2378
  const iconClasses = {
2220
2379
  info: "text-info",
@@ -2222,6 +2381,21 @@ function Alert({ children, type = "info", dismissible = false, onClose, classNam
2222
2381
  warning: "text-warning",
2223
2382
  error: "text-destructive"
2224
2383
  };
2384
+ const handleClose = useCallback(() => {
2385
+ setIsClosing(true);
2386
+ setTimeout(() => {
2387
+ onClose?.();
2388
+ }, 300);
2389
+ }, [onClose]);
2390
+ useEffect(() => {
2391
+ if (duration > 0 && onClose) {
2392
+ const timer = setTimeout(() => {
2393
+ handleClose();
2394
+ }, duration);
2395
+ return () => clearTimeout(timer);
2396
+ }
2397
+ return void 0;
2398
+ }, [duration, onClose, handleClose]);
2225
2399
  const icons = {
2226
2400
  info: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 flex-shrink-0", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx(
2227
2401
  "path",
@@ -2256,19 +2430,44 @@ function Alert({ children, type = "info", dismissible = false, onClose, classNam
2256
2430
  }
2257
2431
  ) })
2258
2432
  };
2259
- return /* @__PURE__ */ jsxs("div", { role: "alert", className: `p-4 border rounded-lg flex items-start gap-3 ${typeClasses[type]} ${className}`, children: [
2260
- /* @__PURE__ */ jsx("span", { className: iconClasses[type], children: icons[type] }),
2261
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-sm", children }),
2262
- dismissible && onClose && /* @__PURE__ */ jsx(
2263
- "button",
2264
- {
2265
- onClick: onClose,
2266
- className: "flex-shrink-0 text-current opacity-70 hover:opacity-100 transition-opacity focus:outline-none focus:ring-2 focus:ring-ring rounded p-0.5",
2267
- "aria-label": "Close alert",
2268
- children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
2269
- }
2270
- )
2271
- ] });
2433
+ const positionClasses = {
2434
+ top: "fixed top-4 left-1/2 -translate-x-1/2 z-[9999] max-w-2xl w-full mx-4",
2435
+ bottom: "fixed bottom-4 left-1/2 -translate-x-1/2 z-[9999] max-w-2xl w-full mx-4",
2436
+ relative: ""
2437
+ };
2438
+ const getAnimationClasses = () => {
2439
+ if (position === "top") {
2440
+ return isClosing ? "opacity-0 -translate-y-full" : "opacity-100 translate-y-0 animate-slideInTop";
2441
+ }
2442
+ if (position === "bottom") {
2443
+ return isClosing ? "opacity-0 translate-y-full" : "opacity-100 translate-y-0 animate-slideInBottom";
2444
+ }
2445
+ return isClosing ? "opacity-0" : "opacity-100";
2446
+ };
2447
+ const alertContent = /* @__PURE__ */ jsxs(
2448
+ "div",
2449
+ {
2450
+ role: "alert",
2451
+ className: `p-4 border rounded-lg flex items-start gap-3 transition-all duration-300 ease-out ${typeClasses[type]} ${positionClasses[position]} ${getAnimationClasses()} ${className}`,
2452
+ children: [
2453
+ /* @__PURE__ */ jsx("span", { className: iconClasses[type], children: icons[type] }),
2454
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-sm", children }),
2455
+ dismissible && onClose && /* @__PURE__ */ jsx(
2456
+ "button",
2457
+ {
2458
+ onClick: handleClose,
2459
+ className: "flex-shrink-0 text-current opacity-70 hover:opacity-100 transition-opacity focus:outline-none focus:ring-2 focus:ring-ring rounded p-0.5",
2460
+ "aria-label": "Close alert",
2461
+ children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
2462
+ }
2463
+ )
2464
+ ]
2465
+ }
2466
+ );
2467
+ if (position !== "relative" && typeof document !== "undefined") {
2468
+ return createPortal(alertContent, document.body);
2469
+ }
2470
+ return alertContent;
2272
2471
  }
2273
2472
  Alert.displayName = "Alert";
2274
2473
  var alert_default = Alert;
@@ -3385,7 +3584,7 @@ function Drawer({
3385
3584
  closeOnEscape = true,
3386
3585
  closeOnOutside = true,
3387
3586
  unmountOnExit = true,
3388
- portalRoot = typeof document !== "undefined" ? document.body : null,
3587
+ portalRoot = portal_default(),
3389
3588
  noAnimation = false
3390
3589
  }) {
3391
3590
  const { phase, shouldRender, ref, getPhaseClass } = useOverlay_default({
@@ -3394,8 +3593,8 @@ function Drawer({
3394
3593
  restoreFocus: true,
3395
3594
  focusTrap: true,
3396
3595
  unmountOnExit,
3397
- exitDuration: noAnimation ? 0 : 300,
3398
- animationFrames: noAnimation ? 0 : 2
3596
+ exitDuration: noAnimation ? 0 : 250,
3597
+ animationFrames: noAnimation ? 0 : 0
3399
3598
  });
3400
3599
  if (!shouldRender) return null;
3401
3600
  const sizeClasses = {
@@ -3419,7 +3618,7 @@ function Drawer({
3419
3618
  };
3420
3619
  const openTransform = "translate-x-0 translate-y-0";
3421
3620
  const panelTransform = noAnimation ? "" : getPhaseClass(openTransform, closedTransform[position]);
3422
- const overlayOpacity = noAnimation ? "" : getPhaseClass("opacity-100", "opacity-0");
3621
+ const overlayOpacity = noAnimation ? "opacity-100" : getPhaseClass("opacity-100", "opacity-0");
3423
3622
  const handleKeyDown = (e) => {
3424
3623
  if (e.key === "Escape" && closeOnEscape) {
3425
3624
  e.stopPropagation();
@@ -3428,11 +3627,21 @@ function Drawer({
3428
3627
  };
3429
3628
  const panel = /* @__PURE__ */ jsxs(Fragment, { children: [
3430
3629
  /* @__PURE__ */ jsx(
3431
- "div",
3630
+ "button",
3432
3631
  {
3433
- className: `fixed inset-0 z-40 bg-black/50 backdrop-blur-sm transition-opacity duration-300 ${overlayOpacity}`,
3434
- "aria-hidden": "true",
3632
+ type: "button",
3633
+ className: `fixed inset-0 z-[999] bg-black/50 backdrop-blur-sm transition-opacity duration-[250ms] ease-in-out ${overlayOpacity} border-0 p-0 m-0`,
3634
+ "aria-label": closeOnOutside ? "Close overlay" : void 0,
3635
+ "aria-hidden": !closeOnOutside,
3636
+ tabIndex: closeOnOutside ? 0 : -1,
3435
3637
  onClick: () => closeOnOutside && onClose(),
3638
+ onKeyDown: (e) => {
3639
+ if (!closeOnOutside) return;
3640
+ if (e.key === "Enter" || e.key === " ") {
3641
+ e.preventDefault();
3642
+ onClose();
3643
+ }
3644
+ },
3436
3645
  "data-phase": phase
3437
3646
  }
3438
3647
  ),
@@ -3446,7 +3655,7 @@ function Drawer({
3446
3655
  tabIndex: -1,
3447
3656
  "data-phase": phase,
3448
3657
  "data-position": position,
3449
- className: `fixed ${edgeClasses[position]} ${position === "left" || position === "right" ? sizeClasses[size] : ""} bg-card text-card-foreground shadow-2xl z-50 flex flex-col outline-none ${panelTransform} ${noAnimation ? "" : "transition-[transform] duration-300 ease-out"} ${className}`,
3658
+ className: `fixed ${edgeClasses[position]} ${position === "left" || position === "right" ? sizeClasses[size] : ""} bg-card text-card-foreground shadow-2xl z-[1000] flex flex-col outline-none ${panelTransform} ${noAnimation ? "" : "transition-transform duration-[250ms] ease-out will-change-transform"} ${className}`,
3450
3659
  onKeyDown: handleKeyDown,
3451
3660
  children: [
3452
3661
  title && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-border/60 bg-card/95 backdrop-blur-sm", children: [
@@ -3726,10 +3935,14 @@ function useScrollReset(deps, container) {
3726
3935
  let cancelled = false;
3727
3936
  const maxRaf = 6;
3728
3937
  let rafCount = 0;
3938
+ const isRef = (obj) => {
3939
+ return typeof obj === "object" && obj !== null && "current" in obj;
3940
+ };
3729
3941
  const setAllScrollTop = () => {
3730
3942
  if (cancelled) return;
3731
3943
  window.scrollTo(0, 0);
3732
- if (container) container.scrollTop = 0;
3944
+ const resolved = isRef(container) ? container.current : container;
3945
+ if (resolved) resolved.scrollTop = 0;
3733
3946
  document.documentElement.scrollTop = 0;
3734
3947
  document.body.scrollTop = 0;
3735
3948
  };
@@ -3796,7 +4009,7 @@ function LeftNavLayout({
3796
4009
  navRef.current.scrollTop = scrollPosRef.current;
3797
4010
  }
3798
4011
  }, [children]);
3799
- useScrollReset_default([children], contentRef.current);
4012
+ useScrollReset_default([children], contentRef);
3800
4013
  const containerClasses = embedded ? "flex bg-background border border-border rounded-lg overflow-hidden" : "flex h-[calc(100vh-4rem)] bg-background";
3801
4014
  return /* @__PURE__ */ jsxs("div", { className: `${containerClasses} ${className}`, children: [
3802
4015
  mobileCollapsible && mobileMenuOpen && /* @__PURE__ */ jsx(
@@ -3828,17 +4041,34 @@ function LeftNavLayout({
3828
4041
  "div",
3829
4042
  {
3830
4043
  className: `
3831
- flex items-center h-12 flex-shrink-0
4044
+ relative flex items-center h-12 flex-shrink-0
3832
4045
  px-4 border-b border-border
3833
- ${collapsed ? "justify-center" : "justify-between"}
3834
4046
  `,
3835
4047
  children: [
3836
- !collapsed && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "Navigation" }),
4048
+ /* @__PURE__ */ jsx(
4049
+ "span",
4050
+ {
4051
+ className: `
4052
+ text-sm font-semibold text-foreground
4053
+ transition-all duration-300 ease-in-out
4054
+ ${collapsed ? "opacity-0 w-0 overflow-hidden" : "opacity-100"}
4055
+ `,
4056
+ children: "Navigation"
4057
+ }
4058
+ ),
3837
4059
  /* @__PURE__ */ jsx(
3838
4060
  "button",
3839
4061
  {
3840
4062
  onClick: toggleCollapsed,
3841
- className: "\n hidden lg:flex items-center justify-center\n w-8 h-8 rounded-md\n text-muted-foreground hover:text-foreground\n hover:bg-muted\n transition-colors\n focus:outline-none focus:ring-2 focus:ring-ring\n ",
4063
+ className: `
4064
+ hidden lg:flex items-center justify-center
4065
+ w-8 h-8 rounded-md
4066
+ text-muted-foreground hover:text-foreground
4067
+ hover:bg-muted
4068
+ transition-all duration-300 ease-in-out
4069
+ focus:outline-none focus:ring-2 focus:ring-ring
4070
+ ${collapsed ? "absolute left-1/2 -translate-x-1/2" : "absolute right-4"}
4071
+ `,
3842
4072
  "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
3843
4073
  type: "button",
3844
4074
  children: collapsed ? /* @__PURE__ */ jsx(IconChevronRight, { size: 20 }) : /* @__PURE__ */ jsx(IconChevronLeft, { size: 20 })