@techzyapp/ui-library 1.0.39 → 1.0.40

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.
Files changed (125) hide show
  1. package/dist/{AdminRoot-B2lwjQig.cjs → AdminRoot-CRTBGUxq.cjs} +2 -2
  2. package/dist/{AdminRoot-B2lwjQig.cjs.map → AdminRoot-CRTBGUxq.cjs.map} +1 -1
  3. package/dist/{AdminRoot-BTPLhufU.js → AdminRoot-JOgiNkJA.js} +3 -3
  4. package/dist/{AdminRoot-BTPLhufU.js.map → AdminRoot-JOgiNkJA.js.map} +1 -1
  5. package/dist/{CodeBlock-BbIC5BRK.cjs → CodeBlock-C3mG_seZ.cjs} +2 -2
  6. package/dist/{CodeBlock-BbIC5BRK.cjs.map → CodeBlock-C3mG_seZ.cjs.map} +1 -1
  7. package/dist/{CodeBlock-C8A6E-tJ.js → CodeBlock-DrWRSRmO.js} +2 -2
  8. package/dist/{CodeBlock-C8A6E-tJ.js.map → CodeBlock-DrWRSRmO.js.map} +1 -1
  9. package/dist/{ColorRamp-B_-ZPqMa.cjs → ColorRamp-D9_5JULy.cjs} +2 -2
  10. package/dist/{ColorRamp-B_-ZPqMa.cjs.map → ColorRamp-D9_5JULy.cjs.map} +1 -1
  11. package/dist/{ColorRamp-CCfImssD.js → ColorRamp-Mp_ddhmb.js} +2 -2
  12. package/dist/{ColorRamp-CCfImssD.js.map → ColorRamp-Mp_ddhmb.js.map} +1 -1
  13. package/dist/{CopyButton-CUEEp_Na.cjs → CopyButton-DZeYcG68.cjs} +2 -2
  14. package/dist/CopyButton-DZeYcG68.cjs.map +1 -0
  15. package/dist/{CopyButton-BD5sN32G.js → CopyButton-Yg5ZVTiX.js} +2 -2
  16. package/dist/CopyButton-Yg5ZVTiX.js.map +1 -0
  17. package/dist/{DescriptionList-BD65gv7C.js → DescriptionList-CG08_qwT.js} +2 -2
  18. package/dist/{DescriptionList-BD65gv7C.js.map → DescriptionList-CG08_qwT.js.map} +1 -1
  19. package/dist/{DescriptionList-BlAJIabM.cjs → DescriptionList-S9C6Tp90.cjs} +2 -2
  20. package/dist/{DescriptionList-BlAJIabM.cjs.map → DescriptionList-S9C6Tp90.cjs.map} +1 -1
  21. package/dist/{ErrorBoundary-sQY2sFdu.js → ErrorBoundary-BILk_3fM.js} +2 -2
  22. package/dist/{ErrorBoundary-sQY2sFdu.js.map → ErrorBoundary-BILk_3fM.js.map} +1 -1
  23. package/dist/{ErrorBoundary-6HvsCoJ8.cjs → ErrorBoundary-JCNa0_Ys.cjs} +2 -2
  24. package/dist/{ErrorBoundary-6HvsCoJ8.cjs.map → ErrorBoundary-JCNa0_Ys.cjs.map} +1 -1
  25. package/dist/{ErrorState-DdxOeZy2.js → ErrorState-DI28ZCJN.js} +3 -3
  26. package/dist/{ErrorState-DdxOeZy2.js.map → ErrorState-DI28ZCJN.js.map} +1 -1
  27. package/dist/{ErrorState-To_MAOf4.cjs → ErrorState-MQBeyF2n.cjs} +2 -2
  28. package/dist/{ErrorState-To_MAOf4.cjs.map → ErrorState-MQBeyF2n.cjs.map} +1 -1
  29. package/dist/{FileUploader-CWH-RS-z.js → FileUploader-DwAl3UXI.js} +2 -2
  30. package/dist/{FileUploader-CWH-RS-z.js.map → FileUploader-DwAl3UXI.js.map} +1 -1
  31. package/dist/{FileUploader-H4k4Cm5B.cjs → FileUploader-FMVbj7rz.cjs} +2 -2
  32. package/dist/{FileUploader-H4k4Cm5B.cjs.map → FileUploader-FMVbj7rz.cjs.map} +1 -1
  33. package/dist/{LocationPicker-CHGJkMXl.js → LocationPicker-Ce8aLubZ.js} +3 -3
  34. package/dist/{LocationPicker-CHGJkMXl.js.map → LocationPicker-Ce8aLubZ.js.map} +1 -1
  35. package/dist/{LocationPicker-CgzZnTFE.cjs → LocationPicker-D-sT4rSe.cjs} +2 -2
  36. package/dist/{LocationPicker-CgzZnTFE.cjs.map → LocationPicker-D-sT4rSe.cjs.map} +1 -1
  37. package/dist/Modal-DTnB_mGg.js +206 -0
  38. package/dist/Modal-DTnB_mGg.js.map +1 -0
  39. package/dist/Modal-DjSOhZqd.cjs +2 -0
  40. package/dist/Modal-DjSOhZqd.cjs.map +1 -0
  41. package/dist/{PageError-9tfvBssn.js → PageError-BORFaxGW.js} +2 -2
  42. package/dist/{PageError-9tfvBssn.js.map → PageError-BORFaxGW.js.map} +1 -1
  43. package/dist/{PageError-CJHOUfCa.cjs → PageError-aUV0vUfB.cjs} +2 -2
  44. package/dist/{PageError-CJHOUfCa.cjs.map → PageError-aUV0vUfB.cjs.map} +1 -1
  45. package/dist/{PaletteField-AMZpUMID.js → PaletteField-ChG2VQtS.js} +2 -2
  46. package/dist/{PaletteField-AMZpUMID.js.map → PaletteField-ChG2VQtS.js.map} +1 -1
  47. package/dist/{PaletteField-ClCvXFdN.cjs → PaletteField-DQ1OvXqe.cjs} +2 -2
  48. package/dist/{PaletteField-ClCvXFdN.cjs.map → PaletteField-DQ1OvXqe.cjs.map} +1 -1
  49. package/dist/{RemoveDialog-yEZm6JaC.cjs → RemoveDialog-B_Y18MeJ.cjs} +2 -2
  50. package/dist/{RemoveDialog-yEZm6JaC.cjs.map → RemoveDialog-B_Y18MeJ.cjs.map} +1 -1
  51. package/dist/{RemoveDialog-BfPTOs1y.js → RemoveDialog-ayAfGXpB.js} +2 -2
  52. package/dist/{RemoveDialog-BfPTOs1y.js.map → RemoveDialog-ayAfGXpB.js.map} +1 -1
  53. package/dist/{RichTextEditor-CitILCiw.cjs → RichTextEditor-DSvSaszc.cjs} +2 -2
  54. package/dist/{RichTextEditor-CitILCiw.cjs.map → RichTextEditor-DSvSaszc.cjs.map} +1 -1
  55. package/dist/{RichTextEditor-CgItVhAR.js → RichTextEditor-itYD1PfE.js} +2 -2
  56. package/dist/{RichTextEditor-CgItVhAR.js.map → RichTextEditor-itYD1PfE.js.map} +1 -1
  57. package/dist/{RootLayout-CVYEk7Wi.js → RootLayout-BlYjYAYk.js} +3 -3
  58. package/dist/{RootLayout-CVYEk7Wi.js.map → RootLayout-BlYjYAYk.js.map} +1 -1
  59. package/dist/{RootLayout-DJ2Xv35m.cjs → RootLayout-D5nlzYlH.cjs} +2 -2
  60. package/dist/{RootLayout-DJ2Xv35m.cjs.map → RootLayout-D5nlzYlH.cjs.map} +1 -1
  61. package/dist/{SpeedDial-C2hkC_8p.js → SpeedDial-CEBAWIYo.js} +2 -2
  62. package/dist/{SpeedDial-C2hkC_8p.js.map → SpeedDial-CEBAWIYo.js.map} +1 -1
  63. package/dist/{SpeedDial-DRSIilyC.cjs → SpeedDial-D6me6w_m.cjs} +2 -2
  64. package/dist/{SpeedDial-DRSIilyC.cjs.map → SpeedDial-D6me6w_m.cjs.map} +1 -1
  65. package/dist/{Table-DDPzTA1H.cjs → Table-CFl3OYlo.cjs} +2 -2
  66. package/dist/{Table-DDPzTA1H.cjs.map → Table-CFl3OYlo.cjs.map} +1 -1
  67. package/dist/{Table-AXMp0eiw.js → Table-DChIfjqj.js} +2 -2
  68. package/dist/{Table-AXMp0eiw.js.map → Table-DChIfjqj.js.map} +1 -1
  69. package/dist/Tooltip-BbT0uNzy.js +152 -0
  70. package/dist/Tooltip-BbT0uNzy.js.map +1 -0
  71. package/dist/Tooltip-DmMq5R1x.cjs +2 -0
  72. package/dist/Tooltip-DmMq5R1x.cjs.map +1 -0
  73. package/dist/components/CodeBlock/index.cjs +1 -1
  74. package/dist/components/CodeBlock/index.js +1 -1
  75. package/dist/components/ColorRamp/index.cjs +1 -1
  76. package/dist/components/ColorRamp/index.js +1 -1
  77. package/dist/components/CopyButton/CopyButton.d.cts +1 -1
  78. package/dist/components/CopyButton/CopyButton.d.ts +1 -1
  79. package/dist/components/CopyButton/index.cjs +1 -1
  80. package/dist/components/CopyButton/index.js +1 -1
  81. package/dist/components/DescriptionList/index.cjs +1 -1
  82. package/dist/components/DescriptionList/index.js +1 -1
  83. package/dist/components/ErrorState/index.cjs +1 -1
  84. package/dist/components/ErrorState/index.js +2 -2
  85. package/dist/components/FileUploader/index.cjs +1 -1
  86. package/dist/components/FileUploader/index.js +1 -1
  87. package/dist/components/LocationPicker/index.cjs +1 -1
  88. package/dist/components/LocationPicker/index.js +1 -1
  89. package/dist/components/Modal/index.cjs +1 -1
  90. package/dist/components/Modal/index.js +1 -1
  91. package/dist/components/PageError/index.cjs +1 -1
  92. package/dist/components/PageError/index.js +1 -1
  93. package/dist/components/PaletteField/index.cjs +1 -1
  94. package/dist/components/PaletteField/index.js +1 -1
  95. package/dist/components/RemoveDialog/index.cjs +1 -1
  96. package/dist/components/RemoveDialog/index.js +1 -1
  97. package/dist/components/RichTextEditor/index.cjs +1 -1
  98. package/dist/components/RichTextEditor/index.js +1 -1
  99. package/dist/components/RootLayout/index.cjs +1 -1
  100. package/dist/components/RootLayout/index.js +1 -1
  101. package/dist/components/SpeedDial/index.cjs +1 -1
  102. package/dist/components/SpeedDial/index.js +1 -1
  103. package/dist/components/Table/index.cjs +1 -1
  104. package/dist/components/Table/index.js +1 -1
  105. package/dist/components/Tooltip/Tooltip.d.cts +5 -3
  106. package/dist/components/Tooltip/Tooltip.d.ts +5 -3
  107. package/dist/components/Tooltip/index.cjs +1 -1
  108. package/dist/components/Tooltip/index.js +1 -1
  109. package/dist/entries/app.cjs +1 -1
  110. package/dist/entries/app.js +1 -1
  111. package/dist/entries/components.cjs +1 -1
  112. package/dist/entries/components.js +17 -17
  113. package/dist/index.cjs +1 -1
  114. package/dist/index.js +18 -18
  115. package/package.json +1 -1
  116. package/dist/CopyButton-BD5sN32G.js.map +0 -1
  117. package/dist/CopyButton-CUEEp_Na.cjs.map +0 -1
  118. package/dist/Modal-BkO_6afj.js +0 -195
  119. package/dist/Modal-BkO_6afj.js.map +0 -1
  120. package/dist/Modal-D8V-JZBg.cjs +0 -2
  121. package/dist/Modal-D8V-JZBg.cjs.map +0 -1
  122. package/dist/Tooltip-Bw-1Xk2Z.cjs +0 -2
  123. package/dist/Tooltip-Bw-1Xk2Z.cjs.map +0 -1
  124. package/dist/Tooltip-DsDv4NOq.js +0 -135
  125. package/dist/Tooltip-DsDv4NOq.js.map +0 -1
