@hexdspace/react 0.1.12 → 0.1.14

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.
@@ -181,7 +181,7 @@
181
181
 
182
182
  @keyframes dialog-surface-in {
183
183
  from {
184
- transform: translateY(0.75rem);
184
+ transform: translateY(1.25rem);
185
185
  }
186
186
  to {
187
187
  transform: translateY(0);
@@ -193,7 +193,7 @@
193
193
  transform: translateY(0);
194
194
  }
195
195
  to {
196
- transform: translateY(0.75rem);
196
+ transform: translateY(1.25rem);
197
197
  }
198
198
  }
199
199
 
package/dist/index.d.ts CHANGED
@@ -426,6 +426,7 @@ interface ConfirmDialogProps {
426
426
  declare const ConfirmDialog: React__default.FC<ConfirmDialogProps>;
427
427
 
428
428
  interface CustomDialogPayload {
429
+ title?: string;
429
430
  children?: React__default.ReactNode;
430
431
  }
431
432
  interface CustomDialogProps {
package/dist/index.js CHANGED
@@ -1552,26 +1552,30 @@ var dialogPanelVariants = cva2(
1552
1552
  "-translate-x-1/2 -translate-y-1/2",
1553
1553
  "w-[min(92vw,var(--dialog-width))]",
1554
1554
  "pointer-events-auto",
1555
- "data-[state=open]:animate-[dialog-fade-in_var(--motion-med)_cubic-bezier(0.16,1,0.3,1)]",
1556
- "data-[state=closed]:animate-[dialog-fade-out_var(--motion-fast)_cubic-bezier(0.4,0,1,1)]",
1555
+ "data-[state=open]:animate-[dialog-fade-in_var(--motion-med)_cubic-bezier(0.2,0,0.2,1)]",
1556
+ "data-[state=closed]:animate-[dialog-fade-out_var(--motion-med)_cubic-bezier(0.2,0,0.2,1)]",
1557
1557
  "motion-reduce:animate-none",
1558
1558
  "data-[state=closed]:pointer-events-none"
1559
1559
  )
1560
1560
  );
1561
1561
  var dialogOverlayClass = cn(
1562
1562
  "fixed inset-0 bg-[color:var(--overlay)]",
1563
- "data-[state=open]:animate-[dialog-overlay-in_var(--motion-med)_cubic-bezier(0.16,1,0.3,1)]",
1564
- "data-[state=closed]:animate-[dialog-overlay-out_var(--motion-fast)_cubic-bezier(0.4,0,1,1)]",
1563
+ "data-[state=open]:animate-[dialog-overlay-in_var(--motion-med)_cubic-bezier(0.2,0,0.2,1)]",
1564
+ "data-[state=closed]:animate-[dialog-overlay-out_var(--motion-med)_cubic-bezier(0.2,0,0.2,1)]",
1565
1565
  "motion-reduce:animate-none",
1566
1566
  "data-[state=closed]:pointer-events-none"
1567
1567
  );
1568
1568
  function DialogContent({ className, ...props }) {
1569
1569
  return /* @__PURE__ */ jsx7("div", { className: cn("DialogContent p-6", className), ...props });
1570
1570
  }
1571
- function AutoHeight({ children }) {
1571
+ function AutoHeight({ children, freeze = false }) {
1572
1572
  const ref = React2.useRef(null);
1573
1573
  const [height, setHeight] = React2.useState("auto");
1574
1574
  React2.useLayoutEffect(() => {
1575
+ if (freeze) {
1576
+ setHeight("auto");
1577
+ return;
1578
+ }
1575
1579
  const el = ref.current;
1576
1580
  if (!el) return;
1577
1581
  const dpr = Math.max(1, Math.round(window.devicePixelRatio || 1));
@@ -1593,11 +1597,14 @@ function AutoHeight({ children }) {
1593
1597
  ro.disconnect();
1594
1598
  cancelAnimationFrame(raf);
1595
1599
  };
1596
- }, []);
1600
+ }, [freeze]);
1597
1601
  return /* @__PURE__ */ jsx7(
1598
1602
  "div",
1599
1603
  {
1600
- className: "transition-[height] duration-(--motion-med) ease",
1604
+ className: cn(
1605
+ "transition-[height] duration-(--motion-med) ease motion-reduce:transition-none",
1606
+ freeze && "transition-none"
1607
+ ),
1601
1608
  style: { height: height === "auto" ? "auto" : height, overflow: "hidden", willChange: "height" },
1602
1609
  children: /* @__PURE__ */ jsx7("div", { ref, style: { boxSizing: "border-box" }, children })
1603
1610
  }
@@ -1625,8 +1632,19 @@ function Dialog({
1625
1632
  }) {
1626
1633
  const fallbackId = React2.useId();
1627
1634
  const resolvedRef = React2.useRef(false);
1635
+ const [isAnimating, setIsAnimating] = React2.useState(false);
1636
+ const animationTimer = React2.useRef(null);
1637
+ const motionMs = useMotionDurationMs("--motion-med", 160);
1628
1638
  const Template = template;
1629
1639
  const handleOpenChange = (nextOpen) => {
1640
+ if (typeof window !== "undefined") {
1641
+ if (animationTimer.current) window.clearTimeout(animationTimer.current);
1642
+ setIsAnimating(true);
1643
+ animationTimer.current = window.setTimeout(() => {
1644
+ setIsAnimating(false);
1645
+ animationTimer.current = null;
1646
+ }, motionMs);
1647
+ }
1630
1648
  onOpenChange?.(nextOpen);
1631
1649
  if (nextOpen) {
1632
1650
  resolvedRef.current = false;
@@ -1662,10 +1680,11 @@ function Dialog({
1662
1680
  "DialogContent",
1663
1681
  dialogPanelVariants(),
1664
1682
  "data-[state=open]:[&_.dialog-surface]:animate-[dialog-surface-in_var(--motion-med)_cubic-bezier(0.16,1,0.3,1)]",
1665
- "data-[state=closed]:[&_.dialog-surface]:animate-[dialog-surface-out_var(--motion-fast)_cubic-bezier(0.4,0,1,1)]",
1683
+ "data-[state=closed]:[&_.dialog-surface]:animate-[dialog-surface-out_var(--motion-med)_cubic-bezier(0.4,0,1,1)]",
1666
1684
  "motion-reduce:[&_.dialog-surface]:animate-none",
1667
1685
  className
1668
1686
  ),
1687
+ "aria-describedby": void 0,
1669
1688
  style: {
1670
1689
  ...style,
1671
1690
  zIndex,
@@ -1689,13 +1708,32 @@ function Dialog({
1689
1708
  "aria-label": closeLabel
1690
1709
  }
1691
1710
  ) }) : null,
1692
- /* @__PURE__ */ jsx7(AutoHeight, { children: /* @__PURE__ */ jsx7(Template, { id: id ?? fallbackId, payload, onResolve: handleResolve }) })
1711
+ /* @__PURE__ */ jsx7(AutoHeight, { freeze: isAnimating, children: /* @__PURE__ */ jsx7(Template, { id: id ?? fallbackId, payload, onResolve: handleResolve }) })
1693
1712
  ] })
1694
1713
  }
1695
1714
  )