@@ -0,0 +1,206 @@
1
+ import { jsx as c, jsxs as S } from "react/jsx-runtime";
2
+ import { forwardRef as Q, useRef as y, useState as ee, useEffect as W, useId as K } from "react";
3
+ import { clsx as T } from "clsx";
4
+ import { b as U, a as te } from "./Icon-D5TxgfCc.js";
5
+ import { I as V } from "./IconButton-06pmftMo.js";
6
+ import { createPortal as le } from "react-dom";
7
+ import { u as ne } from "./useLockBodyScroll-DwBILLPK.js";
8
+ import { a as ae } from "./ConfigProvider-DeMASTVo.js";
9
+ const re = "_overlay_gwv2a_6", ce = {
10
+ overlay: re
11
+ }, oe = Q(function({
12
+ open: l,
13
+ onClose: t,
14
+ closeOnBackdrop: d = !0,
15
+ closeOnEscape: s = !0,
16
+ lockScroll: N = !0,
17
+ dim: o,
18
+ className: u,
19
+ style: i,
20
+ children: A,
21
+ onKeyDown: f,
22
+ onMouseDown: h,
23
+ onClick: _,
24
+ ...M
25
+ }, D) {
26
+ const g = y(null), [O, E] = ee(!1);
27
+ ne(l && N), W(() => {
28
+ if (!l) {
29
+ E(!1);
30
+ return;
31
+ }
32
+ const n = requestAnimationFrame(() => E(!0));
33
+ return () => cancelAnimationFrame(n);
34
+ }, [l]);
35
+ const v = (n) => {
36
+ n.key === "Escape" && (n.stopPropagation(), s && (t == null || t())), f == null || f(n);
37
+ }, k = (n) => {
38
+ g.current = n.target, h == null || h(n);
39
+ }, b = (n) => {
40
+ d && n.target === n.currentTarget && g.current === n.currentTarget && (t == null || t()), _ == null || _(n);
41
+ };
42
+ return !l || typeof document > "u" ? null : le(
43
+ /* @__PURE__ */ c(
44
+ "div",
45
+ {
46
+ ref: D,
47
+ className: T(ce.overlay, u),
48
+ "data-open": O ? "true" : "false",
49
+ style: o != null ? { "--tz-overlay-dim": o, ...i } : i,
50
+ onKeyDown: v,
51
+ onMouseDown: k,
52
+ onClick: b,
53
+ ...M,
54
+ children: A
55
+ }
56
+ ),
57
+ document.body
58
+ );
59
+ }), se = "_overlay_1lotx_3", ie = "_modal_1lotx_25", de = "_sm_1lotx_48", ue = "_md_1lotx_51", fe = "_lg_1lotx_54", me = "_fullScreen_1lotx_57", _e = "_header_1lotx_74", be = "_headerDivided_1lotx_83", xe = "_headerMain_1lotx_86", ye = "_icon_1lotx_93", he = "_headerText_1lotx_96", ve = "_title_1lotx_103", pe = "_subtitle_1lotx_115", Ne = "_close_1lotx_127", ge = "_body_1lotx_132", Ee = "_footer_1lotx_143", a = {
60
+ overlay: se,
61
+ modal: ie,
62
+ sm: de,
63
+ md: ue,
64
+ lg: fe,
65
+ fullScreen: me,
66
+ header: _e,
67
+ headerDivided: be,
68
+ headerMain: xe,
69
+ icon: ye,
70
+ headerText: he,
71
+ title: ve,
72
+ subtitle: pe,
73
+ close: Ne,
74
+ body: ge,
75
+ footer: Ee
76
+ }, Se = new Set(te), G = 'a[href]:not([tabindex="-1"]), button:not(:disabled):not([tabindex="-1"]), textarea:not(:disabled):not([tabindex="-1"]), input:not(:disabled):not([tabindex="-1"]), select:not(:disabled):not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])', Te = 'input:not([type="hidden"]):not(:disabled):not([tabindex="-1"]), textarea:not(:disabled):not([tabindex="-1"]), select:not(:disabled):not([tabindex="-1"]), [contenteditable]:not([contenteditable="false"]):not([tabindex="-1"])';
77
+ function J(I) {
78
+ const l = Array.from(I);
79
+ for (const t of l) {
80
+ const d = t instanceof HTMLInputElement && t.type === "radio" && !t.checked ? l.find(
81
+ (s) => s instanceof HTMLInputElement && s.type === "radio" && s.name === t.name && s.checked
82
+ ) ?? t : t;
83
+ if (d.focus({ preventScroll: !0 }), document.activeElement === d) return !0;
84
+ }
85
+ return !1;
86
+ }
87
+ const Re = Q(function({
88
+ open: l,
89
+ onClose: t,
90
+ size: d = "md",
91
+ scrollBehavior: s = "outside",
92
+ placement: N = "center",
93
+ title: o,
94
+ description: u,
95
+ icon: i,
96
+ color: A = "accent",
97
+ footer: f,
98
+ bodyClassName: h,
99
+ showCloseButton: _ = !0,
100
+ closeOnBackdrop: M = !0,
101
+ closeOnEscape: D = !0,
102
+ ariaLabel: g,
103
+ className: O,
104
+ style: E,
105
+ children: v,
106
+ ...k
107
+ }, b) {
108
+ const n = ae(), F = y(null), q = y(null), P = y(null), z = K(), H = K(), j = y(null), B = y(!1);
109
+ l !== B.current && (B.current = l, l && typeof document < "u" && (j.current = document.activeElement)), W(() => {
110
+ var m, p;
111
+ if (!l) return;
112
+ const e = F.current, r = j.current;
113
+ return e != null && e.contains(document.activeElement) || J(((m = q.current) == null ? void 0 : m.querySelectorAll(Te)) ?? []) || J(((p = P.current) == null ? void 0 : p.querySelectorAll(G)) ?? []) || e == null || e.focus({ preventScroll: !0 }), () => {
114
+ var x;
115
+ r != null && r.isConnected && ((x = r.focus) == null || x.call(r));
116
+ };
117
+ }, [l]);
118
+ const X = (e) => {
119
+ F.current = e, typeof b == "function" ? b(e) : b && (b.current = e);
120
+ }, Y = (e) => {
121
+ if (e.key !== "Tab") return;
122
+ const r = F.current;
123
+ if (!r) return;
124
+ const m = [...r.querySelectorAll(G)].filter(
125
+ ($) => $.offsetParent !== null || $ === document.activeElement
126
+ );
127
+ if (m.length === 0) {
128
+ e.preventDefault(), r.focus();
129
+ return;
130
+ }
131
+ const p = m[0], x = m[m.length - 1], R = document.activeElement;
132
+ e.shiftKey && (R === p || R === r) ? (e.preventDefault(), x.focus()) : !e.shiftKey && R === x && (e.preventDefault(), p.focus());
133
+ };
134
+ if (!l) return null;
135
+ const Z = N !== "center", L = i == null ? null : typeof i == "string" && Se.has(i) ? /* @__PURE__ */ c(U, { name: i }) : i, w = o != null || u != null || L != null || _, C = v != null || f != null;
136
+ return /* @__PURE__ */ c(
137
+ oe,
138
+ {
139
+ open: l,
140
+ onClose: t,
141
+ closeOnBackdrop: M,
142
+ closeOnEscape: D,
143
+ onKeyDown: Y,
144
+ className: a.overlay,
145
+ "data-size": d,
146
+ "data-placement": N,
147
+ "data-scroll": Z ? "inside" : s,
148
+ children: /* @__PURE__ */ S(
149
+ "div",
150
+ {
151
+ ref: X,
152
+ className: T(a.modal, a[d], O),
153
+ role: "dialog",
154
+ "aria-modal": "true",
155
+ "aria-labelledby": o != null ? z : void 0,
156
+ "aria-label": o == null ? g : void 0,
157
+ "aria-describedby": u != null ? H : void 0,
158
+ tabIndex: -1,
159
+ style: E,
160
+ ...k,
161
+ children: [
162
+ w && /* @__PURE__ */ S("div", { className: T(a.header, C && a.headerDivided), children: [
163
+ /* @__PURE__ */ S("div", { className: a.headerMain, children: [
164
+ L != null && /* @__PURE__ */ c(
165
+ V,
166
+ {
167
+ variant: "filled",
168
+ size: "sm",
169
+ color: A,
170
+ nonClickable: !0,
171
+ "aria-hidden": !0,
172
+ className: a.icon,
173
+ children: L
174
+ }
175
+ ),
176
+ (o != null || u != null) && /* @__PURE__ */ S("div", { className: a.headerText, children: [
177
+ o != null && /* @__PURE__ */ c("div", { id: z, className: a.title, children: o }),
178
+ u != null && /* @__PURE__ */ c("div", { id: H, className: a.subtitle, children: u })
179
+ ] })
180
+ ] }),
181
+ _ && /* @__PURE__ */ c(
182
+ V,
183
+ {
184
+ variant: "filled",
185
+ color: "primary",
186
+ size: "sm",
187
+ className: a.close,
188
+ "aria-label": n("common.close"),
189
+ onClick: t,
190
+ children: /* @__PURE__ */ c(U, { name: "Close" })
191
+ }
192
+ )
193
+ ] }),
194
+ v != null && /* @__PURE__ */ c("div", { ref: q, className: T(a.body, h), children: v }),
195
+ f != null && /* @__PURE__ */ c("div", { ref: P, className: a.footer, children: f })
196
+ ]
197
+ }
198
+ )
199
+ }
200
+ );
201
+ });
202
+ export {
203
+ Re as M,
204
+ oe as O
205
+ };
206
+ //# sourceMappingURL=Modal-DTnB_mGg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Modal-DTnB_mGg.js","sources":["../src/components/Overlay/Overlay.tsx","../src/components/Modal/Modal.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { clsx } from 'clsx'\nimport { useLockBodyScroll } from '../../hooks/useLockBodyScroll'\nimport styles from './Overlay.module.css'\n\nexport interface OverlayProps extends HTMLAttributes<HTMLDivElement> {\n /** Whether the overlay is mounted + shown — gates the portal and drives the fade-in. */\n open: boolean\n /** Requested dismiss — fired on a backdrop click (`closeOnBackdrop`) or Escape (`closeOnEscape`). */\n onClose?: () => void\n /** Close when the scrim itself (outside the content) is clicked. Defaults to `true`. */\n closeOnBackdrop?: boolean\n /** Close when Escape is pressed while focus is within the overlay. Defaults to `true`. */\n closeOnEscape?: boolean\n /** Lock page scroll while open. Defaults to `true`. */\n lockScroll?: boolean\n /** Scrim darkness — the black backdrop's alpha (0–1). Defaults to `0.45`. */\n dim?: number\n /** Overlay content — typically a centered dialog / box / media. */\n children?: ReactNode\n}\n\n/**\n * The shared backdrop primitive behind every dimmed, page-blocking surface (`Modal`, the FileUploader\n * lightbox). Portals a fixed full-viewport scrim to `<body>`, locks page scroll, fades in (opacity keyed\n * off a rAF `visible` flag → `data-open`), and dismisses on **backdrop-click** (gated on the pointer-down\n * AND click both landing on the scrim, so a text-selection drag ending on it doesn't close) / **Escape**\n * (swallowed via `stopPropagation` so it can't reach a React-tree ancestor overlay). Centers its children\n * by default; consumers layer their own padding / scroll / placement via `className` + `data-*` and read\n * the shared `data-open` on the scrim for their own box's enter transform. A consumer `onKeyDown` still\n * fires (after the Escape handling) so it can add e.g. a Tab focus-trap. Internal — no `index.ts`, so it's\n * never exposed as a package subpath; import it via `../Overlay/Overlay`. The ref points at the scrim div.\n */\nexport const Overlay = forwardRef<HTMLDivElement, OverlayProps>(function Overlay(\n {\n open,\n onClose,\n closeOnBackdrop = true,\n closeOnEscape = true,\n lockScroll = true,\n dim,\n className,\n style,\n children,\n onKeyDown,\n onMouseDown,\n onClick,\n ...props\n },\n ref,\n) {\n const mouseDownTarget = useRef<EventTarget | null>(null)\n const [visible, setVisible] = useState(false)\n\n useLockBodyScroll(open && lockScroll)\n\n // enter animation: flip `visible` on the frame after mount so the opacity transition runs\n useEffect(() => {\n if (!open) {\n setVisible(false)\n return\n }\n const raf = requestAnimationFrame(() => setVisible(true))\n return () => cancelAnimationFrame(raf)\n }, [open])\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Escape') {\n // the overlay owns the page — swallow Escape so it can't reach a React-tree ancestor overlay (e.g.\n // an outer Modal) even when this one isn't Escape-dismissible\n event.stopPropagation()\n if (closeOnEscape) onClose?.()\n }\n onKeyDown?.(event)\n }\n // remember where the pointer went down so the click handler can tell a scrim click from a drag that\n // started inside the content and ended on the scrim (a text-selection drag)\n const handleMouseDown = (event: MouseEvent<HTMLDivElement>) => {\n mouseDownTarget.current = event.target\n onMouseDown?.(event)\n }\n const handleClick = (event: MouseEvent<HTMLDivElement>) => {\n if (\n closeOnBackdrop &&\n event.target === event.currentTarget &&\n mouseDownTarget.current === event.currentTarget\n ) {\n onClose?.()\n }\n onClick?.(event)\n }\n\n if (!open || typeof document === 'undefined') return null\n\n return createPortal(\n <div\n ref={ref}\n className={clsx(styles.overlay, className)}\n data-open={visible ? 'true' : 'false'}\n style={dim != null ? ({ '--tz-overlay-dim': dim, ...style } as CSSProperties) : style}\n onKeyDown={handleKeyDown}\n onMouseDown={handleMouseDown}\n onClick={handleClick}\n {...props}\n >\n {children}\n </div>,\n document.body,\n )\n})\n","import {\n forwardRef,\n useEffect,\n useId,\n useRef,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport { ICON_NAMES, type IconName } from '../../icons/names'\nimport { useT, type ThemeColor } from '../../theme'\nimport { Icon } from '../Icon'\nimport { IconButton } from '../IconButton'\nimport { Overlay } from '../Overlay/Overlay'\nimport styles from './Modal.module.css'\n\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'fullScreen'\nexport type ModalScrollBehavior = 'inside' | 'outside'\nexport type ModalPlacement = 'center' | 'left' | 'right'\n\nconst ICON_NAME_SET = new Set<string>(ICON_NAMES)\n\n/** Focusable descendants used by the focus trap + initial focus. */\n// every clause excludes tabindex=\"-1\" so decorative/programmatic-only nodes (e.g. the header's\n// `nonClickable aria-hidden` IconButton) don't count as trap boundaries or initial-focus targets\nconst FOCUSABLE =\n 'a[href]:not([tabindex=\"-1\"]), button:not(:disabled):not([tabindex=\"-1\"]), textarea:not(:disabled):not([tabindex=\"-1\"]), input:not(:disabled):not([tabindex=\"-1\"]), select:not(:disabled):not([tabindex=\"-1\"]), [tabindex]:not([tabindex=\"-1\"])'\n\n/** Body FIELDS — where a form modal's initial focus lands (buttons / links in content don't count). */\nconst FIELD =\n 'input:not([type=\"hidden\"]):not(:disabled):not([tabindex=\"-1\"]), textarea:not(:disabled):not([tabindex=\"-1\"]), select:not(:disabled):not([tabindex=\"-1\"]), [contenteditable]:not([contenteditable=\"false\"]):not([tabindex=\"-1\"])'\n\n/**\n * Focus the first candidate that actually takes focus (a field inside a hidden tab panel won't). A radio\n * lands on its group's checked option, as Tab would.\n */\nfunction focusFirst(candidates: ArrayLike<HTMLElement>): boolean {\n const list = Array.from(candidates)\n for (const candidate of list) {\n const el =\n candidate instanceof HTMLInputElement && candidate.type === 'radio' && !candidate.checked\n ? (list.find(\n (other) =>\n other instanceof HTMLInputElement &&\n other.type === 'radio' &&\n other.name === candidate.name &&\n other.checked,\n ) ?? candidate)\n : candidate\n el.focus({ preventScroll: true })\n if (document.activeElement === el) return true\n }\n return false\n}\n\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'color'> {\n /** Whether the modal is open — gates the portal and drives the enter animation. */\n open: boolean\n /** Called when a dismiss is requested (close button, backdrop click, or Escape). */\n onClose: () => void\n /** Preset width: `sm` 360 · `md` 620 · `lg` 900 px, or `fullScreen` (fills the viewport). Defaults to `md`. */\n size?: ModalSize\n /**\n * Where overflow scrolls when the content is taller than the viewport: `outside` (default — the\n * whole dialog grows and the overlay/page scrolls) or `inside` (the body scrolls while the header +\n * footer stay pinned). Ignored for side-drawer placements (`left`/`right`), which always scroll inside.\n */\n scrollBehavior?: ModalScrollBehavior\n /**\n * Where the dialog sits: `center` (default — the standard centered modal), or `left` / `right` to\n * make it a **full-height side drawer** (sheet) that slides in from that edge. A drawer takes its\n * width from `size`, fills the viewport height, and always scrolls its body inside (header + footer\n * stay pinned).\n */\n placement?: ModalPlacement\n /** Header title. When set, labels the dialog (`aria-labelledby`). */\n title?: ReactNode\n /** Muted description line under the title (e.g. a confirm prompt). */\n description?: ReactNode\n /** Leading header icon shown in a tinted box (like `Card`) — a known `IconName` or any node. */\n icon?: IconName | ReactNode\n /** Theme color that tints the leading icon box. Defaults to `accent`. */\n color?: ThemeColor\n /** Footer slot — right-aligned actions over a top divider (e.g. Cancel / Confirm buttons). */\n footer?: ReactNode\n /** Extra class on the scrollable body element — to tune its padding / layout (e.g. a compact confirm). */\n bodyClassName?: string\n /** Show the header × close button. Defaults to `true`. */\n showCloseButton?: boolean\n /** Close when the backdrop (outside the dialog) is clicked. Defaults to `true`. */\n closeOnBackdrop?: boolean\n /** Close when Escape is pressed. Defaults to `true`. */\n closeOnEscape?: boolean\n /** Accessible label — used when there's no `title`. */\n ariaLabel?: string\n /** Dialog body. */\n children?: ReactNode\n}\n\n/**\n * A centered, backdrop-dimmed overlay dialog. Portaled to `<body>`, it locks page scroll while open,\n * traps Tab focus inside, restores focus to the previously-focused element on close, and dismisses on\n * close button / backdrop click / Escape (each toggleable). Sizes are `sm`/`md`/`lg` (max-width caps)\n * or `fullScreen`; `scrollBehavior` picks inside-body vs. whole-dialog scrolling. `placement` turns it\n * into a full-height side drawer (`left`/`right`) that slides in from the edge. The header mirrors\n * `Card` — an optional tinted `icon` box + `title` + `description`. Controlled by `open` + `onClose`.\n * The forwarded ref points at the dialog element.\n *\n * @example\n * const { isOpen, open, close } = useDisclosure()\n * <Modal open={isOpen} onClose={close} title=\"Edit\" footer={<Button onClick={close}>Done</Button>}>\n * …\n * </Modal>\n */\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\n {\n open,\n onClose,\n size = 'md',\n scrollBehavior = 'outside',\n placement = 'center',\n title,\n description,\n icon,\n color = 'accent',\n footer,\n bodyClassName,\n showCloseButton = true,\n closeOnBackdrop = true,\n closeOnEscape = true,\n ariaLabel,\n className,\n style,\n children,\n ...props\n },\n forwardedRef,\n) {\n const t = useT()\n const panelRef = useRef<HTMLDivElement | null>(null)\n const bodyRef = useRef<HTMLDivElement | null>(null)\n const footerRef = useRef<HTMLDivElement | null>(null)\n const titleId = useId()\n const descId = useId()\n\n // portal + scrim + scroll-lock + fade-in + backdrop-dismiss are handled by the shared `<Overlay>`\n\n // the element focused before opening — read during the render that opens the modal, because an\n // `autoFocus` element inside moves focus on mount, before any effect could see the opener\n const openerRef = useRef<HTMLElement | null>(null)\n const wasOpenRef = useRef(false)\n if (open !== wasOpenRef.current) {\n wasOpenRef.current = open\n if (open && typeof document !== 'undefined') {\n openerRef.current = document.activeElement as HTMLElement | null\n }\n }\n\n // focus management on open — an explicit `autoFocus` inside wins; else the first body FIELD (form\n // modals land on their first input), else the first footer action (a confirm dialog lands on\n // Cancel/Confirm, a sheet on its Close), else the dialog panel itself. Never \"any control in the\n // body\": a copy / icon / link button deep in content isn't where focus should start (it would also\n // pop that button's tooltip). The Tab trap still reaches every control. Restore on close.\n useEffect(() => {\n if (!open) return\n const panel = panelRef.current\n const opener = openerRef.current\n if (!panel?.contains(document.activeElement)) {\n // preventScroll so opening an `outside`-scroll modal doesn't jump the overlay to a below-fold control\n const placed =\n focusFirst(bodyRef.current?.querySelectorAll<HTMLElement>(FIELD) ?? []) ||\n focusFirst(footerRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE) ?? [])\n if (!placed) panel?.focus({ preventScroll: true })\n }\n // only restore if the element is still mounted (it may have unmounted while the modal was open)\n return () => {\n if (opener?.isConnected) opener.focus?.()\n }\n }, [open])\n\n // merge the panel ref (positioning/focus) with the forwarded ref (consumer access)\n const setPanelRef = (node: HTMLDivElement | null) => {\n panelRef.current = node\n if (typeof forwardedRef === 'function') forwardedRef(node)\n else if (forwardedRef) forwardedRef.current = node\n }\n\n // trap Tab within the dialog so focus can't strand on the locked page behind it (Escape + backdrop\n // dismissal live in `<Overlay>`; this fires after the overlay's own handler)\n const handleTabTrap = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key !== 'Tab') return\n const panel = panelRef.current\n if (!panel) return\n const focusables = [...panel.querySelectorAll<HTMLElement>(FOCUSABLE)].filter(\n (el) => el.offsetParent !== null || el === document.activeElement,\n )\n if (focusables.length === 0) {\n event.preventDefault()\n panel.focus()\n return\n }\n const first = focusables[0]\n const last = focusables[focusables.length - 1]\n const active = document.activeElement\n if (event.shiftKey && (active === first || active === panel)) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n if (!open) return null\n\n // a side drawer (left/right) is always full-height with the body scrolling inside (header/footer pinned)\n const isDrawer = placement !== 'center'\n\n const renderedIcon =\n icon == null ? null : typeof icon === 'string' && ICON_NAME_SET.has(icon) ? (\n <Icon name={icon as IconName} />\n ) : (\n icon\n )\n\n const hasHeader = title != null || description != null || renderedIcon != null || showCloseButton\n // a divider sets the header apart from the body/footer, like Card\n const headerDivided = children != null || footer != null\n\n return (\n <Overlay\n open={open}\n onClose={onClose}\n closeOnBackdrop={closeOnBackdrop}\n closeOnEscape={closeOnEscape}\n onKeyDown={handleTabTrap}\n className={styles.overlay}\n data-size={size}\n data-placement={placement}\n data-scroll={isDrawer ? 'inside' : scrollBehavior}\n >\n <div\n ref={setPanelRef}\n className={clsx(styles.modal, styles[size], className)}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title != null ? titleId : undefined}\n aria-label={title == null ? ariaLabel : undefined}\n aria-describedby={description != null ? descId : undefined}\n tabIndex={-1}\n style={style as CSSProperties}\n {...props}\n >\n {hasHeader && (\n <div className={clsx(styles.header, headerDivided && styles.headerDivided)}>\n <div className={styles.headerMain}>\n {renderedIcon != null && (\n <IconButton\n variant=\"filled\"\n size=\"sm\"\n color={color}\n nonClickable\n aria-hidden\n className={styles.icon}\n >\n {renderedIcon}\n </IconButton>\n )}\n {(title != null || description != null) && (\n <div className={styles.headerText}>\n {title != null && (\n <div id={titleId} className={styles.title}>\n {title}\n </div>\n )}\n {description != null && (\n <div id={descId} className={styles.subtitle}>\n {description}\n </div>\n )}\n </div>\n )}\n </div>\n {showCloseButton && (\n <IconButton\n variant=\"filled\"\n color=\"primary\"\n size=\"sm\"\n className={styles.close}\n aria-label={t('common.close')}\n onClick={onClose}\n >\n <Icon name=\"Close\" />\n </IconButton>\n )}\n </div>\n )}\n {children != null && (\n <div ref={bodyRef} className={clsx(styles.body, bodyClassName)}>\n {children}\n </div>\n )}\n {footer != null && (\n <div ref={footerRef} className={styles.footer}>\n {footer}\n </div>\n )}\n </div>\n </Overlay>\n )\n})\n"],"names":["Overlay","forwardRef","open","onClose","closeOnBackdrop","closeOnEscape","lockScroll","dim","className","style","children","onKeyDown","onMouseDown","onClick","props","ref","mouseDownTarget","useRef","visible","setVisible","useState","useLockBodyScroll","useEffect","raf","handleKeyDown","event","handleMouseDown","handleClick","createPortal","jsx","clsx","styles","ICON_NAME_SET","ICON_NAMES","FOCUSABLE","FIELD","focusFirst","candidates","list","candidate","el","other","Modal","size","scrollBehavior","placement","title","description","icon","color","footer","bodyClassName","showCloseButton","ariaLabel","forwardedRef","t","useT","panelRef","bodyRef","footerRef","titleId","useId","descId","openerRef","wasOpenRef","panel","opener","_a","_b","setPanelRef","node","handleTabTrap","focusables","first","last","active","isDrawer","renderedIcon","Icon","hasHeader","headerDivided","jsxs","IconButton"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";const r=require("react/jsx-runtime"),s=require("react"),E=require("clsx"),k=require("./Icon-B6lz-V0A.cjs"),K=require("./IconButton-CQ25TtBc.cjs"),Y=require("react-dom"),Z=require("./useLockBodyScroll-CIXHZo-Q.cjs"),C=require("./ConfigProvider-B_Bb6pk1.cjs"),ee="_overlay_gwv2a_6",te={overlay:ee},w=s.forwardRef(function({open:n,onClose:t,closeOnBackdrop:u=!0,closeOnEscape:i=!0,lockScroll:N=!0,dim:o,className:f,style:d,children:T,onKeyDown:m,onMouseDown:h,onClick:_,...q},R){const g=s.useRef(null),[p,j]=s.useState(!1);Z.useLockBodyScroll(n&&N),s.useEffect(()=>{if(!n){j(!1);return}const l=requestAnimationFrame(()=>j(!0));return()=>cancelAnimationFrame(l)},[n]);const v=l=>{l.key==="Escape"&&(l.stopPropagation(),i&&(t==null||t())),m==null||m(l)},A=l=>{g.current=l.target,h==null||h(l)},b=l=>{u&&l.target===l.currentTarget&&g.current===l.currentTarget&&(t==null||t()),_==null||_(l)};return!n||typeof document>"u"?null:Y.createPortal(r.jsx("div",{ref:R,className:E.clsx(te.overlay,f),"data-open":p?"true":"false",style:o!=null?{"--tz-overlay-dim":o,...d}:d,onKeyDown:v,onMouseDown:A,onClick:b,...q,children:T}),document.body)}),ne="_overlay_1lotx_3",le="_modal_1lotx_25",re="_sm_1lotx_48",ae="_md_1lotx_51",se="_lg_1lotx_54",ce="_fullScreen_1lotx_57",oe="_header_1lotx_74",ie="_headerDivided_1lotx_83",de="_headerMain_1lotx_86",ue="_icon_1lotx_93",fe="_headerText_1lotx_96",me="_title_1lotx_103",xe="_subtitle_1lotx_115",_e="_close_1lotx_127",be="_body_1lotx_132",ye="_footer_1lotx_143",a={overlay:ne,modal:le,sm:re,md:ae,lg:se,fullScreen:ce,header:oe,headerDivided:ie,headerMain:de,icon:ue,headerText:fe,title:me,subtitle:xe,close:_e,body:be,footer:ye},he=new Set(k.ICON_NAMES),U='a[href]:not([tabindex="-1"]), button:not(:disabled):not([tabindex="-1"]), textarea:not(:disabled):not([tabindex="-1"]), input:not(:disabled):not([tabindex="-1"]), select:not(:disabled):not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])',ve='input:not([type="hidden"]):not(:disabled):not([tabindex="-1"]), textarea:not(:disabled):not([tabindex="-1"]), select:not(:disabled):not([tabindex="-1"]), [contenteditable]:not([contenteditable="false"]):not([tabindex="-1"])';function V(S){const n=Array.from(S);for(const t of n){const u=t instanceof HTMLInputElement&&t.type==="radio"&&!t.checked?n.find(i=>i instanceof HTMLInputElement&&i.type==="radio"&&i.name===t.name&&i.checked)??t:t;if(u.focus({preventScroll:!0}),document.activeElement===u)return!0}return!1}const Ie=s.forwardRef(function({open:n,onClose:t,size:u="md",scrollBehavior:i="outside",placement:N="center",title:o,description:f,icon:d,color:T="accent",footer:m,bodyClassName:h,showCloseButton:_=!0,closeOnBackdrop:q=!0,closeOnEscape:R=!0,ariaLabel:g,className:p,style:j,children:v,...A},b){const l=C.useT(),D=s.useRef(null),L=s.useRef(null),B=s.useRef(null),F=s.useId(),P=s.useId(),z=s.useRef(null),H=s.useRef(!1);n!==H.current&&(H.current=n,n&&typeof document<"u"&&(z.current=document.activeElement)),s.useEffect(()=>{var x,I;if(!n)return;const e=D.current,c=z.current;return e!=null&&e.contains(document.activeElement)||V(((x=L.current)==null?void 0:x.querySelectorAll(ve))??[])||V(((I=B.current)==null?void 0:I.querySelectorAll(U))??[])||e==null||e.focus({preventScroll:!0}),()=>{var y;c!=null&&c.isConnected&&((y=c.focus)==null||y.call(c))}},[n]);const G=e=>{D.current=e,typeof b=="function"?b(e):b&&(b.current=e)},J=e=>{if(e.key!=="Tab")return;const c=D.current;if(!c)return;const x=[...c.querySelectorAll(U)].filter($=>$.offsetParent!==null||$===document.activeElement);if(x.length===0){e.preventDefault(),c.focus();return}const I=x[0],y=x[x.length-1],O=document.activeElement;e.shiftKey&&(O===I||O===c)?(e.preventDefault(),y.focus()):!e.shiftKey&&O===y&&(e.preventDefault(),I.focus())};if(!n)return null;const Q=N!=="center",M=d==null?null:typeof d=="string"&&he.has(d)?r.jsx(k.Icon,{name:d}):d,W=o!=null||f!=null||M!=null||_,X=v!=null||m!=null;return r.jsx(w,{open:n,onClose:t,closeOnBackdrop:q,closeOnEscape:R,onKeyDown:J,className:a.overlay,"data-size":u,"data-placement":N,"data-scroll":Q?"inside":i,children:r.jsxs("div",{ref:G,className:E.clsx(a.modal,a[u],p),role:"dialog","aria-modal":"true","aria-labelledby":o!=null?F:void 0,"aria-label":o==null?g:void 0,"aria-describedby":f!=null?P:void 0,tabIndex:-1,style:j,...A,children:[W&&r.jsxs("div",{className:E.clsx(a.header,X&&a.headerDivided),children:[r.jsxs("div",{className:a.headerMain,children:[M!=null&&r.jsx(K.IconButton,{variant:"filled",size:"sm",color:T,nonClickable:!0,"aria-hidden":!0,className:a.icon,children:M}),(o!=null||f!=null)&&r.jsxs("div",{className:a.headerText,children:[o!=null&&r.jsx("div",{id:F,className:a.title,children:o}),f!=null&&r.jsx("div",{id:P,className:a.subtitle,children:f})]})]}),_&&r.jsx(K.IconButton,{variant:"filled",color:"primary",size:"sm",className:a.close,"aria-label":l("common.close"),onClick:t,children:r.jsx(k.Icon,{name:"Close"})})]}),v!=null&&r.jsx("div",{ref:L,className:E.clsx(a.body,h),children:v}),m!=null&&r.jsx("div",{ref:B,className:a.footer,children:m})]})})});exports.Modal=Ie;exports.Overlay=w;
2
+ //# sourceMappingURL=Modal-DjSOhZqd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Modal-DjSOhZqd.cjs","sources":["../src/components/Overlay/Overlay.tsx","../src/components/Modal/Modal.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type MouseEvent,\n type ReactNode,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { clsx } from 'clsx'\nimport { useLockBodyScroll } from '../../hooks/useLockBodyScroll'\nimport styles from './Overlay.module.css'\n\nexport interface OverlayProps extends HTMLAttributes<HTMLDivElement> {\n /** Whether the overlay is mounted + shown — gates the portal and drives the fade-in. */\n open: boolean\n /** Requested dismiss — fired on a backdrop click (`closeOnBackdrop`) or Escape (`closeOnEscape`). */\n onClose?: () => void\n /** Close when the scrim itself (outside the content) is clicked. Defaults to `true`. */\n closeOnBackdrop?: boolean\n /** Close when Escape is pressed while focus is within the overlay. Defaults to `true`. */\n closeOnEscape?: boolean\n /** Lock page scroll while open. Defaults to `true`. */\n lockScroll?: boolean\n /** Scrim darkness — the black backdrop's alpha (0–1). Defaults to `0.45`. */\n dim?: number\n /** Overlay content — typically a centered dialog / box / media. */\n children?: ReactNode\n}\n\n/**\n * The shared backdrop primitive behind every dimmed, page-blocking surface (`Modal`, the FileUploader\n * lightbox). Portals a fixed full-viewport scrim to `<body>`, locks page scroll, fades in (opacity keyed\n * off a rAF `visible` flag → `data-open`), and dismisses on **backdrop-click** (gated on the pointer-down\n * AND click both landing on the scrim, so a text-selection drag ending on it doesn't close) / **Escape**\n * (swallowed via `stopPropagation` so it can't reach a React-tree ancestor overlay). Centers its children\n * by default; consumers layer their own padding / scroll / placement via `className` + `data-*` and read\n * the shared `data-open` on the scrim for their own box's enter transform. A consumer `onKeyDown` still\n * fires (after the Escape handling) so it can add e.g. a Tab focus-trap. Internal — no `index.ts`, so it's\n * never exposed as a package subpath; import it via `../Overlay/Overlay`. The ref points at the scrim div.\n */\nexport const Overlay = forwardRef<HTMLDivElement, OverlayProps>(function Overlay(\n {\n open,\n onClose,\n closeOnBackdrop = true,\n closeOnEscape = true,\n lockScroll = true,\n dim,\n className,\n style,\n children,\n onKeyDown,\n onMouseDown,\n onClick,\n ...props\n },\n ref,\n) {\n const mouseDownTarget = useRef<EventTarget | null>(null)\n const [visible, setVisible] = useState(false)\n\n useLockBodyScroll(open && lockScroll)\n\n // enter animation: flip `visible` on the frame after mount so the opacity transition runs\n useEffect(() => {\n if (!open) {\n setVisible(false)\n return\n }\n const raf = requestAnimationFrame(() => setVisible(true))\n return () => cancelAnimationFrame(raf)\n }, [open])\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Escape') {\n // the overlay owns the page — swallow Escape so it can't reach a React-tree ancestor overlay (e.g.\n // an outer Modal) even when this one isn't Escape-dismissible\n event.stopPropagation()\n if (closeOnEscape) onClose?.()\n }\n onKeyDown?.(event)\n }\n // remember where the pointer went down so the click handler can tell a scrim click from a drag that\n // started inside the content and ended on the scrim (a text-selection drag)\n const handleMouseDown = (event: MouseEvent<HTMLDivElement>) => {\n mouseDownTarget.current = event.target\n onMouseDown?.(event)\n }\n const handleClick = (event: MouseEvent<HTMLDivElement>) => {\n if (\n closeOnBackdrop &&\n event.target === event.currentTarget &&\n mouseDownTarget.current === event.currentTarget\n ) {\n onClose?.()\n }\n onClick?.(event)\n }\n\n if (!open || typeof document === 'undefined') return null\n\n return createPortal(\n <div\n ref={ref}\n className={clsx(styles.overlay, className)}\n data-open={visible ? 'true' : 'false'}\n style={dim != null ? ({ '--tz-overlay-dim': dim, ...style } as CSSProperties) : style}\n onKeyDown={handleKeyDown}\n onMouseDown={handleMouseDown}\n onClick={handleClick}\n {...props}\n >\n {children}\n </div>,\n document.body,\n )\n})\n","import {\n forwardRef,\n useEffect,\n useId,\n useRef,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\nimport { clsx } from 'clsx'\nimport { ICON_NAMES, type IconName } from '../../icons/names'\nimport { useT, type ThemeColor } from '../../theme'\nimport { Icon } from '../Icon'\nimport { IconButton } from '../IconButton'\nimport { Overlay } from '../Overlay/Overlay'\nimport styles from './Modal.module.css'\n\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'fullScreen'\nexport type ModalScrollBehavior = 'inside' | 'outside'\nexport type ModalPlacement = 'center' | 'left' | 'right'\n\nconst ICON_NAME_SET = new Set<string>(ICON_NAMES)\n\n/** Focusable descendants used by the focus trap + initial focus. */\n// every clause excludes tabindex=\"-1\" so decorative/programmatic-only nodes (e.g. the header's\n// `nonClickable aria-hidden` IconButton) don't count as trap boundaries or initial-focus targets\nconst FOCUSABLE =\n 'a[href]:not([tabindex=\"-1\"]), button:not(:disabled):not([tabindex=\"-1\"]), textarea:not(:disabled):not([tabindex=\"-1\"]), input:not(:disabled):not([tabindex=\"-1\"]), select:not(:disabled):not([tabindex=\"-1\"]), [tabindex]:not([tabindex=\"-1\"])'\n\n/** Body FIELDS — where a form modal's initial focus lands (buttons / links in content don't count). */\nconst FIELD =\n 'input:not([type=\"hidden\"]):not(:disabled):not([tabindex=\"-1\"]), textarea:not(:disabled):not([tabindex=\"-1\"]), select:not(:disabled):not([tabindex=\"-1\"]), [contenteditable]:not([contenteditable=\"false\"]):not([tabindex=\"-1\"])'\n\n/**\n * Focus the first candidate that actually takes focus (a field inside a hidden tab panel won't). A radio\n * lands on its group's checked option, as Tab would.\n */\nfunction focusFirst(candidates: ArrayLike<HTMLElement>): boolean {\n const list = Array.from(candidates)\n for (const candidate of list) {\n const el =\n candidate instanceof HTMLInputElement && candidate.type === 'radio' && !candidate.checked\n ? (list.find(\n (other) =>\n other instanceof HTMLInputElement &&\n other.type === 'radio' &&\n other.name === candidate.name &&\n other.checked,\n ) ?? candidate)\n : candidate\n el.focus({ preventScroll: true })\n if (document.activeElement === el) return true\n }\n return false\n}\n\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'color'> {\n /** Whether the modal is open — gates the portal and drives the enter animation. */\n open: boolean\n /** Called when a dismiss is requested (close button, backdrop click, or Escape). */\n onClose: () => void\n /** Preset width: `sm` 360 · `md` 620 · `lg` 900 px, or `fullScreen` (fills the viewport). Defaults to `md`. */\n size?: ModalSize\n /**\n * Where overflow scrolls when the content is taller than the viewport: `outside` (default — the\n * whole dialog grows and the overlay/page scrolls) or `inside` (the body scrolls while the header +\n * footer stay pinned). Ignored for side-drawer placements (`left`/`right`), which always scroll inside.\n */\n scrollBehavior?: ModalScrollBehavior\n /**\n * Where the dialog sits: `center` (default — the standard centered modal), or `left` / `right` to\n * make it a **full-height side drawer** (sheet) that slides in from that edge. A drawer takes its\n * width from `size`, fills the viewport height, and always scrolls its body inside (header + footer\n * stay pinned).\n */\n placement?: ModalPlacement\n /** Header title. When set, labels the dialog (`aria-labelledby`). */\n title?: ReactNode\n /** Muted description line under the title (e.g. a confirm prompt). */\n description?: ReactNode\n /** Leading header icon shown in a tinted box (like `Card`) — a known `IconName` or any node. */\n icon?: IconName | ReactNode\n /** Theme color that tints the leading icon box. Defaults to `accent`. */\n color?: ThemeColor\n /** Footer slot — right-aligned actions over a top divider (e.g. Cancel / Confirm buttons). */\n footer?: ReactNode\n /** Extra class on the scrollable body element — to tune its padding / layout (e.g. a compact confirm). */\n bodyClassName?: string\n /** Show the header × close button. Defaults to `true`. */\n showCloseButton?: boolean\n /** Close when the backdrop (outside the dialog) is clicked. Defaults to `true`. */\n closeOnBackdrop?: boolean\n /** Close when Escape is pressed. Defaults to `true`. */\n closeOnEscape?: boolean\n /** Accessible label — used when there's no `title`. */\n ariaLabel?: string\n /** Dialog body. */\n children?: ReactNode\n}\n\n/**\n * A centered, backdrop-dimmed overlay dialog. Portaled to `<body>`, it locks page scroll while open,\n * traps Tab focus inside, restores focus to the previously-focused element on close, and dismisses on\n * close button / backdrop click / Escape (each toggleable). Sizes are `sm`/`md`/`lg` (max-width caps)\n * or `fullScreen`; `scrollBehavior` picks inside-body vs. whole-dialog scrolling. `placement` turns it\n * into a full-height side drawer (`left`/`right`) that slides in from the edge. The header mirrors\n * `Card` — an optional tinted `icon` box + `title` + `description`. Controlled by `open` + `onClose`.\n * The forwarded ref points at the dialog element.\n *\n * @example\n * const { isOpen, open, close } = useDisclosure()\n * <Modal open={isOpen} onClose={close} title=\"Edit\" footer={<Button onClick={close}>Done</Button>}>\n * …\n * </Modal>\n */\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\n {\n open,\n onClose,\n size = 'md',\n scrollBehavior = 'outside',\n placement = 'center',\n title,\n description,\n icon,\n color = 'accent',\n footer,\n bodyClassName,\n showCloseButton = true,\n closeOnBackdrop = true,\n closeOnEscape = true,\n ariaLabel,\n className,\n style,\n children,\n ...props\n },\n forwardedRef,\n) {\n const t = useT()\n const panelRef = useRef<HTMLDivElement | null>(null)\n const bodyRef = useRef<HTMLDivElement | null>(null)\n const footerRef = useRef<HTMLDivElement | null>(null)\n const titleId = useId()\n const descId = useId()\n\n // portal + scrim + scroll-lock + fade-in + backdrop-dismiss are handled by the shared `<Overlay>`\n\n // the element focused before opening — read during the render that opens the modal, because an\n // `autoFocus` element inside moves focus on mount, before any effect could see the opener\n const openerRef = useRef<HTMLElement | null>(null)\n const wasOpenRef = useRef(false)\n if (open !== wasOpenRef.current) {\n wasOpenRef.current = open\n if (open && typeof document !== 'undefined') {\n openerRef.current = document.activeElement as HTMLElement | null\n }\n }\n\n // focus management on open — an explicit `autoFocus` inside wins; else the first body FIELD (form\n // modals land on their first input), else the first footer action (a confirm dialog lands on\n // Cancel/Confirm, a sheet on its Close), else the dialog panel itself. Never \"any control in the\n // body\": a copy / icon / link button deep in content isn't where focus should start (it would also\n // pop that button's tooltip). The Tab trap still reaches every control. Restore on close.\n useEffect(() => {\n if (!open) return\n const panel = panelRef.current\n const opener = openerRef.current\n if (!panel?.contains(document.activeElement)) {\n // preventScroll so opening an `outside`-scroll modal doesn't jump the overlay to a below-fold control\n const placed =\n focusFirst(bodyRef.current?.querySelectorAll<HTMLElement>(FIELD) ?? []) ||\n focusFirst(footerRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE) ?? [])\n if (!placed) panel?.focus({ preventScroll: true })\n }\n // only restore if the element is still mounted (it may have unmounted while the modal was open)\n return () => {\n if (opener?.isConnected) opener.focus?.()\n }\n }, [open])\n\n // merge the panel ref (positioning/focus) with the forwarded ref (consumer access)\n const setPanelRef = (node: HTMLDivElement | null) => {\n panelRef.current = node\n if (typeof forwardedRef === 'function') forwardedRef(node)\n else if (forwardedRef) forwardedRef.current = node\n }\n\n // trap Tab within the dialog so focus can't strand on the locked page behind it (Escape + backdrop\n // dismissal live in `<Overlay>`; this fires after the overlay's own handler)\n const handleTabTrap = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key !== 'Tab') return\n const panel = panelRef.current\n if (!panel) return\n const focusables = [...panel.querySelectorAll<HTMLElement>(FOCUSABLE)].filter(\n (el) => el.offsetParent !== null || el === document.activeElement,\n )\n if (focusables.length === 0) {\n event.preventDefault()\n panel.focus()\n return\n }\n const first = focusables[0]\n const last = focusables[focusables.length - 1]\n const active = document.activeElement\n if (event.shiftKey && (active === first || active === panel)) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n if (!open) return null\n\n // a side drawer (left/right) is always full-height with the body scrolling inside (header/footer pinned)\n const isDrawer = placement !== 'center'\n\n const renderedIcon =\n icon == null ? null : typeof icon === 'string' && ICON_NAME_SET.has(icon) ? (\n <Icon name={icon as IconName} />\n ) : (\n icon\n )\n\n const hasHeader = title != null || description != null || renderedIcon != null || showCloseButton\n // a divider sets the header apart from the body/footer, like Card\n const headerDivided = children != null || footer != null\n\n return (\n <Overlay\n open={open}\n onClose={onClose}\n closeOnBackdrop={closeOnBackdrop}\n closeOnEscape={closeOnEscape}\n onKeyDown={handleTabTrap}\n className={styles.overlay}\n data-size={size}\n data-placement={placement}\n data-scroll={isDrawer ? 'inside' : scrollBehavior}\n >\n <div\n ref={setPanelRef}\n className={clsx(styles.modal, styles[size], className)}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title != null ? titleId : undefined}\n aria-label={title == null ? ariaLabel : undefined}\n aria-describedby={description != null ? descId : undefined}\n tabIndex={-1}\n style={style as CSSProperties}\n {...props}\n >\n {hasHeader && (\n <div className={clsx(styles.header, headerDivided && styles.headerDivided)}>\n <div className={styles.headerMain}>\n {renderedIcon != null && (\n <IconButton\n variant=\"filled\"\n size=\"sm\"\n color={color}\n nonClickable\n aria-hidden\n className={styles.icon}\n >\n {renderedIcon}\n </IconButton>\n )}\n {(title != null || description != null) && (\n <div className={styles.headerText}>\n {title != null && (\n <div id={titleId} className={styles.title}>\n {title}\n </div>\n )}\n {description != null && (\n <div id={descId} className={styles.subtitle}>\n {description}\n </div>\n )}\n </div>\n )}\n </div>\n {showCloseButton && (\n <IconButton\n variant=\"filled\"\n color=\"primary\"\n size=\"sm\"\n className={styles.close}\n aria-label={t('common.close')}\n onClick={onClose}\n >\n <Icon name=\"Close\" />\n </IconButton>\n )}\n </div>\n )}\n {children != null && (\n <div ref={bodyRef} className={clsx(styles.body, bodyClassName)}>\n {children}\n </div>\n )}\n {footer != null && (\n <div ref={footerRef} className={styles.footer}>\n {footer}\n </div>\n )}\n </div>\n </Overlay>\n )\n})\n"],"names":["Overlay","forwardRef","open","onClose","closeOnBackdrop","closeOnEscape","lockScroll","dim","className","style","children","onKeyDown","onMouseDown","onClick","props","ref","mouseDownTarget","useRef","visible","setVisible","useState","useLockBodyScroll","useEffect","raf","handleKeyDown","event","handleMouseDown","handleClick","createPortal","jsx","clsx","styles","ICON_NAME_SET","ICON_NAMES","FOCUSABLE","FIELD","focusFirst","candidates","list","candidate","el","other","Modal","size","scrollBehavior","placement","title","description","icon","color","footer","bodyClassName","showCloseButton","ariaLabel","forwardedRef","t","useT","panelRef","bodyRef","footerRef","titleId","useId","descId","openerRef","wasOpenRef","panel","opener","_a","_b","setPanelRef","node","handleTabTrap","focusables","first","last","active","isDrawer","renderedIcon","Icon","hasHeader","headerDivided","jsxs","IconButton"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as s } from "react/jsx-runtime";
2
2
  import { forwardRef as j, useState as w, useRef as u, useEffect as l } from "react";
3
3
  import { B as z } from "./Button-Bst_NtPJ.js";
4
- import { E as C } from "./ErrorState-DdxOeZy2.js";
4
+ import { E as C } from "./ErrorState-DI28ZCJN.js";
5
5
  import { f as p, e as D } from "./errorMessage-C83fB2h5.js";
6
6
  import { u as H } from "./useHomeTo-Ckq7scIY.js";
7
7
  import { a as _ } from "./ConfigProvider-DeMASTVo.js";
@@ -67,4 +67,4 @@ const O = j(function({
67
67
  export {
68
68
  O as P
69
69
  };
70
- //# sourceMappingURL=PageError-9tfvBssn.js.map
70
+ //# sourceMappingURL=PageError-BORFaxGW.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageError-9tfvBssn.js","sources":["../src/components/PageError/PageError.tsx"],"sourcesContent":["import { forwardRef, useEffect, useRef, useState, type HTMLAttributes, type ReactNode } from 'react'\nimport { type LinkProps } from '@tanstack/react-router'\nimport { useT } from '../../theme'\nimport type { IconName } from '../../icons/names'\nimport { Button } from '../Button'\nimport type { EmptyStateSize } from '../EmptyState'\nimport { ErrorState } from '../ErrorState/ErrorState'\nimport { isDev, isServerError } from '../ErrorState/errorMessage'\nimport { useHomeTo } from '../PageNotFound/useHomeTo'\n\nexport interface PageErrorProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'color'> {\n /** What the route threw (TanStack Router's `ErrorComponentProps.error`). */\n error: unknown\n /** Resets the route's error boundary (TanStack Router's `ErrorComponentProps.reset`). */\n reset?: () => void\n /** The React component stack (TanStack Router's `ErrorComponentProps.info`) — unused, accepted for typing. */\n info?: { componentStack: string }\n /**\n * Runs **before** the reset on \"Try Again\" (may be async) — where an app drops cached data, e.g.\n * `() => queryClient.resetQueries({ predicate: (q) => q.queryKey[0] !== 'session' })`. Without it, `reset()`\n * re-renders against the same cached response and throws on the same data again.\n */\n onRetry?: () => void | Promise<void>\n /**\n * Show the error's own message. Default: when it looks **server-originated** (a `traceId` or a numeric\n * `status` — written for people) or in development; otherwise the localized generic sentence (a render\n * crash's message is a developer's). `true` / `false` force it; `'dev'` = development only; a function decides\n * per error.\n */\n showMessage?: boolean | 'dev' | ((error: unknown) => boolean)\n /** A secondary \"Go to Home\" button to the first allowed menu page (RBAC-aware). Defaults to `true`. */\n showHome?: boolean\n /** Heading. Defaults to the localized \"This Page Could Not Be Shown\". */\n title?: ReactNode\n /** Overrides the description (the message / the generic sentence). */\n description?: ReactNode\n /** Leading icon — passed to `ErrorState` (`Danger` / `CloudCross` by default; `false` hides it). */\n icon?: IconName | ReactNode | false\n /** Size — passed to `ErrorState`. Defaults to `md`. */\n size?: EmptyStateSize\n /** Faded-grid hero backdrop — passed to `ErrorState`. Defaults to `true`. */\n pattern?: boolean\n /** The developer details panel (stack) — passed to `ErrorState`. Defaults to `'dev'`. */\n showDetails?: boolean | 'dev'\n}\n\n/**\n * The shared **route error page** — the twin of `PageNotFound`. Drop it into TanStack Router as the\n * `defaultErrorComponent`: TanStack wraps every route in its own error boundary (inside the shell's `<Outlet/>`),\n * so without it a crashing page shows the router's bare \"Something went wrong!\". Renders the full `ErrorState`\n * hero inside the shell with **Try Again** (`await onRetry?.()` → `reset()` → `router.invalidate()`, which re-runs\n * a throwing loader / `beforeLoad`), a **Go to Home** button, the server's `traceId`, and the error's message only\n * when it was written for people (`showMessage`). Every error is `console.error`ed once — in production the\n * screen hides the message, so the console is the record.\n *\n * @example\n * createRouter({ routeTree, defaultNotFoundComponent: PageNotFound, defaultErrorComponent: PageError })\n */\nexport const PageError = forwardRef<HTMLDivElement, PageErrorProps>(function PageError(\n {\n error,\n reset,\n info: _info,\n onRetry,\n showMessage,\n showHome = true,\n title,\n description,\n icon,\n size,\n pattern,\n showDetails,\n ...props\n },\n ref,\n) {\n const t = useT()\n const { router, homeTo } = useHomeTo()\n const [retrying, setRetrying] = useState(false)\n const mounted = useRef(true)\n useEffect(() => {\n mounted.current = true\n return () => {\n mounted.current = false\n }\n }, [])\n\n // log each distinct error once (StrictMode re-runs effects; a re-render with the same error isn't new)\n const logged = useRef<unknown>(undefined)\n useEffect(() => {\n if (logged.current === error) return\n logged.current = error\n console.error('[PageError]', error)\n }, [error])\n\n const messageShown =\n typeof showMessage === 'function'\n ? showMessage(error)\n : showMessage === 'dev'\n ? isDev()\n : (showMessage ?? (isServerError(error) || isDev()))\n\n const retry = async () => {\n setRetrying(true)\n try {\n await onRetry?.()\n reset?.()\n await router?.invalidate()\n } finally {\n if (mounted.current) setRetrying(false)\n }\n }\n\n const home =\n showHome && router && homeTo != null ? (\n <Button\n size=\"sm\"\n variant=\"text\"\n color=\"primary\"\n onClick={() => router.navigate({ to: homeTo as LinkProps['to'] })}\n >\n {t('pageError.home')}\n </Button>\n ) : undefined\n\n return (\n <ErrorState\n ref={ref}\n variant=\"block\"\n error={error}\n title={title ?? t('pageError.title')}\n // hidden message → the generic sentence (the trace id still shows: it's for support, not a leak)\n description={description ?? (messageShown ? undefined : t('pageError.description'))}\n icon={icon}\n size={size}\n pattern={pattern}\n showDetails={showDetails}\n onRetry={reset || onRetry || router ? () => void retry() : undefined}\n retrying={retrying}\n action={home}\n {...props}\n />\n )\n})\n"],"names":["PageError","forwardRef","error","reset","_info","onRetry","showMessage","showHome","title","description","icon","size","pattern","showDetails","props","ref","t","useT","router","homeTo","useHomeTo","retrying","setRetrying","useState","mounted","useRef","useEffect","logged","messageShown","isDev","isServerError","retry","home","jsx","Button","ErrorState"],"mappings":";;;;;;;AA0DO,MAAMA,IAAYC,EAA2C,SAClE;AAAA,EACE,OAAAC;AAAA,EACA,OAAAC;AAAA,EACA,MAAMC;AAAA,EACN,SAAAC;AAAA,EACA,aAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,OAAAC;AAAA,EACA,aAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC;AAAA,EACA,SAAAC;AAAA,EACA,aAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAIC,EAAA,GACJ,EAAE,QAAAC,GAAQ,QAAAC,EAAA,IAAWC,EAAA,GACrB,CAACC,GAAUC,CAAW,IAAIC,EAAS,EAAK,GACxCC,IAAUC,EAAO,EAAI;AAC3B,EAAAC,EAAU,OACRF,EAAQ,UAAU,IACX,MAAM;AACX,IAAAA,EAAQ,UAAU;AAAA,EACpB,IACC,CAAA,CAAE;AAGL,QAAMG,IAASF,EAAgB,MAAS;AACxC,EAAAC,EAAU,MAAM;AACd,IAAIC,EAAO,YAAYzB,MACvByB,EAAO,UAAUzB,GACjB,QAAQ,MAAM,eAAeA,CAAK;AAAA,EACpC,GAAG,CAACA,CAAK,CAAC;AAEV,QAAM0B,IACJ,OAAOtB,KAAgB,aACnBA,EAAYJ,CAAK,IACjBI,MAAgB,QACduB,MACCvB,MAAgBwB,EAAc5B,CAAK,KAAK2B,MAE3CE,IAAQ,YAAY;AACxB,IAAAT,EAAY,EAAI;AAChB,QAAI;AACF,aAAMjB,KAAA,gBAAAA,MACNF,KAAA,QAAAA,KACA,OAAMe,KAAA,gBAAAA,EAAQ;AAAA,IAChB,UAAA;AACE,MAAIM,EAAQ,WAASF,EAAY,EAAK;AAAA,IACxC;AAAA,EACF,GAEMU,IACJzB,KAAYW,KAAUC,KAAU,OAC9B,gBAAAc;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAQ;AAAA,MACR,OAAM;AAAA,MACN,SAAS,MAAMhB,EAAO,SAAS,EAAE,IAAIC,GAA2B;AAAA,MAE/D,YAAE,gBAAgB;AAAA,IAAA;AAAA,EAAA,IAEnB;AAEN,SACE,gBAAAc;AAAA,IAACE;AAAA,IAAA;AAAA,MACC,KAAApB;AAAA,MACA,SAAQ;AAAA,MACR,OAAAb;AAAA,MACA,OAAOM,KAASQ,EAAE,iBAAiB;AAAA,MAEnC,aAAaP,MAAgBmB,IAAe,SAAYZ,EAAE,uBAAuB;AAAA,MACjF,MAAAN;AAAA,MACA,MAAAC;AAAA,MACA,SAAAC;AAAA,MACA,aAAAC;AAAA,MACA,SAASV,KAASE,KAAWa,IAAS,MAAM,KAAKa,MAAU;AAAA,MAC3D,UAAAV;AAAA,MACA,QAAQW;AAAA,MACP,GAAGlB;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;"}
1
+ {"version":3,"file":"PageError-BORFaxGW.js","sources":["../src/components/PageError/PageError.tsx"],"sourcesContent":["import { forwardRef, useEffect, useRef, useState, type HTMLAttributes, type ReactNode } from 'react'\nimport { type LinkProps } from '@tanstack/react-router'\nimport { useT } from '../../theme'\nimport type { IconName } from '../../icons/names'\nimport { Button } from '../Button'\nimport type { EmptyStateSize } from '../EmptyState'\nimport { ErrorState } from '../ErrorState/ErrorState'\nimport { isDev, isServerError } from '../ErrorState/errorMessage'\nimport { useHomeTo } from '../PageNotFound/useHomeTo'\n\nexport interface PageErrorProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'color'> {\n /** What the route threw (TanStack Router's `ErrorComponentProps.error`). */\n error: unknown\n /** Resets the route's error boundary (TanStack Router's `ErrorComponentProps.reset`). */\n reset?: () => void\n /** The React component stack (TanStack Router's `ErrorComponentProps.info`) — unused, accepted for typing. */\n info?: { componentStack: string }\n /**\n * Runs **before** the reset on \"Try Again\" (may be async) — where an app drops cached data, e.g.\n * `() => queryClient.resetQueries({ predicate: (q) => q.queryKey[0] !== 'session' })`. Without it, `reset()`\n * re-renders against the same cached response and throws on the same data again.\n */\n onRetry?: () => void | Promise<void>\n /**\n * Show the error's own message. Default: when it looks **server-originated** (a `traceId` or a numeric\n * `status` — written for people) or in development; otherwise the localized generic sentence (a render\n * crash's message is a developer's). `true` / `false` force it; `'dev'` = development only; a function decides\n * per error.\n */\n showMessage?: boolean | 'dev' | ((error: unknown) => boolean)\n /** A secondary \"Go to Home\" button to the first allowed menu page (RBAC-aware). Defaults to `true`. */\n showHome?: boolean\n /** Heading. Defaults to the localized \"This Page Could Not Be Shown\". */\n title?: ReactNode\n /** Overrides the description (the message / the generic sentence). */\n description?: ReactNode\n /** Leading icon — passed to `ErrorState` (`Danger` / `CloudCross` by default; `false` hides it). */\n icon?: IconName | ReactNode | false\n /** Size — passed to `ErrorState`. Defaults to `md`. */\n size?: EmptyStateSize\n /** Faded-grid hero backdrop — passed to `ErrorState`. Defaults to `true`. */\n pattern?: boolean\n /** The developer details panel (stack) — passed to `ErrorState`. Defaults to `'dev'`. */\n showDetails?: boolean | 'dev'\n}\n\n/**\n * The shared **route error page** — the twin of `PageNotFound`. Drop it into TanStack Router as the\n * `defaultErrorComponent`: TanStack wraps every route in its own error boundary (inside the shell's `<Outlet/>`),\n * so without it a crashing page shows the router's bare \"Something went wrong!\". Renders the full `ErrorState`\n * hero inside the shell with **Try Again** (`await onRetry?.()` → `reset()` → `router.invalidate()`, which re-runs\n * a throwing loader / `beforeLoad`), a **Go to Home** button, the server's `traceId`, and the error's message only\n * when it was written for people (`showMessage`). Every error is `console.error`ed once — in production the\n * screen hides the message, so the console is the record.\n *\n * @example\n * createRouter({ routeTree, defaultNotFoundComponent: PageNotFound, defaultErrorComponent: PageError })\n */\nexport const PageError = forwardRef<HTMLDivElement, PageErrorProps>(function PageError(\n {\n error,\n reset,\n info: _info,\n onRetry,\n showMessage,\n showHome = true,\n title,\n description,\n icon,\n size,\n pattern,\n showDetails,\n ...props\n },\n ref,\n) {\n const t = useT()\n const { router, homeTo } = useHomeTo()\n const [retrying, setRetrying] = useState(false)\n const mounted = useRef(true)\n useEffect(() => {\n mounted.current = true\n return () => {\n mounted.current = false\n }\n }, [])\n\n // log each distinct error once (StrictMode re-runs effects; a re-render with the same error isn't new)\n const logged = useRef<unknown>(undefined)\n useEffect(() => {\n if (logged.current === error) return\n logged.current = error\n console.error('[PageError]', error)\n }, [error])\n\n const messageShown =\n typeof showMessage === 'function'\n ? showMessage(error)\n : showMessage === 'dev'\n ? isDev()\n : (showMessage ?? (isServerError(error) || isDev()))\n\n const retry = async () => {\n setRetrying(true)\n try {\n await onRetry?.()\n reset?.()\n await router?.invalidate()\n } finally {\n if (mounted.current) setRetrying(false)\n }\n }\n\n const home =\n showHome && router && homeTo != null ? (\n <Button\n size=\"sm\"\n variant=\"text\"\n color=\"primary\"\n onClick={() => router.navigate({ to: homeTo as LinkProps['to'] })}\n >\n {t('pageError.home')}\n </Button>\n ) : undefined\n\n return (\n <ErrorState\n ref={ref}\n variant=\"block\"\n error={error}\n title={title ?? t('pageError.title')}\n // hidden message → the generic sentence (the trace id still shows: it's for support, not a leak)\n description={description ?? (messageShown ? undefined : t('pageError.description'))}\n icon={icon}\n size={size}\n pattern={pattern}\n showDetails={showDetails}\n onRetry={reset || onRetry || router ? () => void retry() : undefined}\n retrying={retrying}\n action={home}\n {...props}\n />\n )\n})\n"],"names":["PageError","forwardRef","error","reset","_info","onRetry","showMessage","showHome","title","description","icon","size","pattern","showDetails","props","ref","t","useT","router","homeTo","useHomeTo","retrying","setRetrying","useState","mounted","useRef","useEffect","logged","messageShown","isDev","isServerError","retry","home","jsx","Button","ErrorState"],"mappings":";;;;;;;AA0DO,MAAMA,IAAYC,EAA2C,SAClE;AAAA,EACE,OAAAC;AAAA,EACA,OAAAC;AAAA,EACA,MAAMC;AAAA,EACN,SAAAC;AAAA,EACA,aAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,OAAAC;AAAA,EACA,aAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC;AAAA,EACA,SAAAC;AAAA,EACA,aAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAIC,EAAA,GACJ,EAAE,QAAAC,GAAQ,QAAAC,EAAA,IAAWC,EAAA,GACrB,CAACC,GAAUC,CAAW,IAAIC,EAAS,EAAK,GACxCC,IAAUC,EAAO,EAAI;AAC3B,EAAAC,EAAU,OACRF,EAAQ,UAAU,IACX,MAAM;AACX,IAAAA,EAAQ,UAAU;AAAA,EACpB,IACC,CAAA,CAAE;AAGL,QAAMG,IAASF,EAAgB,MAAS;AACxC,EAAAC,EAAU,MAAM;AACd,IAAIC,EAAO,YAAYzB,MACvByB,EAAO,UAAUzB,GACjB,QAAQ,MAAM,eAAeA,CAAK;AAAA,EACpC,GAAG,CAACA,CAAK,CAAC;AAEV,QAAM0B,IACJ,OAAOtB,KAAgB,aACnBA,EAAYJ,CAAK,IACjBI,MAAgB,QACduB,MACCvB,MAAgBwB,EAAc5B,CAAK,KAAK2B,MAE3CE,IAAQ,YAAY;AACxB,IAAAT,EAAY,EAAI;AAChB,QAAI;AACF,aAAMjB,KAAA,gBAAAA,MACNF,KAAA,QAAAA,KACA,OAAMe,KAAA,gBAAAA,EAAQ;AAAA,IAChB,UAAA;AACE,MAAIM,EAAQ,WAASF,EAAY,EAAK;AAAA,IACxC;AAAA,EACF,GAEMU,IACJzB,KAAYW,KAAUC,KAAU,OAC9B,gBAAAc;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAQ;AAAA,MACR,OAAM;AAAA,MACN,SAAS,MAAMhB,EAAO,SAAS,EAAE,IAAIC,GAA2B;AAAA,MAE/D,YAAE,gBAAgB;AAAA,IAAA;AAAA,EAAA,IAEnB;AAEN,SACE,gBAAAc;AAAA,IAACE;AAAA,IAAA;AAAA,MACC,KAAApB;AAAA,MACA,SAAQ;AAAA,MACR,OAAAb;AAAA,MACA,OAAOM,KAASQ,EAAE,iBAAiB;AAAA,MAEnC,aAAaP,MAAgBmB,IAAe,SAAYZ,EAAE,uBAAuB;AAAA,MACjF,MAAAN;AAAA,MACA,MAAAC;AAAA,MACA,SAAAC;AAAA,MACA,aAAAC;AAAA,MACA,SAASV,KAASE,KAAWa,IAAS,MAAM,KAAKa,MAAU;AAAA,MAC3D,UAAAV;AAAA,MACA,QAAQW;AAAA,MACP,GAAGlB;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;"}
@@ -1,2 +1,2 @@
1
- "use strict";const d=require("react/jsx-runtime"),t=require("react"),k=require("./Button-CYZoF18-.cjs"),B=require("./ErrorState-To_MAOf4.cjs"),s=require("./errorMessage-C7N4KvUS.cjs"),C=require("./useHomeTo-MfvJxziQ.cjs"),D=require("./ConfigProvider-B_Bb6pk1.cjs"),H=t.forwardRef(function({error:r,reset:o,info:w,onRetry:n,showMessage:i,showHome:v=!0,title:g,description:E,icon:m,size:p,pattern:q,showDetails:P,...S},x){const c=D.useT(),{router:e,homeTo:a}=C.useHomeTo(),[y,f]=t.useState(!1),u=t.useRef(!0);t.useEffect(()=>(u.current=!0,()=>{u.current=!1}),[]);const l=t.useRef(void 0);t.useEffect(()=>{l.current!==r&&(l.current=r,console.error("[PageError]",r))},[r]);const T=typeof i=="function"?i(r):i==="dev"?s.isDev():i??(s.isServerError(r)||s.isDev()),j=async()=>{f(!0);try{await(n==null?void 0:n()),o==null||o(),await(e==null?void 0:e.invalidate())}finally{u.current&&f(!1)}},h=v&&e&&a!=null?d.jsx(k.Button,{size:"sm",variant:"text",color:"primary",onClick:()=>e.navigate({to:a}),children:c("pageError.home")}):void 0;return d.jsx(B.ErrorState,{ref:x,variant:"block",error:r,title:g??c("pageError.title"),description:E??(T?void 0:c("pageError.description")),icon:m,size:p,pattern:q,showDetails:P,onRetry:o||n||e?()=>void j():void 0,retrying:y,action:h,...S})});exports.PageError=H;
2
- //# sourceMappingURL=PageError-CJHOUfCa.cjs.map
1
+ "use strict";const d=require("react/jsx-runtime"),t=require("react"),k=require("./Button-CYZoF18-.cjs"),B=require("./ErrorState-MQBeyF2n.cjs"),s=require("./errorMessage-C7N4KvUS.cjs"),C=require("./useHomeTo-MfvJxziQ.cjs"),D=require("./ConfigProvider-B_Bb6pk1.cjs"),H=t.forwardRef(function({error:r,reset:o,info:w,onRetry:n,showMessage:i,showHome:v=!0,title:g,description:E,icon:m,size:p,pattern:q,showDetails:P,...S},x){const c=D.useT(),{router:e,homeTo:a}=C.useHomeTo(),[y,f]=t.useState(!1),u=t.useRef(!0);t.useEffect(()=>(u.current=!0,()=>{u.current=!1}),[]);const l=t.useRef(void 0);t.useEffect(()=>{l.current!==r&&(l.current=r,console.error("[PageError]",r))},[r]);const T=typeof i=="function"?i(r):i==="dev"?s.isDev():i??(s.isServerError(r)||s.isDev()),j=async()=>{f(!0);try{await(n==null?void 0:n()),o==null||o(),await(e==null?void 0:e.invalidate())}finally{u.current&&f(!1)}},h=v&&e&&a!=null?d.jsx(k.Button,{size:"sm",variant:"text",color:"primary",onClick:()=>e.navigate({to:a}),children:c("pageError.home")}):void 0;return d.jsx(B.ErrorState,{ref:x,variant:"block",error:r,title:g??c("pageError.title"),description:E??(T?void 0:c("pageError.description")),icon:m,size:p,pattern:q,showDetails:P,onRetry:o||n||e?()=>void j():void 0,retrying:y,action:h,...S})});exports.PageError=H;
2
+ //# sourceMappingURL=PageError-aUV0vUfB.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageError-CJHOUfCa.cjs","sources":["../src/components/PageError/PageError.tsx"],"sourcesContent":["import { forwardRef, useEffect, useRef, useState, type HTMLAttributes, type ReactNode } from 'react'\nimport { type LinkProps } from '@tanstack/react-router'\nimport { useT } from '../../theme'\nimport type { IconName } from '../../icons/names'\nimport { Button } from '../Button'\nimport type { EmptyStateSize } from '../EmptyState'\nimport { ErrorState } from '../ErrorState/ErrorState'\nimport { isDev, isServerError } from '../ErrorState/errorMessage'\nimport { useHomeTo } from '../PageNotFound/useHomeTo'\n\nexport interface PageErrorProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'color'> {\n /** What the route threw (TanStack Router's `ErrorComponentProps.error`). */\n error: unknown\n /** Resets the route's error boundary (TanStack Router's `ErrorComponentProps.reset`). */\n reset?: () => void\n /** The React component stack (TanStack Router's `ErrorComponentProps.info`) — unused, accepted for typing. */\n info?: { componentStack: string }\n /**\n * Runs **before** the reset on \"Try Again\" (may be async) — where an app drops cached data, e.g.\n * `() => queryClient.resetQueries({ predicate: (q) => q.queryKey[0] !== 'session' })`. Without it, `reset()`\n * re-renders against the same cached response and throws on the same data again.\n */\n onRetry?: () => void | Promise<void>\n /**\n * Show the error's own message. Default: when it looks **server-originated** (a `traceId` or a numeric\n * `status` — written for people) or in development; otherwise the localized generic sentence (a render\n * crash's message is a developer's). `true` / `false` force it; `'dev'` = development only; a function decides\n * per error.\n */\n showMessage?: boolean | 'dev' | ((error: unknown) => boolean)\n /** A secondary \"Go to Home\" button to the first allowed menu page (RBAC-aware). Defaults to `true`. */\n showHome?: boolean\n /** Heading. Defaults to the localized \"This Page Could Not Be Shown\". */\n title?: ReactNode\n /** Overrides the description (the message / the generic sentence). */\n description?: ReactNode\n /** Leading icon — passed to `ErrorState` (`Danger` / `CloudCross` by default; `false` hides it). */\n icon?: IconName | ReactNode | false\n /** Size — passed to `ErrorState`. Defaults to `md`. */\n size?: EmptyStateSize\n /** Faded-grid hero backdrop — passed to `ErrorState`. Defaults to `true`. */\n pattern?: boolean\n /** The developer details panel (stack) — passed to `ErrorState`. Defaults to `'dev'`. */\n showDetails?: boolean | 'dev'\n}\n\n/**\n * The shared **route error page** — the twin of `PageNotFound`. Drop it into TanStack Router as the\n * `defaultErrorComponent`: TanStack wraps every route in its own error boundary (inside the shell's `<Outlet/>`),\n * so without it a crashing page shows the router's bare \"Something went wrong!\". Renders the full `ErrorState`\n * hero inside the shell with **Try Again** (`await onRetry?.()` → `reset()` → `router.invalidate()`, which re-runs\n * a throwing loader / `beforeLoad`), a **Go to Home** button, the server's `traceId`, and the error's message only\n * when it was written for people (`showMessage`). Every error is `console.error`ed once — in production the\n * screen hides the message, so the console is the record.\n *\n * @example\n * createRouter({ routeTree, defaultNotFoundComponent: PageNotFound, defaultErrorComponent: PageError })\n */\nexport const PageError = forwardRef<HTMLDivElement, PageErrorProps>(function PageError(\n {\n error,\n reset,\n info: _info,\n onRetry,\n showMessage,\n showHome = true,\n title,\n description,\n icon,\n size,\n pattern,\n showDetails,\n ...props\n },\n ref,\n) {\n const t = useT()\n const { router, homeTo } = useHomeTo()\n const [retrying, setRetrying] = useState(false)\n const mounted = useRef(true)\n useEffect(() => {\n mounted.current = true\n return () => {\n mounted.current = false\n }\n }, [])\n\n // log each distinct error once (StrictMode re-runs effects; a re-render with the same error isn't new)\n const logged = useRef<unknown>(undefined)\n useEffect(() => {\n if (logged.current === error) return\n logged.current = error\n console.error('[PageError]', error)\n }, [error])\n\n const messageShown =\n typeof showMessage === 'function'\n ? showMessage(error)\n : showMessage === 'dev'\n ? isDev()\n : (showMessage ?? (isServerError(error) || isDev()))\n\n const retry = async () => {\n setRetrying(true)\n try {\n await onRetry?.()\n reset?.()\n await router?.invalidate()\n } finally {\n if (mounted.current) setRetrying(false)\n }\n }\n\n const home =\n showHome && router && homeTo != null ? (\n <Button\n size=\"sm\"\n variant=\"text\"\n color=\"primary\"\n onClick={() => router.navigate({ to: homeTo as LinkProps['to'] })}\n >\n {t('pageError.home')}\n </Button>\n ) : undefined\n\n return (\n <ErrorState\n ref={ref}\n variant=\"block\"\n error={error}\n title={title ?? t('pageError.title')}\n // hidden message → the generic sentence (the trace id still shows: it's for support, not a leak)\n description={description ?? (messageShown ? undefined : t('pageError.description'))}\n icon={icon}\n size={size}\n pattern={pattern}\n showDetails={showDetails}\n onRetry={reset || onRetry || router ? () => void retry() : undefined}\n retrying={retrying}\n action={home}\n {...props}\n />\n )\n})\n"],"names":["PageError","forwardRef","error","reset","_info","onRetry","showMessage","showHome","title","description","icon","size","pattern","showDetails","props","ref","t","useT","router","homeTo","useHomeTo","retrying","setRetrying","useState","mounted","useRef","useEffect","logged","messageShown","isDev","isServerError","retry","home","jsx","Button","ErrorState"],"mappings":"yQA0DaA,EAAYC,EAAAA,WAA2C,SAClE,CACE,MAAAC,EACA,MAAAC,EACA,KAAMC,EACN,QAAAC,EACA,YAAAC,EACA,SAAAC,EAAW,GACX,MAAAC,EACA,YAAAC,EACA,KAAAC,EACA,KAAAC,EACA,QAAAC,EACA,YAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,MAAMC,EAAIC,EAAAA,KAAA,EACJ,CAAE,OAAAC,EAAQ,OAAAC,CAAA,EAAWC,YAAA,EACrB,CAACC,EAAUC,CAAW,EAAIC,EAAAA,SAAS,EAAK,EACxCC,EAAUC,EAAAA,OAAO,EAAI,EAC3BC,EAAAA,UAAU,KACRF,EAAQ,QAAU,GACX,IAAM,CACXA,EAAQ,QAAU,EACpB,GACC,CAAA,CAAE,EAGL,MAAMG,EAASF,EAAAA,OAAgB,MAAS,EACxCC,EAAAA,UAAU,IAAM,CACVC,EAAO,UAAYzB,IACvByB,EAAO,QAAUzB,EACjB,QAAQ,MAAM,cAAeA,CAAK,EACpC,EAAG,CAACA,CAAK,CAAC,EAEV,MAAM0B,EACJ,OAAOtB,GAAgB,WACnBA,EAAYJ,CAAK,EACjBI,IAAgB,MACduB,EAAAA,QACCvB,IAAgBwB,EAAAA,cAAc5B,CAAK,GAAK2B,EAAAA,SAE3CE,EAAQ,SAAY,CACxBT,EAAY,EAAI,EAChB,GAAI,CACF,MAAMjB,GAAA,YAAAA,KACNF,GAAA,MAAAA,IACA,MAAMe,GAAA,YAAAA,EAAQ,aAChB,QAAA,CACMM,EAAQ,SAASF,EAAY,EAAK,CACxC,CACF,EAEMU,EACJzB,GAAYW,GAAUC,GAAU,KAC9Bc,EAAAA,IAACC,EAAAA,OAAA,CACC,KAAK,KACL,QAAQ,OACR,MAAM,UACN,QAAS,IAAMhB,EAAO,SAAS,CAAE,GAAIC,EAA2B,EAE/D,WAAE,gBAAgB,CAAA,CAAA,EAEnB,OAEN,OACEc,EAAAA,IAACE,EAAAA,WAAA,CACC,IAAApB,EACA,QAAQ,QACR,MAAAb,EACA,MAAOM,GAASQ,EAAE,iBAAiB,EAEnC,YAAaP,IAAgBmB,EAAe,OAAYZ,EAAE,uBAAuB,GACjF,KAAAN,EACA,KAAAC,EACA,QAAAC,EACA,YAAAC,EACA,QAASV,GAASE,GAAWa,EAAS,IAAM,KAAKa,IAAU,OAC3D,SAAAV,EACA,OAAQW,EACP,GAAGlB,CAAA,CAAA,CAGV,CAAC"}
1
+ {"version":3,"file":"PageError-aUV0vUfB.cjs","sources":["../src/components/PageError/PageError.tsx"],"sourcesContent":["import { forwardRef, useEffect, useRef, useState, type HTMLAttributes, type ReactNode } from 'react'\nimport { type LinkProps } from '@tanstack/react-router'\nimport { useT } from '../../theme'\nimport type { IconName } from '../../icons/names'\nimport { Button } from '../Button'\nimport type { EmptyStateSize } from '../EmptyState'\nimport { ErrorState } from '../ErrorState/ErrorState'\nimport { isDev, isServerError } from '../ErrorState/errorMessage'\nimport { useHomeTo } from '../PageNotFound/useHomeTo'\n\nexport interface PageErrorProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'color'> {\n /** What the route threw (TanStack Router's `ErrorComponentProps.error`). */\n error: unknown\n /** Resets the route's error boundary (TanStack Router's `ErrorComponentProps.reset`). */\n reset?: () => void\n /** The React component stack (TanStack Router's `ErrorComponentProps.info`) — unused, accepted for typing. */\n info?: { componentStack: string }\n /**\n * Runs **before** the reset on \"Try Again\" (may be async) — where an app drops cached data, e.g.\n * `() => queryClient.resetQueries({ predicate: (q) => q.queryKey[0] !== 'session' })`. Without it, `reset()`\n * re-renders against the same cached response and throws on the same data again.\n */\n onRetry?: () => void | Promise<void>\n /**\n * Show the error's own message. Default: when it looks **server-originated** (a `traceId` or a numeric\n * `status` — written for people) or in development; otherwise the localized generic sentence (a render\n * crash's message is a developer's). `true` / `false` force it; `'dev'` = development only; a function decides\n * per error.\n */\n showMessage?: boolean | 'dev' | ((error: unknown) => boolean)\n /** A secondary \"Go to Home\" button to the first allowed menu page (RBAC-aware). Defaults to `true`. */\n showHome?: boolean\n /** Heading. Defaults to the localized \"This Page Could Not Be Shown\". */\n title?: ReactNode\n /** Overrides the description (the message / the generic sentence). */\n description?: ReactNode\n /** Leading icon — passed to `ErrorState` (`Danger` / `CloudCross` by default; `false` hides it). */\n icon?: IconName | ReactNode | false\n /** Size — passed to `ErrorState`. Defaults to `md`. */\n size?: EmptyStateSize\n /** Faded-grid hero backdrop — passed to `ErrorState`. Defaults to `true`. */\n pattern?: boolean\n /** The developer details panel (stack) — passed to `ErrorState`. Defaults to `'dev'`. */\n showDetails?: boolean | 'dev'\n}\n\n/**\n * The shared **route error page** — the twin of `PageNotFound`. Drop it into TanStack Router as the\n * `defaultErrorComponent`: TanStack wraps every route in its own error boundary (inside the shell's `<Outlet/>`),\n * so without it a crashing page shows the router's bare \"Something went wrong!\". Renders the full `ErrorState`\n * hero inside the shell with **Try Again** (`await onRetry?.()` → `reset()` → `router.invalidate()`, which re-runs\n * a throwing loader / `beforeLoad`), a **Go to Home** button, the server's `traceId`, and the error's message only\n * when it was written for people (`showMessage`). Every error is `console.error`ed once — in production the\n * screen hides the message, so the console is the record.\n *\n * @example\n * createRouter({ routeTree, defaultNotFoundComponent: PageNotFound, defaultErrorComponent: PageError })\n */\nexport const PageError = forwardRef<HTMLDivElement, PageErrorProps>(function PageError(\n {\n error,\n reset,\n info: _info,\n onRetry,\n showMessage,\n showHome = true,\n title,\n description,\n icon,\n size,\n pattern,\n showDetails,\n ...props\n },\n ref,\n) {\n const t = useT()\n const { router, homeTo } = useHomeTo()\n const [retrying, setRetrying] = useState(false)\n const mounted = useRef(true)\n useEffect(() => {\n mounted.current = true\n return () => {\n mounted.current = false\n }\n }, [])\n\n // log each distinct error once (StrictMode re-runs effects; a re-render with the same error isn't new)\n const logged = useRef<unknown>(undefined)\n useEffect(() => {\n if (logged.current === error) return\n logged.current = error\n console.error('[PageError]', error)\n }, [error])\n\n const messageShown =\n typeof showMessage === 'function'\n ? showMessage(error)\n : showMessage === 'dev'\n ? isDev()\n : (showMessage ?? (isServerError(error) || isDev()))\n\n const retry = async () => {\n setRetrying(true)\n try {\n await onRetry?.()\n reset?.()\n await router?.invalidate()\n } finally {\n if (mounted.current) setRetrying(false)\n }\n }\n\n const home =\n showHome && router && homeTo != null ? (\n <Button\n size=\"sm\"\n variant=\"text\"\n color=\"primary\"\n onClick={() => router.navigate({ to: homeTo as LinkProps['to'] })}\n >\n {t('pageError.home')}\n </Button>\n ) : undefined\n\n return (\n <ErrorState\n ref={ref}\n variant=\"block\"\n error={error}\n title={title ?? t('pageError.title')}\n // hidden message → the generic sentence (the trace id still shows: it's for support, not a leak)\n description={description ?? (messageShown ? undefined : t('pageError.description'))}\n icon={icon}\n size={size}\n pattern={pattern}\n showDetails={showDetails}\n onRetry={reset || onRetry || router ? () => void retry() : undefined}\n retrying={retrying}\n action={home}\n {...props}\n />\n )\n})\n"],"names":["PageError","forwardRef","error","reset","_info","onRetry","showMessage","showHome","title","description","icon","size","pattern","showDetails","props","ref","t","useT","router","homeTo","useHomeTo","retrying","setRetrying","useState","mounted","useRef","useEffect","logged","messageShown","isDev","isServerError","retry","home","jsx","Button","ErrorState"],"mappings":"yQA0DaA,EAAYC,EAAAA,WAA2C,SAClE,CACE,MAAAC,EACA,MAAAC,EACA,KAAMC,EACN,QAAAC,EACA,YAAAC,EACA,SAAAC,EAAW,GACX,MAAAC,EACA,YAAAC,EACA,KAAAC,EACA,KAAAC,EACA,QAAAC,EACA,YAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,MAAMC,EAAIC,EAAAA,KAAA,EACJ,CAAE,OAAAC,EAAQ,OAAAC,CAAA,EAAWC,YAAA,EACrB,CAACC,EAAUC,CAAW,EAAIC,EAAAA,SAAS,EAAK,EACxCC,EAAUC,EAAAA,OAAO,EAAI,EAC3BC,EAAAA,UAAU,KACRF,EAAQ,QAAU,GACX,IAAM,CACXA,EAAQ,QAAU,EACpB,GACC,CAAA,CAAE,EAGL,MAAMG,EAASF,EAAAA,OAAgB,MAAS,EACxCC,EAAAA,UAAU,IAAM,CACVC,EAAO,UAAYzB,IACvByB,EAAO,QAAUzB,EACjB,QAAQ,MAAM,cAAeA,CAAK,EACpC,EAAG,CAACA,CAAK,CAAC,EAEV,MAAM0B,EACJ,OAAOtB,GAAgB,WACnBA,EAAYJ,CAAK,EACjBI,IAAgB,MACduB,EAAAA,QACCvB,IAAgBwB,EAAAA,cAAc5B,CAAK,GAAK2B,EAAAA,SAE3CE,EAAQ,SAAY,CACxBT,EAAY,EAAI,EAChB,GAAI,CACF,MAAMjB,GAAA,YAAAA,KACNF,GAAA,MAAAA,IACA,MAAMe,GAAA,YAAAA,EAAQ,aAChB,QAAA,CACMM,EAAQ,SAASF,EAAY,EAAK,CACxC,CACF,EAEMU,EACJzB,GAAYW,GAAUC,GAAU,KAC9Bc,EAAAA,IAACC,EAAAA,OAAA,CACC,KAAK,KACL,QAAQ,OACR,MAAM,UACN,QAAS,IAAMhB,EAAO,SAAS,CAAE,GAAIC,EAA2B,EAE/D,WAAE,gBAAgB,CAAA,CAAA,EAEnB,OAEN,OACEc,EAAAA,IAACE,EAAAA,WAAA,CACC,IAAApB,EACA,QAAQ,QACR,MAAAb,EACA,MAAOM,GAASQ,EAAE,iBAAiB,EAEnC,YAAaP,IAAgBmB,EAAe,OAAYZ,EAAE,uBAAuB,GACjF,KAAAN,EACA,KAAAC,EACA,QAAAC,EACA,YAAAC,EACA,QAASV,GAASE,GAAWa,EAAS,IAAM,KAAKa,IAAU,OAC3D,SAAAV,EACA,OAAQW,EACP,GAAGlB,CAAA,CAAA,CAGV,CAAC"}
@@ -3,7 +3,7 @@ import { forwardRef as k, useState as z } from "react";
3
3
  import { clsx as B } from "clsx";
4
4
  import { u as R } from "./formContext-B7ln1Miy.js";
5
5
  import { C as h } from "./ColorPicker-w7NdprlW.js";
6
- import { C as v } from "./ColorRamp-CCfImssD.js";
6
+ import { C as v } from "./ColorRamp-Mp_ddhmb.js";
7
7
  const w = "_root_bbykz_3", I = {
8
8
  root: w
9
9
  }, H = k(function({
@@ -51,4 +51,4 @@ const w = "_root_bbykz_3", I = {
51
51
  export {
52
52
  H as P
53
53
  };
54
- //# sourceMappingURL=PaletteField-AMZpUMID.js.map
54
+ //# sourceMappingURL=PaletteField-ChG2VQtS.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PaletteField-AMZpUMID.js","sources":["../src/components/PaletteField/PaletteField.tsx"],"sourcesContent":["import { forwardRef, useState, type CSSProperties } from 'react'\nimport { clsx } from 'clsx'\nimport { useFormContext } from '../../form/formContext'\nimport type { GeneratePaletteOptions } from '../../helpers/palette'\nimport { ColorPicker, type ColorPickerProps } from '../ColorPicker'\nimport { ColorRamp, type ColorRampProps } from '../ColorRamp'\nimport styles from './PaletteField.module.css'\n\nexport interface PaletteFieldProps extends Omit<ColorPickerProps, 'swatches'> {\n /** Token prefix for the ramp (see `ColorRamp.name`). Defaults to `name`, then `'color'`. */\n rampName?: string\n /** `generatePalette` overrides, forwarded to the ramp. */\n paletteOptions?: GeneratePaletteOptions\n /** Ramp height (default `md`). */\n rampSize?: ColorRampProps['size']\n /** Hide the ramp (default `false`). */\n hideRamp?: boolean\n /** Brand colors are opaque: hide the alpha slider and emit `#rrggbb` only (default `true`). */\n opaque?: boolean\n /** Forwarded to the ramp's `onCopy`. */\n onCopyStep?: ColorRampProps['onCopy']\n className?: string\n style?: CSSProperties\n}\n\n/**\n * The composition every configurator page repeats: a **`ColorPicker`** with the **`ColorRamp`** of its\n * current value directly beneath, both bound to the same value. Works controlled (`value` +\n * `onChange`), uncontrolled (`defaultValue`), and inside `<Form>` by `name` — the ramp always reflects\n * the live field value (including `setValue` / `reset`), read through the same `useFormContext` binding\n * `ColorPicker` uses, so there is exactly one source of truth. `label` / `helperText` / `error` /\n * `required` go to the picker; the ramp shows no chrome of its own here (its bounds row stays).\n * `opaque` (default `true`) passes `alpha={false}` to the picker: no alpha slider, `#rrggbb` only.\n */\nexport const PaletteField = forwardRef<HTMLDivElement, PaletteFieldProps>(function PaletteField(\n {\n rampName,\n paletteOptions,\n rampSize = 'md',\n hideRamp = false,\n opaque = true,\n onCopyStep,\n value,\n defaultValue,\n onChange,\n name,\n className,\n style,\n ...picker\n },\n ref,\n) {\n // Mirror ColorPicker's own binding exactly: controlled prop → form value (by `name`) → internal state.\n const form = useFormContext()\n const isFormBound = Boolean(form && name)\n const [internal, setInternal] = useState<string>(defaultValue ?? '')\n const current =\n value !== undefined\n ? value\n : isFormBound\n ? ((form!.values[name!] as string | undefined) ?? '')\n : internal\n\n const handleChange = (color: string) => {\n // the picker already writes the form value when bound; we only track the uncontrolled case\n if (value === undefined && !isFormBound) setInternal(color)\n onChange?.(color)\n }\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style}>\n <ColorPicker\n {...picker}\n name={name}\n value={value}\n defaultValue={defaultValue}\n onChange={handleChange}\n alpha={!opaque}\n />\n {!hideRamp && (\n <ColorRamp\n baseColor={current}\n name={rampName ?? name ?? 'color'}\n options={paletteOptions}\n size={rampSize}\n onCopy={onCopyStep}\n />\n )}\n </div>\n )\n})\n"],"names":["PaletteField","forwardRef","rampName","paletteOptions","rampSize","hideRamp","opaque","onCopyStep","value","defaultValue","onChange","name","className","style","picker","ref","form","useFormContext","isFormBound","internal","setInternal","useState","current","handleChange","color","jsxs","clsx","styles","jsx","ColorPicker","ColorRamp"],"mappings":";;;;;;;;GAkCaA,IAAeC,EAA8C,SACxE;AAAA,EACE,UAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,UAAAC,IAAW;AAAA,EACX,QAAAC,IAAS;AAAA,EACT,YAAAC;AAAA,EACA,OAAAC;AAAA,EACA,cAAAC;AAAA,EACA,UAAAC;AAAA,EACA,MAAAC;AAAA,EACA,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AAEA,QAAMC,IAAOC,EAAA,GACPC,IAAc,GAAQF,KAAQL,IAC9B,CAACQ,GAAUC,CAAW,IAAIC,EAAiBZ,KAAgB,EAAE,GAC7Da,IACJd,MAAU,SACNA,IACAU,IACIF,EAAM,OAAOL,CAAK,KAA4B,KAChDQ,GAEFI,IAAe,CAACC,MAAkB;AAEtC,IAAIhB,MAAU,UAAa,CAACU,OAAyBM,CAAK,GAC1Dd,KAAA,QAAAA,EAAWc;AAAA,EACb;AAEA,SACE,gBAAAC,EAAC,SAAI,KAAAV,GAAU,WAAWW,EAAKC,EAAO,MAAMf,CAAS,GAAG,OAAAC,GACtD,UAAA;AAAA,IAAA,gBAAAe;AAAA,MAACC;AAAA,MAAA;AAAA,QACE,GAAGf;AAAA,QACJ,MAAAH;AAAA,QACA,OAAAH;AAAA,QACA,cAAAC;AAAA,QACA,UAAUc;AAAA,QACV,OAAO,CAACjB;AAAA,MAAA;AAAA,IAAA;AAAA,IAET,CAACD,KACA,gBAAAuB;AAAA,MAACE;AAAA,MAAA;AAAA,QACC,WAAWR;AAAA,QACX,MAAMpB,KAAYS,KAAQ;AAAA,QAC1B,SAASR;AAAA,QACT,MAAMC;AAAA,QACN,QAAQG;AAAA,MAAA;AAAA,IAAA;AAAA,EACV,GAEJ;AAEJ,CAAC;"}
1
+ {"version":3,"file":"PaletteField-ChG2VQtS.js","sources":["../src/components/PaletteField/PaletteField.tsx"],"sourcesContent":["import { forwardRef, useState, type CSSProperties } from 'react'\nimport { clsx } from 'clsx'\nimport { useFormContext } from '../../form/formContext'\nimport type { GeneratePaletteOptions } from '../../helpers/palette'\nimport { ColorPicker, type ColorPickerProps } from '../ColorPicker'\nimport { ColorRamp, type ColorRampProps } from '../ColorRamp'\nimport styles from './PaletteField.module.css'\n\nexport interface PaletteFieldProps extends Omit<ColorPickerProps, 'swatches'> {\n /** Token prefix for the ramp (see `ColorRamp.name`). Defaults to `name`, then `'color'`. */\n rampName?: string\n /** `generatePalette` overrides, forwarded to the ramp. */\n paletteOptions?: GeneratePaletteOptions\n /** Ramp height (default `md`). */\n rampSize?: ColorRampProps['size']\n /** Hide the ramp (default `false`). */\n hideRamp?: boolean\n /** Brand colors are opaque: hide the alpha slider and emit `#rrggbb` only (default `true`). */\n opaque?: boolean\n /** Forwarded to the ramp's `onCopy`. */\n onCopyStep?: ColorRampProps['onCopy']\n className?: string\n style?: CSSProperties\n}\n\n/**\n * The composition every configurator page repeats: a **`ColorPicker`** with the **`ColorRamp`** of its\n * current value directly beneath, both bound to the same value. Works controlled (`value` +\n * `onChange`), uncontrolled (`defaultValue`), and inside `<Form>` by `name` — the ramp always reflects\n * the live field value (including `setValue` / `reset`), read through the same `useFormContext` binding\n * `ColorPicker` uses, so there is exactly one source of truth. `label` / `helperText` / `error` /\n * `required` go to the picker; the ramp shows no chrome of its own here (its bounds row stays).\n * `opaque` (default `true`) passes `alpha={false}` to the picker: no alpha slider, `#rrggbb` only.\n */\nexport const PaletteField = forwardRef<HTMLDivElement, PaletteFieldProps>(function PaletteField(\n {\n rampName,\n paletteOptions,\n rampSize = 'md',\n hideRamp = false,\n opaque = true,\n onCopyStep,\n value,\n defaultValue,\n onChange,\n name,\n className,\n style,\n ...picker\n },\n ref,\n) {\n // Mirror ColorPicker's own binding exactly: controlled prop → form value (by `name`) → internal state.\n const form = useFormContext()\n const isFormBound = Boolean(form && name)\n const [internal, setInternal] = useState<string>(defaultValue ?? '')\n const current =\n value !== undefined\n ? value\n : isFormBound\n ? ((form!.values[name!] as string | undefined) ?? '')\n : internal\n\n const handleChange = (color: string) => {\n // the picker already writes the form value when bound; we only track the uncontrolled case\n if (value === undefined && !isFormBound) setInternal(color)\n onChange?.(color)\n }\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style}>\n <ColorPicker\n {...picker}\n name={name}\n value={value}\n defaultValue={defaultValue}\n onChange={handleChange}\n alpha={!opaque}\n />\n {!hideRamp && (\n <ColorRamp\n baseColor={current}\n name={rampName ?? name ?? 'color'}\n options={paletteOptions}\n size={rampSize}\n onCopy={onCopyStep}\n />\n )}\n </div>\n )\n})\n"],"names":["PaletteField","forwardRef","rampName","paletteOptions","rampSize","hideRamp","opaque","onCopyStep","value","defaultValue","onChange","name","className","style","picker","ref","form","useFormContext","isFormBound","internal","setInternal","useState","current","handleChange","color","jsxs","clsx","styles","jsx","ColorPicker","ColorRamp"],"mappings":";;;;;;;;GAkCaA,IAAeC,EAA8C,SACxE;AAAA,EACE,UAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,UAAAC,IAAW;AAAA,EACX,QAAAC,IAAS;AAAA,EACT,YAAAC;AAAA,EACA,OAAAC;AAAA,EACA,cAAAC;AAAA,EACA,UAAAC;AAAA,EACA,MAAAC;AAAA,EACA,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AAEA,QAAMC,IAAOC,EAAA,GACPC,IAAc,GAAQF,KAAQL,IAC9B,CAACQ,GAAUC,CAAW,IAAIC,EAAiBZ,KAAgB,EAAE,GAC7Da,IACJd,MAAU,SACNA,IACAU,IACIF,EAAM,OAAOL,CAAK,KAA4B,KAChDQ,GAEFI,IAAe,CAACC,MAAkB;AAEtC,IAAIhB,MAAU,UAAa,CAACU,OAAyBM,CAAK,GAC1Dd,KAAA,QAAAA,EAAWc;AAAA,EACb;AAEA,SACE,gBAAAC,EAAC,SAAI,KAAAV,GAAU,WAAWW,EAAKC,EAAO,MAAMf,CAAS,GAAG,OAAAC,GACtD,UAAA;AAAA,IAAA,gBAAAe;AAAA,MAACC;AAAA,MAAA;AAAA,QACE,GAAGf;AAAA,QACJ,MAAAH;AAAA,QACA,OAAAH;AAAA,QACA,cAAAC;AAAA,QACA,UAAUc;AAAA,QACV,OAAO,CAACjB;AAAA,MAAA;AAAA,IAAA;AAAA,IAET,CAACD,KACA,gBAAAuB;AAAA,MAACE;AAAA,MAAA;AAAA,QACC,WAAWR;AAAA,QACX,MAAMpB,KAAYS,KAAQ;AAAA,QAC1B,SAASR;AAAA,QACT,MAAMC;AAAA,QACN,QAAQG;AAAA,MAAA;AAAA,IAAA;AAAA,EACV,GAEJ;AAEJ,CAAC;"}
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("react/jsx-runtime"),l=require("react"),k=require("clsx"),y=require("./formContext-bTWrqdEV.cjs"),_=require("./ColorPicker-C1pDI4su.cjs"),z=require("./ColorRamp-B_-ZPqMa.cjs"),B="_root_bbykz_3",h={root:B},v=l.forwardRef(function({rampName:u,paletteOptions:a,rampSize:d="md",hideRamp:x=!1,opaque:m=!0,onCopyStep:f,value:o,defaultValue:s,onChange:r,name:t,className:q,style:C,...p},F){const c=y.useFormContext(),n=!!(c&&t),[P,j]=l.useState(s??""),R=o!==void 0?o:n?c.values[t]??"":P,b=i=>{o===void 0&&!n&&j(i),r==null||r(i)};return e.jsxs("div",{ref:F,className:k.clsx(h.root,q),style:C,children:[e.jsx(_.ColorPicker,{...p,name:t,value:o,defaultValue:s,onChange:b,alpha:!m}),!x&&e.jsx(z.ColorRamp,{baseColor:R,name:u??t??"color",options:a,size:d,onCopy:f})]})});exports.PaletteField=v;
2
- //# sourceMappingURL=PaletteField-ClCvXFdN.cjs.map
1
+ "use strict";const e=require("react/jsx-runtime"),l=require("react"),k=require("clsx"),y=require("./formContext-bTWrqdEV.cjs"),_=require("./ColorPicker-C1pDI4su.cjs"),z=require("./ColorRamp-D9_5JULy.cjs"),B="_root_bbykz_3",h={root:B},v=l.forwardRef(function({rampName:u,paletteOptions:a,rampSize:d="md",hideRamp:x=!1,opaque:m=!0,onCopyStep:f,value:o,defaultValue:s,onChange:r,name:t,className:q,style:C,...p},F){const c=y.useFormContext(),n=!!(c&&t),[P,j]=l.useState(s??""),R=o!==void 0?o:n?c.values[t]??"":P,b=i=>{o===void 0&&!n&&j(i),r==null||r(i)};return e.jsxs("div",{ref:F,className:k.clsx(h.root,q),style:C,children:[e.jsx(_.ColorPicker,{...p,name:t,value:o,defaultValue:s,onChange:b,alpha:!m}),!x&&e.jsx(z.ColorRamp,{baseColor:R,name:u??t??"color",options:a,size:d,onCopy:f})]})});exports.PaletteField=v;
2
+ //# sourceMappingURL=PaletteField-DQ1OvXqe.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"PaletteField-ClCvXFdN.cjs","sources":["../src/components/PaletteField/PaletteField.tsx"],"sourcesContent":["import { forwardRef, useState, type CSSProperties } from 'react'\nimport { clsx } from 'clsx'\nimport { useFormContext } from '../../form/formContext'\nimport type { GeneratePaletteOptions } from '../../helpers/palette'\nimport { ColorPicker, type ColorPickerProps } from '../ColorPicker'\nimport { ColorRamp, type ColorRampProps } from '../ColorRamp'\nimport styles from './PaletteField.module.css'\n\nexport interface PaletteFieldProps extends Omit<ColorPickerProps, 'swatches'> {\n /** Token prefix for the ramp (see `ColorRamp.name`). Defaults to `name`, then `'color'`. */\n rampName?: string\n /** `generatePalette` overrides, forwarded to the ramp. */\n paletteOptions?: GeneratePaletteOptions\n /** Ramp height (default `md`). */\n rampSize?: ColorRampProps['size']\n /** Hide the ramp (default `false`). */\n hideRamp?: boolean\n /** Brand colors are opaque: hide the alpha slider and emit `#rrggbb` only (default `true`). */\n opaque?: boolean\n /** Forwarded to the ramp's `onCopy`. */\n onCopyStep?: ColorRampProps['onCopy']\n className?: string\n style?: CSSProperties\n}\n\n/**\n * The composition every configurator page repeats: a **`ColorPicker`** with the **`ColorRamp`** of its\n * current value directly beneath, both bound to the same value. Works controlled (`value` +\n * `onChange`), uncontrolled (`defaultValue`), and inside `<Form>` by `name` — the ramp always reflects\n * the live field value (including `setValue` / `reset`), read through the same `useFormContext` binding\n * `ColorPicker` uses, so there is exactly one source of truth. `label` / `helperText` / `error` /\n * `required` go to the picker; the ramp shows no chrome of its own here (its bounds row stays).\n * `opaque` (default `true`) passes `alpha={false}` to the picker: no alpha slider, `#rrggbb` only.\n */\nexport const PaletteField = forwardRef<HTMLDivElement, PaletteFieldProps>(function PaletteField(\n {\n rampName,\n paletteOptions,\n rampSize = 'md',\n hideRamp = false,\n opaque = true,\n onCopyStep,\n value,\n defaultValue,\n onChange,\n name,\n className,\n style,\n ...picker\n },\n ref,\n) {\n // Mirror ColorPicker's own binding exactly: controlled prop → form value (by `name`) → internal state.\n const form = useFormContext()\n const isFormBound = Boolean(form && name)\n const [internal, setInternal] = useState<string>(defaultValue ?? '')\n const current =\n value !== undefined\n ? value\n : isFormBound\n ? ((form!.values[name!] as string | undefined) ?? '')\n : internal\n\n const handleChange = (color: string) => {\n // the picker already writes the form value when bound; we only track the uncontrolled case\n if (value === undefined && !isFormBound) setInternal(color)\n onChange?.(color)\n }\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style}>\n <ColorPicker\n {...picker}\n name={name}\n value={value}\n defaultValue={defaultValue}\n onChange={handleChange}\n alpha={!opaque}\n />\n {!hideRamp && (\n <ColorRamp\n baseColor={current}\n name={rampName ?? name ?? 'color'}\n options={paletteOptions}\n size={rampSize}\n onCopy={onCopyStep}\n />\n )}\n </div>\n )\n})\n"],"names":["PaletteField","forwardRef","rampName","paletteOptions","rampSize","hideRamp","opaque","onCopyStep","value","defaultValue","onChange","name","className","style","picker","ref","form","useFormContext","isFormBound","internal","setInternal","useState","current","handleChange","color","jsxs","clsx","styles","jsx","ColorPicker","ColorRamp"],"mappings":"0OAkCaA,EAAeC,EAAAA,WAA8C,SACxE,CACE,SAAAC,EACA,eAAAC,EACA,SAAAC,EAAW,KACX,SAAAC,EAAW,GACX,OAAAC,EAAS,GACT,WAAAC,EACA,MAAAC,EACA,aAAAC,EACA,SAAAC,EACA,KAAAC,EACA,UAAAC,EACA,MAAAC,EACA,GAAGC,CACL,EACAC,EACA,CAEA,MAAMC,EAAOC,EAAAA,eAAA,EACPC,EAAc,GAAQF,GAAQL,GAC9B,CAACQ,EAAUC,CAAW,EAAIC,EAAAA,SAAiBZ,GAAgB,EAAE,EAC7Da,EACJd,IAAU,OACNA,EACAU,EACIF,EAAM,OAAOL,CAAK,GAA4B,GAChDQ,EAEFI,EAAgBC,GAAkB,CAElChB,IAAU,QAAa,CAACU,KAAyBM,CAAK,EAC1Dd,GAAA,MAAAA,EAAWc,EACb,EAEA,OACEC,OAAC,OAAI,IAAAV,EAAU,UAAWW,EAAAA,KAAKC,EAAO,KAAMf,CAAS,EAAG,MAAAC,EACtD,SAAA,CAAAe,EAAAA,IAACC,EAAAA,YAAA,CACE,GAAGf,EACJ,KAAAH,EACA,MAAAH,EACA,aAAAC,EACA,SAAUc,EACV,MAAO,CAACjB,CAAA,CAAA,EAET,CAACD,GACAuB,EAAAA,IAACE,EAAAA,UAAA,CACC,UAAWR,EACX,KAAMpB,GAAYS,GAAQ,QAC1B,QAASR,EACT,KAAMC,EACN,OAAQG,CAAA,CAAA,CACV,EAEJ,CAEJ,CAAC"}
1
+ {"version":3,"file":"PaletteField-DQ1OvXqe.cjs","sources":["../src/components/PaletteField/PaletteField.tsx"],"sourcesContent":["import { forwardRef, useState, type CSSProperties } from 'react'\nimport { clsx } from 'clsx'\nimport { useFormContext } from '../../form/formContext'\nimport type { GeneratePaletteOptions } from '../../helpers/palette'\nimport { ColorPicker, type ColorPickerProps } from '../ColorPicker'\nimport { ColorRamp, type ColorRampProps } from '../ColorRamp'\nimport styles from './PaletteField.module.css'\n\nexport interface PaletteFieldProps extends Omit<ColorPickerProps, 'swatches'> {\n /** Token prefix for the ramp (see `ColorRamp.name`). Defaults to `name`, then `'color'`. */\n rampName?: string\n /** `generatePalette` overrides, forwarded to the ramp. */\n paletteOptions?: GeneratePaletteOptions\n /** Ramp height (default `md`). */\n rampSize?: ColorRampProps['size']\n /** Hide the ramp (default `false`). */\n hideRamp?: boolean\n /** Brand colors are opaque: hide the alpha slider and emit `#rrggbb` only (default `true`). */\n opaque?: boolean\n /** Forwarded to the ramp's `onCopy`. */\n onCopyStep?: ColorRampProps['onCopy']\n className?: string\n style?: CSSProperties\n}\n\n/**\n * The composition every configurator page repeats: a **`ColorPicker`** with the **`ColorRamp`** of its\n * current value directly beneath, both bound to the same value. Works controlled (`value` +\n * `onChange`), uncontrolled (`defaultValue`), and inside `<Form>` by `name` — the ramp always reflects\n * the live field value (including `setValue` / `reset`), read through the same `useFormContext` binding\n * `ColorPicker` uses, so there is exactly one source of truth. `label` / `helperText` / `error` /\n * `required` go to the picker; the ramp shows no chrome of its own here (its bounds row stays).\n * `opaque` (default `true`) passes `alpha={false}` to the picker: no alpha slider, `#rrggbb` only.\n */\nexport const PaletteField = forwardRef<HTMLDivElement, PaletteFieldProps>(function PaletteField(\n {\n rampName,\n paletteOptions,\n rampSize = 'md',\n hideRamp = false,\n opaque = true,\n onCopyStep,\n value,\n defaultValue,\n onChange,\n name,\n className,\n style,\n ...picker\n },\n ref,\n) {\n // Mirror ColorPicker's own binding exactly: controlled prop → form value (by `name`) → internal state.\n const form = useFormContext()\n const isFormBound = Boolean(form && name)\n const [internal, setInternal] = useState<string>(defaultValue ?? '')\n const current =\n value !== undefined\n ? value\n : isFormBound\n ? ((form!.values[name!] as string | undefined) ?? '')\n : internal\n\n const handleChange = (color: string) => {\n // the picker already writes the form value when bound; we only track the uncontrolled case\n if (value === undefined && !isFormBound) setInternal(color)\n onChange?.(color)\n }\n\n return (\n <div ref={ref} className={clsx(styles.root, className)} style={style}>\n <ColorPicker\n {...picker}\n name={name}\n value={value}\n defaultValue={defaultValue}\n onChange={handleChange}\n alpha={!opaque}\n />\n {!hideRamp && (\n <ColorRamp\n baseColor={current}\n name={rampName ?? name ?? 'color'}\n options={paletteOptions}\n size={rampSize}\n onCopy={onCopyStep}\n />\n )}\n </div>\n )\n})\n"],"names":["PaletteField","forwardRef","rampName","paletteOptions","rampSize","hideRamp","opaque","onCopyStep","value","defaultValue","onChange","name","className","style","picker","ref","form","useFormContext","isFormBound","internal","setInternal","useState","current","handleChange","color","jsxs","clsx","styles","jsx","ColorPicker","ColorRamp"],"mappings":"0OAkCaA,EAAeC,EAAAA,WAA8C,SACxE,CACE,SAAAC,EACA,eAAAC,EACA,SAAAC,EAAW,KACX,SAAAC,EAAW,GACX,OAAAC,EAAS,GACT,WAAAC,EACA,MAAAC,EACA,aAAAC,EACA,SAAAC,EACA,KAAAC,EACA,UAAAC,EACA,MAAAC,EACA,GAAGC,CACL,EACAC,EACA,CAEA,MAAMC,EAAOC,EAAAA,eAAA,EACPC,EAAc,GAAQF,GAAQL,GAC9B,CAACQ,EAAUC,CAAW,EAAIC,EAAAA,SAAiBZ,GAAgB,EAAE,EAC7Da,EACJd,IAAU,OACNA,EACAU,EACIF,EAAM,OAAOL,CAAK,GAA4B,GAChDQ,EAEFI,EAAgBC,GAAkB,CAElChB,IAAU,QAAa,CAACU,KAAyBM,CAAK,EAC1Dd,GAAA,MAAAA,EAAWc,EACb,EAEA,OACEC,OAAC,OAAI,IAAAV,EAAU,UAAWW,EAAAA,KAAKC,EAAO,KAAMf,CAAS,EAAG,MAAAC,EACtD,SAAA,CAAAe,EAAAA,IAACC,EAAAA,YAAA,CACE,GAAGf,EACJ,KAAAH,EACA,MAAAH,EACA,aAAAC,EACA,SAAUc,EACV,MAAO,CAACjB,CAAA,CAAA,EAET,CAACD,GACAuB,EAAAA,IAACE,EAAAA,UAAA,CACC,UAAWR,EACX,KAAMpB,GAAYS,GAAQ,QAC1B,QAASR,EACT,KAAMC,EACN,OAAQG,CAAA,CAAA,CACV,EAEJ,CAEJ,CAAC"}
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("react/jsx-runtime"),r=require("react"),z=require("./Blockquote-BWGxIL_V.cjs"),C=require("./Button-CYZoF18-.cjs"),N=require("./Code-CUdpWlY5.cjs"),j=require("./Icon-B6lz-V0A.cjs"),I=require("./Modal-D8V-JZBg.cjs"),R=require("./TextField-BL6OAJcP.cjs"),w=require("./Typography-d97DdbNd.cjs"),v=require("./ConfigProvider-B_Bb6pk1.cjs"),E="_modalBody_1nqut_5",F="_body_1nqut_4",S="_iconBox_1nqut_20",O="_confirm_1nqut_37",L="_phrase_1nqut_45",i={modalBody:E,body:F,iconBox:S,confirm:O,phrase:L},M=r.forwardRef(function({open:s,onClose:t,onConfirm:l,title:d,message:b,confirmLabel:B,cancelLabel:_,loading:T=!1,confirmText:n},k){const c=v.useT(),[D,h]=r.useState(!1),o=T||D,[u,y]=r.useState(""),x=r.useRef(s);x.current!==s&&(x.current=s,s&&y(""));const m=n==null||u.trim()===n.trim(),g=r.useId(),p=()=>{o||t()},q=async()=>{if(!(!m||o))try{h(!0),await l(),t()}catch{}finally{h(!1)}};return e.jsx(I.Modal,{ref:k,open:s,onClose:p,size:"sm",bodyClassName:i.modalBody,title:d??c("removeDialog.title"),showCloseButton:!o,closeOnBackdrop:!o,closeOnEscape:!o,footer:e.jsxs(e.Fragment,{children:[e.jsx(C.Button,{variant:"text",size:"sm",onClick:p,disabled:o,children:_??c("common.cancel")}),e.jsx(C.Button,{color:"error",variant:"filled",size:"sm",onClick:q,loading:o,disabled:!m,children:B??c("common.delete")})]}),children:e.jsxs("div",{className:i.body,children:[e.jsx("div",{className:i.iconBox,"aria-hidden":!0,children:e.jsx(j.Icon,{name:"Trash"})}),e.jsx(w.Typography,{align:"center",children:b??c("removeDialog.message")}),n!=null&&e.jsxs("div",{className:i.confirm,children:[e.jsx(z.Blockquote,{id:g,size:"sm",color:"error",children:e.jsx(P,{text:n})}),e.jsx(R.TextField,{"aria-labelledby":g,placeholder:n,value:u,onChange:a=>y(a.target.value),onKeyDown:a=>{a.key==="Enter"&&(a.preventDefault(),q())},adornment:u&&m?e.jsx(j.Icon,{name:"TickCircle",color:"success"}):void 0,adornmentPosition:"right",disabled:o,size:"sm",autoComplete:"off",autoCapitalize:"off",autoCorrect:"off",spellCheck:!1})]})]})})});function P({text:f}){const s=v.useT(),t="\0",[l,d=""]=s("removeDialog.typeToConfirm",{text:t}).split(t);return e.jsxs(e.Fragment,{children:[l,e.jsx(N.Code,{size:"sm",color:"error",className:i.phrase,children:f}),d]})}exports.RemoveDialog=M;
2
- //# sourceMappingURL=RemoveDialog-yEZm6JaC.cjs.map
1
+ "use strict";const e=require("react/jsx-runtime"),r=require("react"),z=require("./Blockquote-BWGxIL_V.cjs"),C=require("./Button-CYZoF18-.cjs"),N=require("./Code-CUdpWlY5.cjs"),j=require("./Icon-B6lz-V0A.cjs"),I=require("./Modal-DjSOhZqd.cjs"),R=require("./TextField-BL6OAJcP.cjs"),w=require("./Typography-d97DdbNd.cjs"),v=require("./ConfigProvider-B_Bb6pk1.cjs"),E="_modalBody_1nqut_5",F="_body_1nqut_4",S="_iconBox_1nqut_20",O="_confirm_1nqut_37",L="_phrase_1nqut_45",i={modalBody:E,body:F,iconBox:S,confirm:O,phrase:L},M=r.forwardRef(function({open:s,onClose:t,onConfirm:l,title:d,message:b,confirmLabel:B,cancelLabel:_,loading:T=!1,confirmText:n},k){const c=v.useT(),[D,h]=r.useState(!1),o=T||D,[u,y]=r.useState(""),x=r.useRef(s);x.current!==s&&(x.current=s,s&&y(""));const m=n==null||u.trim()===n.trim(),g=r.useId(),p=()=>{o||t()},q=async()=>{if(!(!m||o))try{h(!0),await l(),t()}catch{}finally{h(!1)}};return e.jsx(I.Modal,{ref:k,open:s,onClose:p,size:"sm",bodyClassName:i.modalBody,title:d??c("removeDialog.title"),showCloseButton:!o,closeOnBackdrop:!o,closeOnEscape:!o,footer:e.jsxs(e.Fragment,{children:[e.jsx(C.Button,{variant:"text",size:"sm",onClick:p,disabled:o,children:_??c("common.cancel")}),e.jsx(C.Button,{color:"error",variant:"filled",size:"sm",onClick:q,loading:o,disabled:!m,children:B??c("common.delete")})]}),children:e.jsxs("div",{className:i.body,children:[e.jsx("div",{className:i.iconBox,"aria-hidden":!0,children:e.jsx(j.Icon,{name:"Trash"})}),e.jsx(w.Typography,{align:"center",children:b??c("removeDialog.message")}),n!=null&&e.jsxs("div",{className:i.confirm,children:[e.jsx(z.Blockquote,{id:g,size:"sm",color:"error",children:e.jsx(P,{text:n})}),e.jsx(R.TextField,{"aria-labelledby":g,placeholder:n,value:u,onChange:a=>y(a.target.value),onKeyDown:a=>{a.key==="Enter"&&(a.preventDefault(),q())},adornment:u&&m?e.jsx(j.Icon,{name:"TickCircle",color:"success"}):void 0,adornmentPosition:"right",disabled:o,size:"sm",autoComplete:"off",autoCapitalize:"off",autoCorrect:"off",spellCheck:!1})]})]})})});function P({text:f}){const s=v.useT(),t="\0",[l,d=""]=s("removeDialog.typeToConfirm",{text:t}).split(t);return e.jsxs(e.Fragment,{children:[l,e.jsx(N.Code,{size:"sm",color:"error",className:i.phrase,children:f}),d]})}exports.RemoveDialog=M;
2
+ //# sourceMappingURL=RemoveDialog-B_Y18MeJ.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RemoveDialog-yEZm6JaC.cjs","sources":["../src/components/RemoveDialog/RemoveDialog.tsx"],"sourcesContent":["import { forwardRef, useId, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport { useT } from '../../theme'\nimport { Blockquote } from '../Blockquote'\nimport { Button } from '../Button'\nimport { Code } from '../Code'\nimport { Icon } from '../Icon'\nimport { Modal } from '../Modal'\nimport { TextField } from '../TextField'\nimport { Typography } from '../Typography'\nimport styles from './RemoveDialog.module.css'\n\nexport interface RemoveDialogProps {\n /** Whether the dialog is open. */\n open: boolean\n /** Called on Cancel, the × button, backdrop, or Escape (ignored while a confirm is in flight). */\n onClose: () => void\n /**\n * The destructive action. May be async — while its promise is pending the Delete button shows a\n * loader and the dialog locks (no dismiss); on success the dialog **auto-closes**, on rejection it\n * stays open so the user can retry (surface the error yourself, e.g. a toast).\n */\n onConfirm: () => void | Promise<void>\n /** Header title. Defaults to `\"Remove\"`. */\n title?: ReactNode\n /** Body prompt shown under the icon. Defaults to `\"Are you sure you want to delete?\"`. */\n message?: ReactNode\n /** Confirm (destructive) button label. Defaults to `\"Delete\"`. */\n confirmLabel?: string\n /** Cancel button label. Defaults to `\"Cancel\"`. */\n cancelLabel?: string\n /** External loading override — combined with the internal async-confirm state. */\n loading?: boolean\n /**\n * **Type-to-confirm** for high-risk deletes: the user must type this exact text (e.g. `\"remove user\"`)\n * into a field before Delete enables. Case-sensitive; surrounding whitespace is ignored. The field is\n * focused on open, cleared on every open, and Enter confirms once it matches.\n */\n confirmText?: string\n}\n\n/**\n * A ready-made delete confirmation — a thin convenience wrapper over `Modal` for the ubiquitous\n * \"Are you sure you want to delete?\" prompt. Hardcodes the destructive flavor (a `Trash` glyph in a\n * soft error circle + an `error` Delete button); just wire `open` / `onClose` / `onConfirm`. An async\n * `onConfirm` drives the button loader and locks dismissal, then auto-closes on success. `confirmText`\n * adds **type-to-confirm**: Delete stays disabled until the user types that exact text. Built on\n * `Modal`, so it inherits the portal, scroll-lock, focus-trap, and backdrop/Escape dismissal.\n *\n * @example\n * const { isOpen, open, close } = useDisclosure()\n * <RemoveDialog open={isOpen} onClose={close} onConfirm={() => deleteItem(id)} />\n */\nexport const RemoveDialog = forwardRef<HTMLDivElement, RemoveDialogProps>(function RemoveDialog(\n {\n open,\n onClose,\n onConfirm,\n title,\n message,\n confirmLabel,\n cancelLabel,\n loading = false,\n confirmText,\n },\n ref,\n) {\n const t = useT()\n const [submitting, setSubmitting] = useState(false)\n const busy = loading || submitting\n\n // type-to-confirm — cleared every time the dialog opens (\"adjust state on prop change\", no effect)\n const [typed, setTyped] = useState('')\n const wasOpen = useRef(open)\n if (wasOpen.current !== open) {\n wasOpen.current = open\n if (open) setTyped('')\n }\n const matches = confirmText == null || typed.trim() === confirmText.trim()\n const promptId = useId()\n\n // dismissal is blocked while the confirm is running (so the delete can't be abandoned mid-flight)\n const handleClose = () => {\n if (!busy) onClose()\n }\n\n const handleConfirm = async () => {\n if (!matches || busy) return\n try {\n setSubmitting(true)\n await onConfirm()\n onClose() // auto-close on success\n } catch {\n // keep the dialog open on failure so the user can retry; the consumer surfaces the error\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <Modal\n ref={ref}\n open={open}\n onClose={handleClose}\n size=\"sm\"\n bodyClassName={styles.modalBody}\n title={title ?? t('removeDialog.title')}\n showCloseButton={!busy}\n closeOnBackdrop={!busy}\n closeOnEscape={!busy}\n footer={\n <>\n <Button variant=\"text\" size=\"sm\" onClick={handleClose} disabled={busy}>\n {cancelLabel ?? t('common.cancel')}\n </Button>\n <Button\n color=\"error\"\n variant=\"filled\"\n size=\"sm\"\n onClick={handleConfirm}\n loading={busy}\n disabled={!matches}\n >\n {confirmLabel ?? t('common.delete')}\n </Button>\n </>\n }\n >\n <div className={styles.body}>\n <div className={styles.iconBox} aria-hidden>\n <Icon name=\"Trash\" />\n </div>\n <Typography align=\"center\">{message ?? t('removeDialog.message')}</Typography>\n {confirmText != null && (\n <div className={styles.confirm}>\n {/* the instruction, as an error-tinted quote — it also names the field (aria-labelledby) */}\n <Blockquote id={promptId} size=\"sm\" color=\"error\">\n <TypeToConfirmLabel text={confirmText} />\n </Blockquote>\n <TextField\n aria-labelledby={promptId}\n placeholder={confirmText}\n value={typed}\n onChange={(event) => setTyped(event.target.value)}\n onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void handleConfirm()\n }\n }}\n // a green tick once the phrase matches — the field confirms itself before Delete does\n adornment={typed && matches ? <Icon name=\"TickCircle\" color=\"success\" /> : undefined}\n adornmentPosition=\"right\"\n disabled={busy}\n size=\"sm\"\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n />\n </div>\n )}\n </div>\n </Modal>\n )\n})\n\n/** \"Type {text} to confirm\" with the text set as inline `Code`, so the exact phrase stands out. */\nfunction TypeToConfirmLabel({ text }: { text: string }) {\n const t = useT()\n // translate with a sentinel in place of {text}, then split around it to slot the Code element in\n const SENTINEL = '\\u0000'\n const [before, after = ''] = t('removeDialog.typeToConfirm', { text: SENTINEL }).split(SENTINEL)\n return (\n <>\n {before}\n <Code size=\"sm\" color=\"error\" className={styles.phrase}>\n {text}\n </Code>\n {after}\n </>\n )\n}\n"],"names":["RemoveDialog","forwardRef","open","onClose","onConfirm","title","message","confirmLabel","cancelLabel","loading","confirmText","ref","t","useT","submitting","setSubmitting","useState","busy","typed","setTyped","wasOpen","useRef","matches","promptId","useId","handleClose","handleConfirm","jsx","Modal","styles","jsxs","Fragment","Button","Icon","Typography","Blockquote","TypeToConfirmLabel","TextField","event","text","SENTINEL","before","after","Code"],"mappings":"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"}
1
+ {"version":3,"file":"RemoveDialog-B_Y18MeJ.cjs","sources":["../src/components/RemoveDialog/RemoveDialog.tsx"],"sourcesContent":["import { forwardRef, useId, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport { useT } from '../../theme'\nimport { Blockquote } from '../Blockquote'\nimport { Button } from '../Button'\nimport { Code } from '../Code'\nimport { Icon } from '../Icon'\nimport { Modal } from '../Modal'\nimport { TextField } from '../TextField'\nimport { Typography } from '../Typography'\nimport styles from './RemoveDialog.module.css'\n\nexport interface RemoveDialogProps {\n /** Whether the dialog is open. */\n open: boolean\n /** Called on Cancel, the × button, backdrop, or Escape (ignored while a confirm is in flight). */\n onClose: () => void\n /**\n * The destructive action. May be async — while its promise is pending the Delete button shows a\n * loader and the dialog locks (no dismiss); on success the dialog **auto-closes**, on rejection it\n * stays open so the user can retry (surface the error yourself, e.g. a toast).\n */\n onConfirm: () => void | Promise<void>\n /** Header title. Defaults to `\"Remove\"`. */\n title?: ReactNode\n /** Body prompt shown under the icon. Defaults to `\"Are you sure you want to delete?\"`. */\n message?: ReactNode\n /** Confirm (destructive) button label. Defaults to `\"Delete\"`. */\n confirmLabel?: string\n /** Cancel button label. Defaults to `\"Cancel\"`. */\n cancelLabel?: string\n /** External loading override — combined with the internal async-confirm state. */\n loading?: boolean\n /**\n * **Type-to-confirm** for high-risk deletes: the user must type this exact text (e.g. `\"remove user\"`)\n * into a field before Delete enables. Case-sensitive; surrounding whitespace is ignored. The field is\n * focused on open, cleared on every open, and Enter confirms once it matches.\n */\n confirmText?: string\n}\n\n/**\n * A ready-made delete confirmation — a thin convenience wrapper over `Modal` for the ubiquitous\n * \"Are you sure you want to delete?\" prompt. Hardcodes the destructive flavor (a `Trash` glyph in a\n * soft error circle + an `error` Delete button); just wire `open` / `onClose` / `onConfirm`. An async\n * `onConfirm` drives the button loader and locks dismissal, then auto-closes on success. `confirmText`\n * adds **type-to-confirm**: Delete stays disabled until the user types that exact text. Built on\n * `Modal`, so it inherits the portal, scroll-lock, focus-trap, and backdrop/Escape dismissal.\n *\n * @example\n * const { isOpen, open, close } = useDisclosure()\n * <RemoveDialog open={isOpen} onClose={close} onConfirm={() => deleteItem(id)} />\n */\nexport const RemoveDialog = forwardRef<HTMLDivElement, RemoveDialogProps>(function RemoveDialog(\n {\n open,\n onClose,\n onConfirm,\n title,\n message,\n confirmLabel,\n cancelLabel,\n loading = false,\n confirmText,\n },\n ref,\n) {\n const t = useT()\n const [submitting, setSubmitting] = useState(false)\n const busy = loading || submitting\n\n // type-to-confirm — cleared every time the dialog opens (\"adjust state on prop change\", no effect)\n const [typed, setTyped] = useState('')\n const wasOpen = useRef(open)\n if (wasOpen.current !== open) {\n wasOpen.current = open\n if (open) setTyped('')\n }\n const matches = confirmText == null || typed.trim() === confirmText.trim()\n const promptId = useId()\n\n // dismissal is blocked while the confirm is running (so the delete can't be abandoned mid-flight)\n const handleClose = () => {\n if (!busy) onClose()\n }\n\n const handleConfirm = async () => {\n if (!matches || busy) return\n try {\n setSubmitting(true)\n await onConfirm()\n onClose() // auto-close on success\n } catch {\n // keep the dialog open on failure so the user can retry; the consumer surfaces the error\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <Modal\n ref={ref}\n open={open}\n onClose={handleClose}\n size=\"sm\"\n bodyClassName={styles.modalBody}\n title={title ?? t('removeDialog.title')}\n showCloseButton={!busy}\n closeOnBackdrop={!busy}\n closeOnEscape={!busy}\n footer={\n <>\n <Button variant=\"text\" size=\"sm\" onClick={handleClose} disabled={busy}>\n {cancelLabel ?? t('common.cancel')}\n </Button>\n <Button\n color=\"error\"\n variant=\"filled\"\n size=\"sm\"\n onClick={handleConfirm}\n loading={busy}\n disabled={!matches}\n >\n {confirmLabel ?? t('common.delete')}\n </Button>\n </>\n }\n >\n <div className={styles.body}>\n <div className={styles.iconBox} aria-hidden>\n <Icon name=\"Trash\" />\n </div>\n <Typography align=\"center\">{message ?? t('removeDialog.message')}</Typography>\n {confirmText != null && (\n <div className={styles.confirm}>\n {/* the instruction, as an error-tinted quote — it also names the field (aria-labelledby) */}\n <Blockquote id={promptId} size=\"sm\" color=\"error\">\n <TypeToConfirmLabel text={confirmText} />\n </Blockquote>\n <TextField\n aria-labelledby={promptId}\n placeholder={confirmText}\n value={typed}\n onChange={(event) => setTyped(event.target.value)}\n onKeyDown={(event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void handleConfirm()\n }\n }}\n // a green tick once the phrase matches — the field confirms itself before Delete does\n adornment={typed && matches ? <Icon name=\"TickCircle\" color=\"success\" /> : undefined}\n adornmentPosition=\"right\"\n disabled={busy}\n size=\"sm\"\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n />\n </div>\n )}\n </div>\n </Modal>\n )\n})\n\n/** \"Type {text} to confirm\" with the text set as inline `Code`, so the exact phrase stands out. */\nfunction TypeToConfirmLabel({ text }: { text: string }) {\n const t = useT()\n // translate with a sentinel in place of {text}, then split around it to slot the Code element in\n const SENTINEL = '\\u0000'\n const [before, after = ''] = t('removeDialog.typeToConfirm', { text: SENTINEL }).split(SENTINEL)\n return (\n <>\n {before}\n <Code size=\"sm\" color=\"error\" className={styles.phrase}>\n {text}\n </Code>\n {after}\n </>\n )\n}\n"],"names":["RemoveDialog","forwardRef","open","onClose","onConfirm","title","message","confirmLabel","cancelLabel","loading","confirmText","ref","t","useT","submitting","setSubmitting","useState","busy","typed","setTyped","wasOpen","useRef","matches","promptId","useId","handleClose","handleConfirm","jsx","Modal","styles","jsxs","Fragment","Button","Icon","Typography","Blockquote","TypeToConfirmLabel","TextField","event","text","SENTINEL","before","after","Code"],"mappings":"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"}