1696
1715
  ] })
1697
1716
  ] });
1698
1717
  }
1718
+ function useMotionDurationMs(token, fallback) {
1719
+ const [ms, setMs] = React2.useState(fallback);
1720
+ React2.useEffect(() => {
1721
+ if (typeof window === "undefined") return;
1722
+ const raw = getComputedStyle(document.documentElement).getPropertyValue(token).trim();
1723
+ const parsed = parseDurationMs(raw);
1724
+ if (parsed !== null) setMs(parsed);
1725
+ }, [token]);
1726
+ return ms;
1727
+ }
1728
+ function parseDurationMs(value) {
1729
+ if (!value) return null;
1730
+ const trimmed = value.trim();
1731
+ const numeric = Number(trimmed.replace(/ms|s/g, ""));
1732
+ if (Number.isNaN(numeric)) return null;
1733
+ if (trimmed.endsWith("ms")) return numeric;
1734
+ if (trimmed.endsWith("s")) return numeric * 1e3;
1735
+ return numeric;
1736
+ }
1699
1737
 
1700
1738
  // src/ui/components/dialog/dialog-variant/ConfirmDialog.tsx
1701
1739
  import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
@@ -1703,9 +1741,9 @@ var ConfirmDialog = (props) => {
1703
1741
  const p = props.payload ?? { title: "Are you sure?" };
1704
1742
  const confirmVariant = p.variant ?? (p.danger ? "danger" : "secondary");
1705
1743
  return /* @__PURE__ */ jsxs4(DialogContent, { children: [
1706
- /* @__PURE__ */ jsx8("h3", { className: "text-lg font-semibold", children: p.title }),
1744
+ /* @__PURE__ */ jsx8(DialogPrimitive2.Title, { className: "text-lg font-semibold", children: p.title }),
1707
1745
  p.children && p.children,
1708
- p.message ? /* @__PURE__ */ jsx8("p", { className: "mt-2 text-sm opacity-80", children: p.message }) : null,
1746
+ p.message ? /* @__PURE__ */ jsx8(DialogPrimitive2.Description, { className: "mt-2 text-sm opacity-80", children: p.message }) : null,
1709
1747
  /* @__PURE__ */ jsxs4("div", { className: "mt-5 flex items-center justify-end gap-4", children: [
1710
1748
  /* @__PURE__ */ jsx8(DialogPrimitive2.Close, { asChild: true, children: /* @__PURE__ */ jsx8(Button, { variant: "secondary", children: p.cancelText ?? "Cancel" }) }),
1711
1749
  /* @__PURE__ */ jsx8(DialogPrimitive2.Close, { asChild: true, children: /* @__PURE__ */ jsx8(Button, { variant: confirmVariant, onClick: () => props.onResolve?.({ confirmed: true }), children: p.confirmText ?? "Confirm" }) })
@@ -1714,21 +1752,25 @@ var ConfirmDialog = (props) => {
1714
1752
  };
1715
1753
 
1716
1754
  // src/ui/components/dialog/dialog-variant/CustomDialog.tsx
1717
- import { jsx as jsx9 } from "react/jsx-runtime";
1755
+ import { Dialog as DialogPrimitive3 } from "radix-ui";
1756
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
1718
1757
  var CustomDialog = (props) => {
1719
1758
  const p = props.payload ?? { children: /* @__PURE__ */ jsx9("div", { children: "Empty custom dialog" }) };
1720
- return /* @__PURE__ */ jsx9(DialogContent, { children: p.children });
1759
+ return /* @__PURE__ */ jsxs5(DialogContent, { children: [
1760
+ /* @__PURE__ */ jsx9(DialogPrimitive3.Title, { className: "sr-only", children: p.title ?? "Dialog" }),
1761
+ p.children
1762
+ ] });
1721
1763
  };
1722
1764
 
1723
1765
  // src/ui/components/dialog/dialog-variant/InfoDialog.tsx
1724
- import { Dialog as DialogPrimitive3 } from "radix-ui";
1725
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
1766
+ import { Dialog as DialogPrimitive4 } from "radix-ui";
1767
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1726
1768
  var InfoDialog = (props) => {
1727
1769
  const p = props.payload ?? { title: "Information" };
1728
- return /* @__PURE__ */ jsxs5(DialogContent, { children: [
1729
- /* @__PURE__ */ jsx10("h3", { className: "text-lg font-semibold", children: p.title }),
1730
- p.children,
1731
- /* @__PURE__ */ jsx10("div", { className: "mt-5 flex items-center justify-end gap-4", children: /* @__PURE__ */ jsx10(DialogPrimitive3.Close, { asChild: true, children: /* @__PURE__ */ jsx10(Button, { variant: "secondary", children: p.confirmText || "Dismiss" }) }) })
1770
+ return /* @__PURE__ */ jsxs6(DialogContent, { children: [
1771
+ /* @__PURE__ */ jsx10(DialogPrimitive4.Title, { className: "text-lg font-semibold", children: p.title }),
1772
+ p.children ? /* @__PURE__ */ jsx10(DialogPrimitive4.Description, { asChild: true, children: /* @__PURE__ */ jsx10("div", { className: "mt-2 text-sm opacity-80", children: p.children }) }) : null,
1773
+ /* @__PURE__ */ jsx10("div", { className: "mt-5 flex items-center justify-end gap-4", children: /* @__PURE__ */ jsx10(DialogPrimitive4.Close, { asChild: true, children: /* @__PURE__ */ jsx10(Button, { variant: "secondary", children: p.confirmText || "Dismiss" }) }) })
1732
1774
  ] });
1733
1775
  };
1734
1776
 
@@ -1905,13 +1947,52 @@ var dialogController = new DialogController(registry, open, resolve, dismiss, cl
1905
1947
  import { Fragment as Fragment2, jsx as jsx11 } from "react/jsx-runtime";
1906
1948
  var DialogHost = () => {
1907
1949
  const [state, setState] = React3.useState(dialogController.handleGetRegistry().getState());
1950
+ const [closingIds, setClosingIds] = React3.useState(() => /* @__PURE__ */ new Set());
1951
+ const closeTimers = React3.useRef(/* @__PURE__ */ new Map());
1952
+ const [closeDelayMs, setCloseDelayMs] = React3.useState(160);
1908
1953
  React3.useEffect(() => dialogController.handleGetRegistry().subscribe(setState), []);
1954
+ React3.useEffect(() => {
1955
+ if (typeof window === "undefined") return;
1956
+ const raw = getComputedStyle(document.documentElement).getPropertyValue("--motion-fast").trim();
1957
+ const parsed = parseDurationMs2(raw);
1958
+ if (parsed) setCloseDelayMs(parsed);
1959
+ }, []);
1960
+ React3.useEffect(() => {
1961
+ return () => {
1962
+ for (const timer of closeTimers.current.values()) {
1963
+ window.clearTimeout(timer);
1964
+ }
1965
+ closeTimers.current.clear();
1966
+ };
1967
+ }, []);
1968
+ const scheduleClose = React3.useCallback(
1969
+ (id, action, result) => {
1970
+ if (closingIds.has(id)) return;
1971
+ setClosingIds((prev) => new Set(prev).add(id));
1972
+ const timer = window.setTimeout(() => {
1973
+ if (action === "resolve") {
1974
+ dialogController.handleResolve(id, result);
1975
+ } else {
1976
+ dialogController.handleDismiss(id);
1977
+ }
1978
+ setClosingIds((prev) => {
1979
+ const next = new Set(prev);
1980
+ next.delete(id);
1981
+ return next;
1982
+ });
1983
+ closeTimers.current.delete(id);
1984
+ }, closeDelayMs);
1985
+ closeTimers.current.set(id, timer);
1986
+ },
1987
+ [closeDelayMs, closingIds]
1988
+ );
1909
1989
  return /* @__PURE__ */ jsx11(Fragment2, { children: state.stack.map((dialog, index) => {
1910
1990
  const template = getDialogTemplate(dialog.templateKey);
1911
1991
  if (!template) {
1912
1992
  console.error(`Missing dialog template: ${dialog.templateKey}`);
1913
1993
  return null;
1914
1994
  }
1995
+ const isClosing = closingIds.has(dialog.id);
1915
1996
  return /* @__PURE__ */ jsx11(
1916
1997
  Dialog,
1917
1998
  {
@@ -1921,14 +2002,23 @@ var DialogHost = () => {
1921
2002
  modal: dialog.options.modal,
1922
2003
  dismissible: dialog.options.dismissible,
1923
2004
  zIndex: dialog.options.zIndex + index,
1924
- defaultOpen: true,
1925
- onResolve: (result) => dialogController.handleResolve(dialog.id, result),
1926
- onDismiss: () => dialogController.handleDismiss(dialog.id)
2005
+ open: !isClosing,
2006
+ onResolve: (result) => scheduleClose(dialog.id, "resolve", result),
2007
+ onDismiss: () => scheduleClose(dialog.id, "dismiss")
1927
2008
  },
1928
2009
  dialog.id
1929
2010
  );
1930
2011
  }) });
1931
2012
  };
2013
+ function parseDurationMs2(value) {
2014
+ if (!value) return null;
2015
+ const trimmed = value.trim();
2016
+ const asNumber = Number(trimmed.replace(/ms|s/g, ""));
2017
+ if (Number.isNaN(asNumber)) return null;
2018
+ if (trimmed.endsWith("ms")) return asNumber;
2019
+ if (trimmed.endsWith("s")) return asNumber * 1e3;
2020
+ return asNumber;
2021
+ }
1932
2022
 
1933
2023
  // src/feature/dialog/infra/web/react/useDialog.tsx
1934
2024
  import { useMemo as useMemo3 } from "react";
@@ -1966,7 +2056,7 @@ var Label = React4.forwardRef(
1966
2056
  Label.displayName = "Label";
1967
2057
 
1968
2058
  // src/ui/components/Field.tsx
1969
- import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
2059
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
1970
2060
  function Field({ label, hint, error, required, disabled, id, children, className, ...props }) {
1971
2061
  const reactId = React5.useId();
1972
2062
  const controlId = id ?? `field-${reactId}`;
@@ -1985,10 +2075,10 @@ function Field({ label, hint, error, required, disabled, id, children, className
1985
2075
  invalid,
1986
2076
  "aria-describedby": ariaDescribedBy
1987
2077
  });
1988
- return /* @__PURE__ */ jsxs6("div", { className: cn("grid gap-1.5", className), ...props, children: [
1989
- label ? /* @__PURE__ */ jsx13("div", { className: "flex items-center justify-between gap-3", children: /* @__PURE__ */ jsxs6(Label, { htmlFor: controlId, children: [
2078
+ return /* @__PURE__ */ jsxs7("div", { className: cn("grid gap-1.5", className), ...props, children: [
2079
+ label ? /* @__PURE__ */ jsx13("div", { className: "flex items-center justify-between gap-3", children: /* @__PURE__ */ jsxs7(Label, { htmlFor: controlId, children: [
1990
2080
  label,
1991
- resolvedRequired ? /* @__PURE__ */ jsxs6("span", { "aria-hidden": "true", className: "text-(--muted)", children: [
2081
+ resolvedRequired ? /* @__PURE__ */ jsxs7("span", { "aria-hidden": "true", className: "text-(--muted)", children: [
1992
2082
  " ",
1993
2083
  "*"
1994
2084
  ] }) : null
@@ -2002,7 +2092,7 @@ function Field({ label, hint, error, required, disabled, id, children, className
2002
2092
  // src/ui/components/Input.tsx
2003
2093
  import { cva as cva3 } from "class-variance-authority";
2004
2094
  import * as React6 from "react";
2005
- import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
2095
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
2006
2096
  var controlShellBase = cn(
2007
2097
  "relative inline-flex items-center gap-2",
2008
2098
  "rounded-[var(--radius-input)]",
@@ -2075,7 +2165,7 @@ var Input = React6.forwardRef(
2075
2165
  const isDisabled = Boolean(disabled);
2076
2166
  const showLeftSlot = Boolean(leftSlot || reserveLeftSlot);
2077
2167
  const showRightSlot = Boolean(rightSlot || reserveRightSlot);
2078
- return /* @__PURE__ */ jsxs7(
2168
+ return /* @__PURE__ */ jsxs8(
2079
2169
  "div",
2080
2170
  {
2081
2171
  className: cn(controlShellVariants({ variant, size, fullWidth }), className),
@@ -2326,7 +2416,7 @@ Textarea.displayName = "Textarea";
2326
2416
 
2327
2417
  // src/ui/components/Tooltip.tsx
2328
2418
  import { Tooltip as TooltipPrimitive } from "radix-ui";
2329
- import { jsx as jsx17, jsxs as jsxs8 } from "react/jsx-runtime";
2419
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
2330
2420
  var tooltipContentBase = cn(
2331
2421
  "panel shadow-none px-4 py-2.5 text-xs z-5000 select-none",
2332
2422
  "origin-center will-change-[transform,opacity]",
@@ -2348,9 +2438,9 @@ function Tooltip({
2348
2438
  maxWidthPx = 240,
2349
2439
  preserveWhitespace = false
2350
2440
  }) {
2351
- return /* @__PURE__ */ jsx17(TooltipPrimitive.Provider, { delayDuration: delayMs, children: /* @__PURE__ */ jsxs8(TooltipPrimitive.Root, { children: [
2441
+ return /* @__PURE__ */ jsx17(TooltipPrimitive.Provider, { delayDuration: delayMs, children: /* @__PURE__ */ jsxs9(TooltipPrimitive.Root, { children: [
2352
2442
  /* @__PURE__ */ jsx17(TooltipPrimitive.Trigger, { asChild: true, children }),
2353
- /* @__PURE__ */ jsx17(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs8(
2443
+ /* @__PURE__ */ jsx17(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs9(
2354
2444
  TooltipPrimitive.Content,
2355
2445
  {
2356
2446
  side,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexdspace/react",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",