@diwauhris/ui 1.0.0

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 (272) hide show
  1. package/README.md +75 -0
  2. package/lib/assets/ui.css +154 -0
  3. package/lib/index.cjs +183 -0
  4. package/lib/index.mjs +65 -0
  5. package/lib/index10.cjs +33 -0
  6. package/lib/index10.cjs.map +1 -0
  7. package/lib/index10.mjs +33 -0
  8. package/lib/index10.mjs.map +1 -0
  9. package/lib/index11.cjs +16 -0
  10. package/lib/index11.cjs.map +1 -0
  11. package/lib/index11.mjs +16 -0
  12. package/lib/index11.mjs.map +1 -0
  13. package/lib/index12.cjs +16 -0
  14. package/lib/index12.cjs.map +1 -0
  15. package/lib/index12.mjs +15 -0
  16. package/lib/index12.mjs.map +1 -0
  17. package/lib/index13.cjs +57 -0
  18. package/lib/index13.cjs.map +1 -0
  19. package/lib/index13.mjs +57 -0
  20. package/lib/index13.mjs.map +1 -0
  21. package/lib/index14.cjs +10 -0
  22. package/lib/index14.cjs.map +1 -0
  23. package/lib/index14.mjs +10 -0
  24. package/lib/index14.mjs.map +1 -0
  25. package/lib/index15.cjs +30 -0
  26. package/lib/index15.cjs.map +1 -0
  27. package/lib/index15.mjs +30 -0
  28. package/lib/index15.mjs.map +1 -0
  29. package/lib/index16.cjs +28 -0
  30. package/lib/index16.cjs.map +1 -0
  31. package/lib/index16.mjs +26 -0
  32. package/lib/index16.mjs.map +1 -0
  33. package/lib/index17.cjs +43 -0
  34. package/lib/index17.cjs.map +1 -0
  35. package/lib/index17.mjs +43 -0
  36. package/lib/index17.mjs.map +1 -0
  37. package/lib/index18.cjs +59 -0
  38. package/lib/index18.cjs.map +1 -0
  39. package/lib/index18.mjs +59 -0
  40. package/lib/index18.mjs.map +1 -0
  41. package/lib/index19.cjs +67 -0
  42. package/lib/index19.cjs.map +1 -0
  43. package/lib/index19.mjs +67 -0
  44. package/lib/index19.mjs.map +1 -0
  45. package/lib/index2.cjs +33 -0
  46. package/lib/index2.mjs +13 -0
  47. package/lib/index20.cjs +79 -0
  48. package/lib/index20.cjs.map +1 -0
  49. package/lib/index20.mjs +79 -0
  50. package/lib/index20.mjs.map +1 -0
  51. package/lib/index21.cjs +105 -0
  52. package/lib/index21.cjs.map +1 -0
  53. package/lib/index21.mjs +104 -0
  54. package/lib/index21.mjs.map +1 -0
  55. package/lib/index22.cjs +50 -0
  56. package/lib/index22.cjs.map +1 -0
  57. package/lib/index22.mjs +50 -0
  58. package/lib/index22.mjs.map +1 -0
  59. package/lib/index23.cjs +143 -0
  60. package/lib/index23.cjs.map +1 -0
  61. package/lib/index23.mjs +143 -0
  62. package/lib/index23.mjs.map +1 -0
  63. package/lib/index25.cjs +177 -0
  64. package/lib/index25.cjs.map +1 -0
  65. package/lib/index25.mjs +175 -0
  66. package/lib/index25.mjs.map +1 -0
  67. package/lib/index26.cjs +108 -0
  68. package/lib/index26.cjs.map +1 -0
  69. package/lib/index26.mjs +108 -0
  70. package/lib/index26.mjs.map +1 -0
  71. package/lib/index27.cjs +120 -0
  72. package/lib/index27.cjs.map +1 -0
  73. package/lib/index27.mjs +120 -0
  74. package/lib/index27.mjs.map +1 -0
  75. package/lib/index28.cjs +54 -0
  76. package/lib/index28.cjs.map +1 -0
  77. package/lib/index28.mjs +54 -0
  78. package/lib/index28.mjs.map +1 -0
  79. package/lib/index29.cjs +100 -0
  80. package/lib/index29.cjs.map +1 -0
  81. package/lib/index29.mjs +100 -0
  82. package/lib/index29.mjs.map +1 -0
  83. package/lib/index3.cjs +38 -0
  84. package/lib/index3.cjs.map +1 -0
  85. package/lib/index3.mjs +38 -0
  86. package/lib/index3.mjs.map +1 -0
  87. package/lib/index30.cjs +27 -0
  88. package/lib/index30.cjs.map +1 -0
  89. package/lib/index30.mjs +27 -0
  90. package/lib/index30.mjs.map +1 -0
  91. package/lib/index31.cjs +72 -0
  92. package/lib/index31.cjs.map +1 -0
  93. package/lib/index31.mjs +72 -0
  94. package/lib/index31.mjs.map +1 -0
  95. package/lib/index32.cjs +19 -0
  96. package/lib/index32.cjs.map +1 -0
  97. package/lib/index32.mjs +19 -0
  98. package/lib/index32.mjs.map +1 -0
  99. package/lib/index33.cjs +25 -0
  100. package/lib/index33.cjs.map +1 -0
  101. package/lib/index33.mjs +25 -0
  102. package/lib/index33.mjs.map +1 -0
  103. package/lib/index34.cjs +55 -0
  104. package/lib/index34.cjs.map +1 -0
  105. package/lib/index34.mjs +51 -0
  106. package/lib/index34.mjs.map +1 -0
  107. package/lib/index35.cjs +25 -0
  108. package/lib/index35.cjs.map +1 -0
  109. package/lib/index35.mjs +25 -0
  110. package/lib/index35.mjs.map +1 -0
  111. package/lib/index36.cjs +54 -0
  112. package/lib/index36.cjs.map +1 -0
  113. package/lib/index36.mjs +49 -0
  114. package/lib/index36.mjs.map +1 -0
  115. package/lib/index37.cjs +130 -0
  116. package/lib/index37.cjs.map +1 -0
  117. package/lib/index37.mjs +128 -0
  118. package/lib/index37.mjs.map +1 -0
  119. package/lib/index38.cjs +106 -0
  120. package/lib/index38.cjs.map +1 -0
  121. package/lib/index38.mjs +102 -0
  122. package/lib/index38.mjs.map +1 -0
  123. package/lib/index39.cjs +137 -0
  124. package/lib/index39.cjs.map +1 -0
  125. package/lib/index39.mjs +137 -0
  126. package/lib/index39.mjs.map +1 -0
  127. package/lib/index4.cjs +24 -0
  128. package/lib/index4.cjs.map +1 -0
  129. package/lib/index4.mjs +24 -0
  130. package/lib/index4.mjs.map +1 -0
  131. package/lib/index40.cjs +130 -0
  132. package/lib/index40.cjs.map +1 -0
  133. package/lib/index40.mjs +126 -0
  134. package/lib/index40.mjs.map +1 -0
  135. package/lib/index41.cjs +60 -0
  136. package/lib/index41.cjs.map +1 -0
  137. package/lib/index41.mjs +58 -0
  138. package/lib/index41.mjs.map +1 -0
  139. package/lib/index42.cjs +105 -0
  140. package/lib/index42.cjs.map +1 -0
  141. package/lib/index42.mjs +105 -0
  142. package/lib/index42.mjs.map +1 -0
  143. package/lib/index43.cjs +47 -0
  144. package/lib/index43.cjs.map +1 -0
  145. package/lib/index43.mjs +42 -0
  146. package/lib/index43.mjs.map +1 -0
  147. package/lib/index44.cjs +40 -0
  148. package/lib/index44.cjs.map +1 -0
  149. package/lib/index44.mjs +35 -0
  150. package/lib/index44.mjs.map +1 -0
  151. package/lib/index45.cjs +91 -0
  152. package/lib/index45.cjs.map +1 -0
  153. package/lib/index45.mjs +91 -0
  154. package/lib/index45.mjs.map +1 -0
  155. package/lib/index46.cjs +180 -0
  156. package/lib/index46.cjs.map +1 -0
  157. package/lib/index46.mjs +180 -0
  158. package/lib/index46.mjs.map +1 -0
  159. package/lib/index47.cjs +61 -0
  160. package/lib/index47.cjs.map +1 -0
  161. package/lib/index47.mjs +61 -0
  162. package/lib/index47.mjs.map +1 -0
  163. package/lib/index48.cjs +165 -0
  164. package/lib/index48.cjs.map +1 -0
  165. package/lib/index48.mjs +161 -0
  166. package/lib/index48.mjs.map +1 -0
  167. package/lib/index49.cjs +83 -0
  168. package/lib/index49.cjs.map +1 -0
  169. package/lib/index49.mjs +83 -0
  170. package/lib/index49.mjs.map +1 -0
  171. package/lib/index5.cjs +39 -0
  172. package/lib/index5.cjs.map +1 -0
  173. package/lib/index5.mjs +39 -0
  174. package/lib/index5.mjs.map +1 -0
  175. package/lib/index50.cjs +100 -0
  176. package/lib/index50.cjs.map +1 -0
  177. package/lib/index50.mjs +100 -0
  178. package/lib/index50.mjs.map +1 -0
  179. package/lib/index51.cjs +116 -0
  180. package/lib/index51.cjs.map +1 -0
  181. package/lib/index51.mjs +113 -0
  182. package/lib/index51.mjs.map +1 -0
  183. package/lib/index52.cjs +93 -0
  184. package/lib/index52.cjs.map +1 -0
  185. package/lib/index52.mjs +93 -0
  186. package/lib/index52.mjs.map +1 -0
  187. package/lib/index53.cjs +120 -0
  188. package/lib/index53.cjs.map +1 -0
  189. package/lib/index53.mjs +120 -0
  190. package/lib/index53.mjs.map +1 -0
  191. package/lib/index54.cjs +53 -0
  192. package/lib/index54.cjs.map +1 -0
  193. package/lib/index54.mjs +50 -0
  194. package/lib/index54.mjs.map +1 -0
  195. package/lib/index55.cjs +130 -0
  196. package/lib/index55.cjs.map +1 -0
  197. package/lib/index55.mjs +127 -0
  198. package/lib/index55.mjs.map +1 -0
  199. package/lib/index56.cjs +36 -0
  200. package/lib/index56.cjs.map +1 -0
  201. package/lib/index56.mjs +35 -0
  202. package/lib/index56.mjs.map +1 -0
  203. package/lib/index57.cjs +47 -0
  204. package/lib/index57.cjs.map +1 -0
  205. package/lib/index57.mjs +47 -0
  206. package/lib/index57.mjs.map +1 -0
  207. package/lib/index58.cjs +97 -0
  208. package/lib/index58.cjs.map +1 -0
  209. package/lib/index58.mjs +97 -0
  210. package/lib/index58.mjs.map +1 -0
  211. package/lib/index59.cjs +120 -0
  212. package/lib/index59.cjs.map +1 -0
  213. package/lib/index59.mjs +116 -0
  214. package/lib/index59.mjs.map +1 -0
  215. package/lib/index6.cjs +29 -0
  216. package/lib/index6.cjs.map +1 -0
  217. package/lib/index6.mjs +29 -0
  218. package/lib/index6.mjs.map +1 -0
  219. package/lib/index60.cjs +34 -0
  220. package/lib/index60.cjs.map +1 -0
  221. package/lib/index60.mjs +34 -0
  222. package/lib/index60.mjs.map +1 -0
  223. package/lib/index61.cjs +62 -0
  224. package/lib/index61.cjs.map +1 -0
  225. package/lib/index61.mjs +62 -0
  226. package/lib/index61.mjs.map +1 -0
  227. package/lib/index62.cjs +213 -0
  228. package/lib/index62.cjs.map +1 -0
  229. package/lib/index62.mjs +213 -0
  230. package/lib/index62.mjs.map +1 -0
  231. package/lib/index63.cjs +42 -0
  232. package/lib/index63.cjs.map +1 -0
  233. package/lib/index63.mjs +42 -0
  234. package/lib/index63.mjs.map +1 -0
  235. package/lib/index64.cjs +86 -0
  236. package/lib/index64.cjs.map +1 -0
  237. package/lib/index64.mjs +86 -0
  238. package/lib/index64.mjs.map +1 -0
  239. package/lib/index65.cjs +156 -0
  240. package/lib/index65.cjs.map +1 -0
  241. package/lib/index65.mjs +156 -0
  242. package/lib/index65.mjs.map +1 -0
  243. package/lib/index66.cjs +107 -0
  244. package/lib/index66.cjs.map +1 -0
  245. package/lib/index66.mjs +106 -0
  246. package/lib/index66.mjs.map +1 -0
  247. package/lib/index67.cjs +52 -0
  248. package/lib/index67.cjs.map +1 -0
  249. package/lib/index67.mjs +52 -0
  250. package/lib/index67.mjs.map +1 -0
  251. package/lib/index68.cjs +125 -0
  252. package/lib/index68.cjs.map +1 -0
  253. package/lib/index68.mjs +115 -0
  254. package/lib/index68.mjs.map +1 -0
  255. package/lib/index69.cjs +34 -0
  256. package/lib/index69.cjs.map +1 -0
  257. package/lib/index69.mjs +29 -0
  258. package/lib/index69.mjs.map +1 -0
  259. package/lib/index7.cjs +17 -0
  260. package/lib/index7.cjs.map +1 -0
  261. package/lib/index7.mjs +17 -0
  262. package/lib/index7.mjs.map +1 -0
  263. package/lib/index8.cjs +86 -0
  264. package/lib/index8.cjs.map +1 -0
  265. package/lib/index8.mjs +86 -0
  266. package/lib/index8.mjs.map +1 -0
  267. package/lib/index9.cjs +29 -0
  268. package/lib/index9.cjs.map +1 -0
  269. package/lib/index9.mjs +29 -0
  270. package/lib/index9.mjs.map +1 -0
  271. package/lib/types/index.d.ts +1 -0
  272. package/package.json +68 -0
@@ -0,0 +1,54 @@
1
+ //#region src/ui-library/tokens/typography.ts
2
+ var typography_exports = /* @__PURE__ */ require("./index2.cjs").__exportAll({ typography: () => typography });
3
+ /**
4
+ * DIWA Typography Tokens
5
+ *
6
+ * Formalizes the typographic vocabulary identified in the Phase 9 audit.
7
+ * Values are Tailwind utility class strings.
8
+ *
9
+ * Two brand typefaces (defined in tailwind.config.js):
10
+ * font-heading — Barlow (700/800) — headings, titles, overlay headers
11
+ * font-body — Libre Franklin (variable) — all other UI text (inherited)
12
+ *
13
+ * Known inconsistency resolved in Phase 11:
14
+ * Modal and Dialog titles now both use typography.titleOverlay.
15
+ * Drawer title uses font-heading text-base font-bold directly — align in a future pass.
16
+ */
17
+ var typography = {
18
+ /**
19
+ * Overlay title (Modal, Dialog, Drawer).
20
+ * Applied to: Modal (DialogPrimitive.Title) and Dialog (DialogPrimitive.Title).
21
+ * Drawer title uses font-heading text-base font-bold directly — align in a future pass.
22
+ */
23
+ titleOverlay: "font-heading text-base font-bold leading-tight",
24
+ /**
25
+ * Section/card heading.
26
+ */
27
+ titleSection: "font-heading text-sm font-bold text-slate-900",
28
+ /**
29
+ * Uppercase field label — used above form inputs.
30
+ */
31
+ labelUppercase: "text-xs font-bold uppercase tracking-wide text-slate-500",
32
+ /**
33
+ * Helper/error text below form fields.
34
+ */
35
+ helper: "text-xs font-medium",
36
+ /**
37
+ * Primary UI text — buttons, body, inputs, descriptions.
38
+ */
39
+ body: "text-sm font-medium",
40
+ /**
41
+ * Secondary/supporting text.
42
+ */
43
+ secondary: "text-xs font-medium text-slate-500"
44
+ };
45
+ //#endregion
46
+ exports.typography = typography;
47
+ Object.defineProperty(exports, "typography_exports", {
48
+ enumerable: true,
49
+ get: function() {
50
+ return typography_exports;
51
+ }
52
+ });
53
+
54
+ //# sourceMappingURL=index36.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index36.cjs","names":[],"sources":["../src/ui-library/tokens/typography.ts"],"sourcesContent":["/**\n * DIWA Typography Tokens\n *\n * Formalizes the typographic vocabulary identified in the Phase 9 audit.\n * Values are Tailwind utility class strings.\n *\n * Two brand typefaces (defined in tailwind.config.js):\n * font-heading — Barlow (700/800) — headings, titles, overlay headers\n * font-body — Libre Franklin (variable) — all other UI text (inherited)\n *\n * Known inconsistency resolved in Phase 11:\n * Modal and Dialog titles now both use typography.titleOverlay.\n * Drawer title uses font-heading text-base font-bold directly — align in a future pass.\n */\n\nexport const typography = {\n // ── Heading roles ─────────────────────────────────────────────────────────\n\n /**\n * Overlay title (Modal, Dialog, Drawer).\n * Applied to: Modal (DialogPrimitive.Title) and Dialog (DialogPrimitive.Title).\n * Drawer title uses font-heading text-base font-bold directly — align in a future pass.\n */\n titleOverlay: 'font-heading text-base font-bold leading-tight',\n\n /**\n * Section/card heading.\n */\n titleSection: 'font-heading text-sm font-bold text-slate-900',\n\n // ── Label roles ───────────────────────────────────────────────────────────\n\n /**\n * Uppercase field label — used above form inputs.\n */\n labelUppercase: 'text-xs font-bold uppercase tracking-wide text-slate-500',\n\n /**\n * Helper/error text below form fields.\n */\n helper: 'text-xs font-medium',\n\n // ── Body roles ────────────────────────────────────────────────────────────\n\n /**\n * Primary UI text — buttons, body, inputs, descriptions.\n */\n body: 'text-sm font-medium',\n\n /**\n * Secondary/supporting text.\n */\n secondary: 'text-xs font-medium text-slate-500',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,IAAa,aAAa;;;;;;CAQxB,cAAgB;;;;CAKhB,cAAgB;;;;CAOhB,gBAAgB;;;;CAKhB,QAAgB;;;;CAOhB,MAAgB;;;;CAKhB,WAAgB;AAClB"}
@@ -0,0 +1,49 @@
1
+ import { __exportAll } from "./index2.mjs";
2
+ //#region src/ui-library/tokens/typography.ts
3
+ var typography_exports = /* @__PURE__ */ __exportAll({ typography: () => typography });
4
+ /**
5
+ * DIWA Typography Tokens
6
+ *
7
+ * Formalizes the typographic vocabulary identified in the Phase 9 audit.
8
+ * Values are Tailwind utility class strings.
9
+ *
10
+ * Two brand typefaces (defined in tailwind.config.js):
11
+ * font-heading — Barlow (700/800) — headings, titles, overlay headers
12
+ * font-body — Libre Franklin (variable) — all other UI text (inherited)
13
+ *
14
+ * Known inconsistency resolved in Phase 11:
15
+ * Modal and Dialog titles now both use typography.titleOverlay.
16
+ * Drawer title uses font-heading text-base font-bold directly — align in a future pass.
17
+ */
18
+ var typography = {
19
+ /**
20
+ * Overlay title (Modal, Dialog, Drawer).
21
+ * Applied to: Modal (DialogPrimitive.Title) and Dialog (DialogPrimitive.Title).
22
+ * Drawer title uses font-heading text-base font-bold directly — align in a future pass.
23
+ */
24
+ titleOverlay: "font-heading text-base font-bold leading-tight",
25
+ /**
26
+ * Section/card heading.
27
+ */
28
+ titleSection: "font-heading text-sm font-bold text-slate-900",
29
+ /**
30
+ * Uppercase field label — used above form inputs.
31
+ */
32
+ labelUppercase: "text-xs font-bold uppercase tracking-wide text-slate-500",
33
+ /**
34
+ * Helper/error text below form fields.
35
+ */
36
+ helper: "text-xs font-medium",
37
+ /**
38
+ * Primary UI text — buttons, body, inputs, descriptions.
39
+ */
40
+ body: "text-sm font-medium",
41
+ /**
42
+ * Secondary/supporting text.
43
+ */
44
+ secondary: "text-xs font-medium text-slate-500"
45
+ };
46
+ //#endregion
47
+ export { typography, typography_exports };
48
+
49
+ //# sourceMappingURL=index36.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index36.mjs","names":[],"sources":["../src/ui-library/tokens/typography.ts"],"sourcesContent":["/**\n * DIWA Typography Tokens\n *\n * Formalizes the typographic vocabulary identified in the Phase 9 audit.\n * Values are Tailwind utility class strings.\n *\n * Two brand typefaces (defined in tailwind.config.js):\n * font-heading — Barlow (700/800) — headings, titles, overlay headers\n * font-body — Libre Franklin (variable) — all other UI text (inherited)\n *\n * Known inconsistency resolved in Phase 11:\n * Modal and Dialog titles now both use typography.titleOverlay.\n * Drawer title uses font-heading text-base font-bold directly — align in a future pass.\n */\n\nexport const typography = {\n // ── Heading roles ─────────────────────────────────────────────────────────\n\n /**\n * Overlay title (Modal, Dialog, Drawer).\n * Applied to: Modal (DialogPrimitive.Title) and Dialog (DialogPrimitive.Title).\n * Drawer title uses font-heading text-base font-bold directly — align in a future pass.\n */\n titleOverlay: 'font-heading text-base font-bold leading-tight',\n\n /**\n * Section/card heading.\n */\n titleSection: 'font-heading text-sm font-bold text-slate-900',\n\n // ── Label roles ───────────────────────────────────────────────────────────\n\n /**\n * Uppercase field label — used above form inputs.\n */\n labelUppercase: 'text-xs font-bold uppercase tracking-wide text-slate-500',\n\n /**\n * Helper/error text below form fields.\n */\n helper: 'text-xs font-medium',\n\n // ── Body roles ────────────────────────────────────────────────────────────\n\n /**\n * Primary UI text — buttons, body, inputs, descriptions.\n */\n body: 'text-sm font-medium',\n\n /**\n * Secondary/supporting text.\n */\n secondary: 'text-xs font-medium text-slate-500',\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;AAeA,IAAa,aAAa;;;;;;CAQxB,cAAgB;;;;CAKhB,cAAgB;;;;CAOhB,gBAAgB;;;;CAKhB,QAAgB;;;;CAOhB,MAAgB;;;;CAKhB,WAAgB;AAClB"}
@@ -0,0 +1,130 @@
1
+ const require_runtime = require("./index2.cjs");
2
+ const require_utils = require("./index14.cjs");
3
+ const require_typography = require("./index36.cjs");
4
+ let react_jsx_runtime = require("react/jsx-runtime");
5
+ require("react");
6
+ let lucide_react = require("lucide-react");
7
+ let _radix_ui_react_dialog = require("@radix-ui/react-dialog");
8
+ _radix_ui_react_dialog = require_runtime.__toESM(_radix_ui_react_dialog, 1);
9
+ //#region src/ui-library/gallery/modal/Modal.tsx
10
+ /**
11
+ * Modal — Gallery-native dialog component.
12
+ *
13
+ * Behavior is provided by @radix-ui/react-dialog, which handles:
14
+ * - Focus trap (Tab / Shift+Tab contained within panel)
15
+ * - Focus restoration on close (correct on every open/close cycle)
16
+ * - Initial focus (first focusable element on open)
17
+ * - Escape key closes the modal
18
+ * - Outside-click (pointer-down) closes the modal
19
+ * - Body scroll lock while open
20
+ * - Portal rendering to document.body
21
+ * - role="dialog" + aria-modal + aria-labelledby + aria-describedby
22
+ *
23
+ * DIWA continues to own:
24
+ * - Public API: isOpen, onClose, title, icon, subtitle, headerMeta,
25
+ * footer, onSubmit, submitLabel, submittingLabel, submitting,
26
+ * danger, error, maxWidth, closeLabel, children
27
+ * - Backdrop visual treatment
28
+ * - Panel visual treatment (border, radius, shadow, max-height, max-width)
29
+ * - Header layout: icon badge, title, subtitle, headerMeta
30
+ * - Form wrapper (onSubmit branch)
31
+ * - Default footer buttons (Cancel + Submit)
32
+ * - Error display
33
+ * - Close button styling
34
+ * - Danger tone (rose badge, rose title, rose submit button)
35
+ */
36
+ function Modal({ isOpen, onClose, title, children, icon, subtitle, headerMeta, footer, onSubmit, submitLabel = "Submit", submittingLabel = "Saving...", submitting = false, danger = false, error, maxWidth = "max-w-xl", closeLabel = "Close" }) {
37
+ const open = isOpen !== false;
38
+ const defaultFooter = onSubmit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
39
+ className: "flex items-center justify-end gap-3",
40
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
41
+ type: "button",
42
+ onClick: onClose,
43
+ className: "px-4 py-2 text-sm font-bold text-slate-600 border border-slate-200 rounded-md hover:bg-slate-50 hover:border-slate-300 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
44
+ children: "Cancel"
45
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
46
+ type: "submit",
47
+ disabled: submitting,
48
+ className: require_utils.cn("px-4 py-2 text-sm font-bold text-white rounded-md transition-all focus-visible:outline-none focus-visible:ring-2 disabled:opacity-60 disabled:cursor-not-allowed", danger ? "bg-rose-600 hover:bg-rose-700 focus-visible:ring-rose-300" : "bg-brand-blue hover:bg-brand-navy focus-visible:ring-brand-blue/30"),
49
+ children: submitting ? submittingLabel : submitLabel
50
+ })]
51
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
52
+ className: "flex items-center justify-end",
53
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54
+ type: "button",
55
+ onClick: onClose,
56
+ className: "px-4 py-2 text-sm font-bold text-slate-600 border border-slate-200 rounded-md hover:bg-slate-50 hover:border-slate-300 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
57
+ children: "Cancel"
58
+ })
59
+ });
60
+ const resolvedFooter = footer ?? defaultFooter;
61
+ const iconBadgeClass = danger ? "flex h-8 w-8 items-center justify-center rounded-lg bg-rose-50 text-rose-600 shrink-0" : "flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-brand-blue shrink-0";
62
+ const bodyContent = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
63
+ className: "mb-4 rounded-lg border border-rose-100 bg-rose-50 px-4 py-3 text-sm font-medium text-rose-700",
64
+ children: error
65
+ }), children] });
66
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Root, {
67
+ open,
68
+ onOpenChange: (isOpen) => {
69
+ if (!isOpen) onClose();
70
+ },
71
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dialog.Portal, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Overlay, { className: "fixed inset-0 z-50 bg-brand-navy/50 backdrop-blur-sm" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dialog.Content, {
72
+ className: require_utils.cn("fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2", "flex w-full flex-col", "rounded-lg border border-slate-200 bg-white shadow-xl", "max-h-[85vh] outline-none", "p-4", maxWidth),
73
+ style: { padding: 0 },
74
+ children: [
75
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Close, {
76
+ asChild: true,
77
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
78
+ type: "button",
79
+ "aria-label": closeLabel,
80
+ className: "absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
81
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
82
+ size: 16,
83
+ "aria-hidden": "true"
84
+ })
85
+ })
86
+ }),
87
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
88
+ className: "px-6 pt-6 pb-4 border-b-2 border-slate-100",
89
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
90
+ className: "flex items-center gap-2.5",
91
+ children: [icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
92
+ className: iconBadgeClass,
93
+ children: icon
94
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Title, {
95
+ className: require_utils.cn(require_typography.typography.titleOverlay, danger ? "text-rose-700" : "text-slate-900"),
96
+ children: title
97
+ }), subtitle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Description, {
98
+ className: "text-xs text-slate-500 mt-0.5",
99
+ children: subtitle
100
+ })] })]
101
+ }), headerMeta && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
102
+ className: "mt-2",
103
+ children: headerMeta
104
+ })]
105
+ }),
106
+ onSubmit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
107
+ onSubmit,
108
+ className: "flex flex-col min-h-0 flex-1",
109
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
110
+ className: "flex-1 overflow-y-auto px-6 py-5",
111
+ children: bodyContent
112
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
113
+ className: "px-6 py-4 border-t border-slate-100",
114
+ children: resolvedFooter
115
+ })]
116
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
117
+ className: "flex-1 overflow-y-auto px-6 py-5",
118
+ children: bodyContent
119
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
120
+ className: "px-6 py-4 border-t border-slate-100",
121
+ children: resolvedFooter
122
+ })] })
123
+ ]
124
+ })] })
125
+ });
126
+ }
127
+ //#endregion
128
+ exports.default = Modal;
129
+
130
+ //# sourceMappingURL=index37.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index37.cjs","names":[],"sources":["../src/ui-library/gallery/modal/Modal.tsx"],"sourcesContent":["/**\n * Modal — Gallery-native dialog component.\n *\n * Behavior is provided by @radix-ui/react-dialog, which handles:\n * - Focus trap (Tab / Shift+Tab contained within panel)\n * - Focus restoration on close (correct on every open/close cycle)\n * - Initial focus (first focusable element on open)\n * - Escape key closes the modal\n * - Outside-click (pointer-down) closes the modal\n * - Body scroll lock while open\n * - Portal rendering to document.body\n * - role=\"dialog\" + aria-modal + aria-labelledby + aria-describedby\n *\n * DIWA continues to own:\n * - Public API: isOpen, onClose, title, icon, subtitle, headerMeta,\n * footer, onSubmit, submitLabel, submittingLabel, submitting,\n * danger, error, maxWidth, closeLabel, children\n * - Backdrop visual treatment\n * - Panel visual treatment (border, radius, shadow, max-height, max-width)\n * - Header layout: icon badge, title, subtitle, headerMeta\n * - Form wrapper (onSubmit branch)\n * - Default footer buttons (Cancel + Submit)\n * - Error display\n * - Close button styling\n * - Danger tone (rose badge, rose title, rose submit button)\n */\n\nimport * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { type ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { cn } from '../../../lib/utils';\nimport { typography } from '../../tokens/typography';\n\nexport interface ModalProps {\n isOpen?: boolean;\n onClose: () => void;\n title: ReactNode;\n children: ReactNode;\n icon?: ReactNode;\n subtitle?: ReactNode;\n headerMeta?: ReactNode;\n footer?: ReactNode;\n onSubmit?: (e: React.FormEvent<HTMLFormElement>) => void;\n submitLabel?: string;\n submittingLabel?: string;\n submitting?: boolean;\n danger?: boolean;\n error?: string;\n maxWidth?: string;\n closeLabel?: string;\n}\n\nexport default function Modal({\n isOpen,\n onClose,\n title,\n children,\n icon,\n subtitle,\n headerMeta,\n footer,\n onSubmit,\n submitLabel = 'Submit',\n submittingLabel = 'Saving...',\n submitting = false,\n danger = false,\n error,\n maxWidth = 'max-w-xl',\n closeLabel = 'Close',\n}: ModalProps) {\n // Radix open state: undefined isOpen means \"always mounted\" (the consumer\n // controls mount/unmount externally). false means explicitly closed.\n const open = isOpen !== false;\n\n // Default footer — DIWA-owned form actions, no Radix involvement\n const defaultFooter = onSubmit ? (\n <div className=\"flex items-center justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-bold text-slate-600 border border-slate-200 rounded-md hover:bg-slate-50 hover:border-slate-300 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={submitting}\n className={cn(\n 'px-4 py-2 text-sm font-bold text-white rounded-md transition-all focus-visible:outline-none focus-visible:ring-2 disabled:opacity-60 disabled:cursor-not-allowed',\n danger\n ? 'bg-rose-600 hover:bg-rose-700 focus-visible:ring-rose-300'\n : 'bg-brand-blue hover:bg-brand-navy focus-visible:ring-brand-blue/30',\n )}\n >\n {submitting ? submittingLabel : submitLabel}\n </button>\n </div>\n ) : (\n <div className=\"flex items-center justify-end\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-bold text-slate-600 border border-slate-200 rounded-md hover:bg-slate-50 hover:border-slate-300 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n Cancel\n </button>\n </div>\n );\n\n const resolvedFooter = footer ?? defaultFooter;\n\n const iconBadgeClass = danger\n ? 'flex h-8 w-8 items-center justify-center rounded-lg bg-rose-50 text-rose-600 shrink-0'\n : 'flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-brand-blue shrink-0';\n\n const bodyContent = (\n <>\n {error && (\n <div className=\"mb-4 rounded-lg border border-rose-100 bg-rose-50 px-4 py-3 text-sm font-medium text-rose-700\">\n {error}\n </div>\n )}\n {children}\n </>\n );\n\n return (\n <DialogPrimitive.Root\n open={open}\n // Radix calls onOpenChange(false) on Escape or outside click.\n // Route to DIWA's onClose — the existing behavior is always-closeable.\n onOpenChange={(isOpen) => { if (!isOpen) onClose(); }}\n >\n <DialogPrimitive.Portal>\n {/* Backdrop — DIWA visual treatment */}\n <DialogPrimitive.Overlay\n className=\"fixed inset-0 z-50 bg-brand-navy/50 backdrop-blur-sm\"\n />\n\n {/* Panel — DIWA visual treatment */}\n <DialogPrimitive.Content\n className={cn(\n 'fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2',\n 'flex w-full flex-col',\n 'rounded-lg border border-slate-200 bg-white shadow-xl',\n 'max-h-[85vh] outline-none',\n 'p-4',\n maxWidth,\n )}\n style={{ padding: 0 }}\n // Modal is always closeable — no preventClose equivalent in this API.\n // Radix default (close on Escape + outside click) matches existing behavior.\n >\n {/* Close button — Radix Dialog.Close triggers onOpenChange(false) */}\n <DialogPrimitive.Close asChild>\n <button\n type=\"button\"\n aria-label={closeLabel}\n className=\"absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n <X size={16} aria-hidden=\"true\" />\n </button>\n </DialogPrimitive.Close>\n\n {/* Header — DIWA-owned layout */}\n <div className=\"px-6 pt-6 pb-4 border-b-2 border-slate-100\">\n <div className=\"flex items-center gap-2.5\">\n {icon && (\n <div className={iconBadgeClass}>\n {icon}\n </div>\n )}\n <div>\n {/*\n DialogPrimitive.Title provides the aria-labelledby relationship\n automatically. DIWA visual classes are applied via className.\n title is ReactNode — can be string or JSX (e.g. with a badge).\n */}\n <DialogPrimitive.Title\n className={cn(\n typography.titleOverlay,\n danger ? 'text-rose-700' : 'text-slate-900',\n )}\n >\n {title}\n </DialogPrimitive.Title>\n {subtitle && (\n // DialogPrimitive.Description provides aria-describedby when subtitle is present.\n <DialogPrimitive.Description className=\"text-xs text-slate-500 mt-0.5\">\n {subtitle}\n </DialogPrimitive.Description>\n )}\n </div>\n </div>\n {headerMeta && (\n <div className=\"mt-2\">{headerMeta}</div>\n )}\n </div>\n\n {/* Body + footer — form branch vs non-form branch, DIWA-owned */}\n {onSubmit ? (\n <form onSubmit={onSubmit} className=\"flex flex-col min-h-0 flex-1\">\n <div className=\"flex-1 overflow-y-auto px-6 py-5\">\n {bodyContent}\n </div>\n <div className=\"px-6 py-4 border-t border-slate-100\">\n {resolvedFooter}\n </div>\n </form>\n ) : (\n <>\n <div className=\"flex-1 overflow-y-auto px-6 py-5\">\n {bodyContent}\n </div>\n <div className=\"px-6 py-4 border-t border-slate-100\">\n {resolvedFooter}\n </div>\n </>\n )}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAwB,MAAM,EAC5B,QACA,SACA,OACA,UACA,MACA,UACA,YACA,QACA,UACA,cAAkB,UAClB,kBAAkB,aAClB,aAAkB,OAClB,SAAkB,OAClB,OACA,WAAkB,YAClB,aAAkB,WACL;CAGb,MAAM,OAAO,WAAW;CAGxB,MAAM,gBAAgB,WACpB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;GACX,UAAA;EAEO,CAAA,GACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,MAAK;GACL,UAAU;GACV,WAAW,cAAA,GACT,oKACA,SACI,8DACA,oEACN;GAEC,UAAA,aAAa,kBAAkB;EAC1B,CAAA,CACL;CAEL,CAAA,IAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,WAAU;EACb,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;GACX,UAAA;EAEO,CAAA;CACL,CAAA;CAGP,MAAM,iBAAiB,UAAU;CAEjC,MAAM,iBAAiB,SACnB,0FACA;CAEJ,MAAM,cACJ,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACG,SACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,WAAU;EACZ,UAAA;CACE,CAAA,GAEN,QACD,EAAA,CAAA;CAGJ,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,MAAjB;EACQ;EAGN,eAAe,WAAW;GAAE,IAAI,CAAC,QAAQ,QAAQ;EAAG;EAEpD,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,uBAAgB,QAAjB,EAAA,UAAA,CAEE,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,SAAjB,EACE,WAAU,uDACX,CAAA,GAGD,iBAAA,GAAA,kBAAA,KAAA,CAAC,uBAAgB,SAAjB;GACE,WAAW,cAAA,GACT,iEACA,wBACA,yDACA,6BACA,OACA,QACF;GACA,OAAO,EAAE,SAAS,EAAE;GATtB,UAAA;IAcE,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,OAAjB;KAAuB,SAAA;KACrB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MACE,MAAK;MACL,cAAY;MACZ,WAAU;MAEV,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,GAAD;OAAG,MAAM;OAAI,eAAY;MAAQ,CAAA;KAC3B,CAAA;IACa,CAAA;IAGvB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACG,QACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAW;OACb,UAAA;MACE,CAAA,GAEP,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD,EAAA,UAAA,CAME,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,OAAjB;OACE,WAAW,cAAA,GACT,mBAAA,WAAW,cACX,SAAS,kBAAkB,gBAC7B;OAEC,UAAA;MACoB,CAAA,GACtB,YAEC,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,aAAjB;OAA6B,WAAU;OACpC,UAAA;MAC0B,CAAA,CAE5B,EAAA,CAAA,CACF;KACJ,CAAA,GAAA,cACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAQ,UAAA;KAAgB,CAAA,CAEtC;;IAGJ,WACC,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;KAAgB;KAAU,WAAU;KAApC,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MACZ,UAAA;KACE,CAAA,GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MACZ,UAAA;KACE,CAAA,CACD;IAEN,CAAA,IAAA,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAU;KACZ,UAAA;IACE,CAAA,GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAU;KACZ,UAAA;IACE,CAAA,CACL,EAAA,CAAA;GAEmB;EACH,CAAA,CAAA,EAAA,CAAA;CACJ,CAAA;AAE1B"}
@@ -0,0 +1,128 @@
1
+ import { cn } from "./index14.mjs";
2
+ import { typography } from "./index36.mjs";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import "react";
5
+ import { X } from "lucide-react";
6
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
7
+ //#region src/ui-library/gallery/modal/Modal.tsx
8
+ /**
9
+ * Modal — Gallery-native dialog component.
10
+ *
11
+ * Behavior is provided by @radix-ui/react-dialog, which handles:
12
+ * - Focus trap (Tab / Shift+Tab contained within panel)
13
+ * - Focus restoration on close (correct on every open/close cycle)
14
+ * - Initial focus (first focusable element on open)
15
+ * - Escape key closes the modal
16
+ * - Outside-click (pointer-down) closes the modal
17
+ * - Body scroll lock while open
18
+ * - Portal rendering to document.body
19
+ * - role="dialog" + aria-modal + aria-labelledby + aria-describedby
20
+ *
21
+ * DIWA continues to own:
22
+ * - Public API: isOpen, onClose, title, icon, subtitle, headerMeta,
23
+ * footer, onSubmit, submitLabel, submittingLabel, submitting,
24
+ * danger, error, maxWidth, closeLabel, children
25
+ * - Backdrop visual treatment
26
+ * - Panel visual treatment (border, radius, shadow, max-height, max-width)
27
+ * - Header layout: icon badge, title, subtitle, headerMeta
28
+ * - Form wrapper (onSubmit branch)
29
+ * - Default footer buttons (Cancel + Submit)
30
+ * - Error display
31
+ * - Close button styling
32
+ * - Danger tone (rose badge, rose title, rose submit button)
33
+ */
34
+ function Modal({ isOpen, onClose, title, children, icon, subtitle, headerMeta, footer, onSubmit, submitLabel = "Submit", submittingLabel = "Saving...", submitting = false, danger = false, error, maxWidth = "max-w-xl", closeLabel = "Close" }) {
35
+ const open = isOpen !== false;
36
+ const defaultFooter = onSubmit ? /* @__PURE__ */ jsxs("div", {
37
+ className: "flex items-center justify-end gap-3",
38
+ children: [/* @__PURE__ */ jsx("button", {
39
+ type: "button",
40
+ onClick: onClose,
41
+ className: "px-4 py-2 text-sm font-bold text-slate-600 border border-slate-200 rounded-md hover:bg-slate-50 hover:border-slate-300 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
42
+ children: "Cancel"
43
+ }), /* @__PURE__ */ jsx("button", {
44
+ type: "submit",
45
+ disabled: submitting,
46
+ className: cn("px-4 py-2 text-sm font-bold text-white rounded-md transition-all focus-visible:outline-none focus-visible:ring-2 disabled:opacity-60 disabled:cursor-not-allowed", danger ? "bg-rose-600 hover:bg-rose-700 focus-visible:ring-rose-300" : "bg-brand-blue hover:bg-brand-navy focus-visible:ring-brand-blue/30"),
47
+ children: submitting ? submittingLabel : submitLabel
48
+ })]
49
+ }) : /* @__PURE__ */ jsx("div", {
50
+ className: "flex items-center justify-end",
51
+ children: /* @__PURE__ */ jsx("button", {
52
+ type: "button",
53
+ onClick: onClose,
54
+ className: "px-4 py-2 text-sm font-bold text-slate-600 border border-slate-200 rounded-md hover:bg-slate-50 hover:border-slate-300 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
55
+ children: "Cancel"
56
+ })
57
+ });
58
+ const resolvedFooter = footer ?? defaultFooter;
59
+ const iconBadgeClass = danger ? "flex h-8 w-8 items-center justify-center rounded-lg bg-rose-50 text-rose-600 shrink-0" : "flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-brand-blue shrink-0";
60
+ const bodyContent = /* @__PURE__ */ jsxs(Fragment, { children: [error && /* @__PURE__ */ jsx("div", {
61
+ className: "mb-4 rounded-lg border border-rose-100 bg-rose-50 px-4 py-3 text-sm font-medium text-rose-700",
62
+ children: error
63
+ }), children] });
64
+ return /* @__PURE__ */ jsx(DialogPrimitive.Root, {
65
+ open,
66
+ onOpenChange: (isOpen) => {
67
+ if (!isOpen) onClose();
68
+ },
69
+ children: /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [/* @__PURE__ */ jsx(DialogPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-brand-navy/50 backdrop-blur-sm" }), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
70
+ className: cn("fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2", "flex w-full flex-col", "rounded-lg border border-slate-200 bg-white shadow-xl", "max-h-[85vh] outline-none", "p-4", maxWidth),
71
+ style: { padding: 0 },
72
+ children: [
73
+ /* @__PURE__ */ jsx(DialogPrimitive.Close, {
74
+ asChild: true,
75
+ children: /* @__PURE__ */ jsx("button", {
76
+ type: "button",
77
+ "aria-label": closeLabel,
78
+ className: "absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
79
+ children: /* @__PURE__ */ jsx(X, {
80
+ size: 16,
81
+ "aria-hidden": "true"
82
+ })
83
+ })
84
+ }),
85
+ /* @__PURE__ */ jsxs("div", {
86
+ className: "px-6 pt-6 pb-4 border-b-2 border-slate-100",
87
+ children: [/* @__PURE__ */ jsxs("div", {
88
+ className: "flex items-center gap-2.5",
89
+ children: [icon && /* @__PURE__ */ jsx("div", {
90
+ className: iconBadgeClass,
91
+ children: icon
92
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DialogPrimitive.Title, {
93
+ className: cn(typography.titleOverlay, danger ? "text-rose-700" : "text-slate-900"),
94
+ children: title
95
+ }), subtitle && /* @__PURE__ */ jsx(DialogPrimitive.Description, {
96
+ className: "text-xs text-slate-500 mt-0.5",
97
+ children: subtitle
98
+ })] })]
99
+ }), headerMeta && /* @__PURE__ */ jsx("div", {
100
+ className: "mt-2",
101
+ children: headerMeta
102
+ })]
103
+ }),
104
+ onSubmit ? /* @__PURE__ */ jsxs("form", {
105
+ onSubmit,
106
+ className: "flex flex-col min-h-0 flex-1",
107
+ children: [/* @__PURE__ */ jsx("div", {
108
+ className: "flex-1 overflow-y-auto px-6 py-5",
109
+ children: bodyContent
110
+ }), /* @__PURE__ */ jsx("div", {
111
+ className: "px-6 py-4 border-t border-slate-100",
112
+ children: resolvedFooter
113
+ })]
114
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
115
+ className: "flex-1 overflow-y-auto px-6 py-5",
116
+ children: bodyContent
117
+ }), /* @__PURE__ */ jsx("div", {
118
+ className: "px-6 py-4 border-t border-slate-100",
119
+ children: resolvedFooter
120
+ })] })
121
+ ]
122
+ })] })
123
+ });
124
+ }
125
+ //#endregion
126
+ export { Modal as default };
127
+
128
+ //# sourceMappingURL=index37.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index37.mjs","names":[],"sources":["../src/ui-library/gallery/modal/Modal.tsx"],"sourcesContent":["/**\n * Modal — Gallery-native dialog component.\n *\n * Behavior is provided by @radix-ui/react-dialog, which handles:\n * - Focus trap (Tab / Shift+Tab contained within panel)\n * - Focus restoration on close (correct on every open/close cycle)\n * - Initial focus (first focusable element on open)\n * - Escape key closes the modal\n * - Outside-click (pointer-down) closes the modal\n * - Body scroll lock while open\n * - Portal rendering to document.body\n * - role=\"dialog\" + aria-modal + aria-labelledby + aria-describedby\n *\n * DIWA continues to own:\n * - Public API: isOpen, onClose, title, icon, subtitle, headerMeta,\n * footer, onSubmit, submitLabel, submittingLabel, submitting,\n * danger, error, maxWidth, closeLabel, children\n * - Backdrop visual treatment\n * - Panel visual treatment (border, radius, shadow, max-height, max-width)\n * - Header layout: icon badge, title, subtitle, headerMeta\n * - Form wrapper (onSubmit branch)\n * - Default footer buttons (Cancel + Submit)\n * - Error display\n * - Close button styling\n * - Danger tone (rose badge, rose title, rose submit button)\n */\n\nimport * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { type ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { cn } from '../../../lib/utils';\nimport { typography } from '../../tokens/typography';\n\nexport interface ModalProps {\n isOpen?: boolean;\n onClose: () => void;\n title: ReactNode;\n children: ReactNode;\n icon?: ReactNode;\n subtitle?: ReactNode;\n headerMeta?: ReactNode;\n footer?: ReactNode;\n onSubmit?: (e: React.FormEvent<HTMLFormElement>) => void;\n submitLabel?: string;\n submittingLabel?: string;\n submitting?: boolean;\n danger?: boolean;\n error?: string;\n maxWidth?: string;\n closeLabel?: string;\n}\n\nexport default function Modal({\n isOpen,\n onClose,\n title,\n children,\n icon,\n subtitle,\n headerMeta,\n footer,\n onSubmit,\n submitLabel = 'Submit',\n submittingLabel = 'Saving...',\n submitting = false,\n danger = false,\n error,\n maxWidth = 'max-w-xl',\n closeLabel = 'Close',\n}: ModalProps) {\n // Radix open state: undefined isOpen means \"always mounted\" (the consumer\n // controls mount/unmount externally). false means explicitly closed.\n const open = isOpen !== false;\n\n // Default footer — DIWA-owned form actions, no Radix involvement\n const defaultFooter = onSubmit ? (\n <div className=\"flex items-center justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-bold text-slate-600 border border-slate-200 rounded-md hover:bg-slate-50 hover:border-slate-300 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={submitting}\n className={cn(\n 'px-4 py-2 text-sm font-bold text-white rounded-md transition-all focus-visible:outline-none focus-visible:ring-2 disabled:opacity-60 disabled:cursor-not-allowed',\n danger\n ? 'bg-rose-600 hover:bg-rose-700 focus-visible:ring-rose-300'\n : 'bg-brand-blue hover:bg-brand-navy focus-visible:ring-brand-blue/30',\n )}\n >\n {submitting ? submittingLabel : submitLabel}\n </button>\n </div>\n ) : (\n <div className=\"flex items-center justify-end\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-bold text-slate-600 border border-slate-200 rounded-md hover:bg-slate-50 hover:border-slate-300 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n Cancel\n </button>\n </div>\n );\n\n const resolvedFooter = footer ?? defaultFooter;\n\n const iconBadgeClass = danger\n ? 'flex h-8 w-8 items-center justify-center rounded-lg bg-rose-50 text-rose-600 shrink-0'\n : 'flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 text-brand-blue shrink-0';\n\n const bodyContent = (\n <>\n {error && (\n <div className=\"mb-4 rounded-lg border border-rose-100 bg-rose-50 px-4 py-3 text-sm font-medium text-rose-700\">\n {error}\n </div>\n )}\n {children}\n </>\n );\n\n return (\n <DialogPrimitive.Root\n open={open}\n // Radix calls onOpenChange(false) on Escape or outside click.\n // Route to DIWA's onClose — the existing behavior is always-closeable.\n onOpenChange={(isOpen) => { if (!isOpen) onClose(); }}\n >\n <DialogPrimitive.Portal>\n {/* Backdrop — DIWA visual treatment */}\n <DialogPrimitive.Overlay\n className=\"fixed inset-0 z-50 bg-brand-navy/50 backdrop-blur-sm\"\n />\n\n {/* Panel — DIWA visual treatment */}\n <DialogPrimitive.Content\n className={cn(\n 'fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2',\n 'flex w-full flex-col',\n 'rounded-lg border border-slate-200 bg-white shadow-xl',\n 'max-h-[85vh] outline-none',\n 'p-4',\n maxWidth,\n )}\n style={{ padding: 0 }}\n // Modal is always closeable — no preventClose equivalent in this API.\n // Radix default (close on Escape + outside click) matches existing behavior.\n >\n {/* Close button — Radix Dialog.Close triggers onOpenChange(false) */}\n <DialogPrimitive.Close asChild>\n <button\n type=\"button\"\n aria-label={closeLabel}\n className=\"absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n <X size={16} aria-hidden=\"true\" />\n </button>\n </DialogPrimitive.Close>\n\n {/* Header — DIWA-owned layout */}\n <div className=\"px-6 pt-6 pb-4 border-b-2 border-slate-100\">\n <div className=\"flex items-center gap-2.5\">\n {icon && (\n <div className={iconBadgeClass}>\n {icon}\n </div>\n )}\n <div>\n {/*\n DialogPrimitive.Title provides the aria-labelledby relationship\n automatically. DIWA visual classes are applied via className.\n title is ReactNode — can be string or JSX (e.g. with a badge).\n */}\n <DialogPrimitive.Title\n className={cn(\n typography.titleOverlay,\n danger ? 'text-rose-700' : 'text-slate-900',\n )}\n >\n {title}\n </DialogPrimitive.Title>\n {subtitle && (\n // DialogPrimitive.Description provides aria-describedby when subtitle is present.\n <DialogPrimitive.Description className=\"text-xs text-slate-500 mt-0.5\">\n {subtitle}\n </DialogPrimitive.Description>\n )}\n </div>\n </div>\n {headerMeta && (\n <div className=\"mt-2\">{headerMeta}</div>\n )}\n </div>\n\n {/* Body + footer — form branch vs non-form branch, DIWA-owned */}\n {onSubmit ? (\n <form onSubmit={onSubmit} className=\"flex flex-col min-h-0 flex-1\">\n <div className=\"flex-1 overflow-y-auto px-6 py-5\">\n {bodyContent}\n </div>\n <div className=\"px-6 py-4 border-t border-slate-100\">\n {resolvedFooter}\n </div>\n </form>\n ) : (\n <>\n <div className=\"flex-1 overflow-y-auto px-6 py-5\">\n {bodyContent}\n </div>\n <div className=\"px-6 py-4 border-t border-slate-100\">\n {resolvedFooter}\n </div>\n </>\n )}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,SAAwB,MAAM,EAC5B,QACA,SACA,OACA,UACA,MACA,UACA,YACA,QACA,UACA,cAAkB,UAClB,kBAAkB,aAClB,aAAkB,OAClB,SAAkB,OAClB,OACA,WAAkB,YAClB,aAAkB,WACL;CAGb,MAAM,OAAO,WAAW;CAGxB,MAAM,gBAAgB,WACpB,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,oBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;GACX,UAAA;EAEO,CAAA,GACR,oBAAC,UAAD;GACE,MAAK;GACL,UAAU;GACV,WAAW,GACT,oKACA,SACI,8DACA,oEACN;GAEC,UAAA,aAAa,kBAAkB;EAC1B,CAAA,CACL;CAEL,CAAA,IAAA,oBAAC,OAAD;EAAK,WAAU;EACb,UAAA,oBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;GACX,UAAA;EAEO,CAAA;CACL,CAAA;CAGP,MAAM,iBAAiB,UAAU;CAEjC,MAAM,iBAAiB,SACnB,0FACA;CAEJ,MAAM,cACJ,qBAAA,UAAA,EAAA,UAAA,CACG,SACC,oBAAC,OAAD;EAAK,WAAU;EACZ,UAAA;CACE,CAAA,GAEN,QACD,EAAA,CAAA;CAGJ,OACE,oBAAC,gBAAgB,MAAjB;EACQ;EAGN,eAAe,WAAW;GAAE,IAAI,CAAC,QAAQ,QAAQ;EAAG;EAEpD,UAAA,qBAAC,gBAAgB,QAAjB,EAAA,UAAA,CAEE,oBAAC,gBAAgB,SAAjB,EACE,WAAU,uDACX,CAAA,GAGD,qBAAC,gBAAgB,SAAjB;GACE,WAAW,GACT,iEACA,wBACA,yDACA,6BACA,OACA,QACF;GACA,OAAO,EAAE,SAAS,EAAE;GATtB,UAAA;IAcE,oBAAC,gBAAgB,OAAjB;KAAuB,SAAA;KACrB,UAAA,oBAAC,UAAD;MACE,MAAK;MACL,cAAY;MACZ,WAAU;MAEV,UAAA,oBAAC,GAAD;OAAG,MAAM;OAAI,eAAY;MAAQ,CAAA;KAC3B,CAAA;IACa,CAAA;IAGvB,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,qBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACG,QACC,oBAAC,OAAD;OAAK,WAAW;OACb,UAAA;MACE,CAAA,GAEP,qBAAC,OAAD,EAAA,UAAA,CAME,oBAAC,gBAAgB,OAAjB;OACE,WAAW,GACT,WAAW,cACX,SAAS,kBAAkB,gBAC7B;OAEC,UAAA;MACoB,CAAA,GACtB,YAEC,oBAAC,gBAAgB,aAAjB;OAA6B,WAAU;OACpC,UAAA;MAC0B,CAAA,CAE5B,EAAA,CAAA,CACF;KACJ,CAAA,GAAA,cACC,oBAAC,OAAD;MAAK,WAAU;MAAQ,UAAA;KAAgB,CAAA,CAEtC;;IAGJ,WACC,qBAAC,QAAD;KAAgB;KAAU,WAAU;KAApC,UAAA,CACE,oBAAC,OAAD;MAAK,WAAU;MACZ,UAAA;KACE,CAAA,GACL,oBAAC,OAAD;MAAK,WAAU;MACZ,UAAA;KACE,CAAA,CACD;IAEN,CAAA,IAAA,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;KAAK,WAAU;KACZ,UAAA;IACE,CAAA,GACL,oBAAC,OAAD;KAAK,WAAU;KACZ,UAAA;IACE,CAAA,CACL,EAAA,CAAA;GAEmB;EACH,CAAA,CAAA,EAAA,CAAA;CACJ,CAAA;AAE1B"}
@@ -0,0 +1,106 @@
1
+ const require_runtime = require("./index2.cjs");
2
+ const require_utils = require("./index14.cjs");
3
+ const require_typography = require("./index36.cjs");
4
+ let react_jsx_runtime = require("react/jsx-runtime");
5
+ require("react");
6
+ let lucide_react = require("lucide-react");
7
+ let _radix_ui_react_dialog = require("@radix-ui/react-dialog");
8
+ _radix_ui_react_dialog = require_runtime.__toESM(_radix_ui_react_dialog, 1);
9
+ //#region src/ui-library/gallery/dialog/Dialog.tsx
10
+ /**
11
+ * Dialog — Design System Component
12
+ *
13
+ * A focus-blocking modal dialog.
14
+ *
15
+ * Behavior is provided by @radix-ui/react-dialog, which handles:
16
+ * - Focus trap (Tab / Shift+Tab contained within dialog)
17
+ * - Focus restoration (returns to trigger on close)
18
+ * - Escape key dismissal (unless preventClose)
19
+ * - Backdrop interaction dismissal (unless preventClose)
20
+ * - Body scroll lock while open
21
+ * - Portal rendering to document.body
22
+ * - role="dialog" + aria-modal + aria-labelledby + aria-describedby
23
+ * - Modal semantics
24
+ *
25
+ * DIWA continues to own:
26
+ * - Public API (open, onClose, title, description, size, preventClose, children)
27
+ * - Backdrop visual treatment
28
+ * - Panel visual treatment (bg, border, radius, shadow, sizing)
29
+ * - Header layout and typography
30
+ * - Close button styling
31
+ * - DialogBody and DialogFooter layout
32
+ *
33
+ * Composition:
34
+ * <Dialog open={open} onClose={close} title="…">
35
+ * <DialogBody>…</DialogBody>
36
+ * <DialogFooter>…</DialogFooter>
37
+ * </Dialog>
38
+ */
39
+ var SIZE_CLASS = {
40
+ sm: "max-w-sm",
41
+ md: "max-w-lg",
42
+ lg: "max-w-2xl",
43
+ xl: "max-w-4xl"
44
+ };
45
+ function DialogBody({ children, className = "" }) {
46
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47
+ className: `min-h-0 overflow-y-auto overscroll-contain px-6 py-4 ${className}`,
48
+ children
49
+ });
50
+ }
51
+ function DialogFooter({ children, className = "" }) {
52
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
53
+ className: `flex items-center justify-end gap-3 border-t border-slate-100 px-6 py-4 ${className}`,
54
+ children
55
+ });
56
+ }
57
+ function Dialog({ open, onClose, title, description, preventClose = false, size = "md", children }) {
58
+ const hasTitle = Boolean(title);
59
+ const hasDesc = Boolean(description);
60
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Root, {
61
+ open,
62
+ onOpenChange: (isOpen) => {
63
+ if (!isOpen && !preventClose) onClose();
64
+ },
65
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dialog.Portal, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Overlay, { className: "fixed inset-0 z-50 bg-brand-navy/50 backdrop-blur-sm" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_dialog.Content, {
66
+ className: require_utils.cn("fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2", "flex w-full flex-col overflow-hidden", "rounded-lg bg-white shadow-xl", "max-h-[90vh] outline-none", "p-4", SIZE_CLASS[size]),
67
+ onEscapeKeyDown: preventClose ? (e) => e.preventDefault() : void 0,
68
+ onInteractOutside: preventClose ? (e) => e.preventDefault() : void 0,
69
+ style: { padding: 0 },
70
+ children: [
71
+ hasTitle ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
72
+ className: "shrink-0 border-b-2 border-slate-100 px-6 py-5 pr-14",
73
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Title, {
74
+ className: require_utils.cn(require_typography.typography.titleOverlay, "text-slate-900"),
75
+ children: title
76
+ }), hasDesc && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Description, {
77
+ className: "mt-0.5 text-sm font-medium text-slate-500",
78
+ children: description
79
+ })]
80
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Title, {
81
+ className: "sr-only",
82
+ children: "Dialog"
83
+ }),
84
+ !preventClose && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_dialog.Close, {
85
+ asChild: true,
86
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
87
+ type: "button",
88
+ "aria-label": "Close dialog",
89
+ className: "absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 transition hover:bg-slate-100 hover:text-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
90
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
91
+ size: 16,
92
+ "aria-hidden": "true"
93
+ })
94
+ })
95
+ }),
96
+ children
97
+ ]
98
+ })] })
99
+ });
100
+ }
101
+ //#endregion
102
+ exports.Dialog = Dialog;
103
+ exports.DialogBody = DialogBody;
104
+ exports.DialogFooter = DialogFooter;
105
+
106
+ //# sourceMappingURL=index38.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index38.cjs","names":[],"sources":["../src/ui-library/gallery/dialog/Dialog.tsx"],"sourcesContent":["/**\n * Dialog — Design System Component\n *\n * A focus-blocking modal dialog.\n *\n * Behavior is provided by @radix-ui/react-dialog, which handles:\n * - Focus trap (Tab / Shift+Tab contained within dialog)\n * - Focus restoration (returns to trigger on close)\n * - Escape key dismissal (unless preventClose)\n * - Backdrop interaction dismissal (unless preventClose)\n * - Body scroll lock while open\n * - Portal rendering to document.body\n * - role=\"dialog\" + aria-modal + aria-labelledby + aria-describedby\n * - Modal semantics\n *\n * DIWA continues to own:\n * - Public API (open, onClose, title, description, size, preventClose, children)\n * - Backdrop visual treatment\n * - Panel visual treatment (bg, border, radius, shadow, sizing)\n * - Header layout and typography\n * - Close button styling\n * - DialogBody and DialogFooter layout\n *\n * Composition:\n * <Dialog open={open} onClose={close} title=\"…\">\n * <DialogBody>…</DialogBody>\n * <DialogFooter>…</DialogFooter>\n * </Dialog>\n */\n\nimport * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { type ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { cn } from '../../../lib/utils';\nimport { typography } from '../../tokens/typography';\n\nexport type DialogSize = 'sm' | 'md' | 'lg' | 'xl';\n\nexport interface DialogProps {\n open: boolean;\n onClose: () => void;\n title?: string;\n description?: string;\n /** When true, Escape and backdrop click will not close the dialog */\n preventClose?: boolean;\n size?: DialogSize;\n children?: ReactNode;\n}\n\nexport interface DialogBodyProps {\n children: ReactNode;\n className?: string;\n}\n\nexport interface DialogFooterProps {\n children: ReactNode;\n className?: string;\n}\n\n// ── Style maps ────────────────────────────────────────────────────────────────\n\nconst SIZE_CLASS: Record<DialogSize, string> = {\n sm: 'max-w-sm',\n md: 'max-w-lg',\n lg: 'max-w-2xl',\n xl: 'max-w-4xl',\n};\n\n// ── Sub-components — pure DIWA layout, no Radix involvement ──────────────────\n\nexport function DialogBody({ children, className = '' }: DialogBodyProps) {\n return (\n <div className={`min-h-0 overflow-y-auto overscroll-contain px-6 py-4 ${className}`}>\n {children}\n </div>\n );\n}\n\nexport function DialogFooter({ children, className = '' }: DialogFooterProps) {\n return (\n <div className={`flex items-center justify-end gap-3 border-t border-slate-100 px-6 py-4 ${className}`}>\n {children}\n </div>\n );\n}\n\n// ── Main component ────────────────────────────────────────────────────────────\n\nexport function Dialog({\n open,\n onClose,\n title,\n description,\n preventClose = false,\n size = 'md',\n children,\n}: DialogProps) {\n const hasTitle = Boolean(title);\n const hasDesc = Boolean(description);\n\n return (\n // Radix Dialog.Root drives open/close state.\n // onOpenChange is called when Radix wants to close (Escape, outside click).\n // We route it through DIWA's onClose, respecting preventClose.\n <DialogPrimitive.Root\n open={open}\n onOpenChange={(isOpen) => {\n // Radix calls onOpenChange(false) when closing.\n // Ignore opening attempts from Radix (we control open externally).\n if (!isOpen && !preventClose) onClose();\n }}\n >\n <DialogPrimitive.Portal>\n {/* Backdrop — DIWA visual treatment */}\n <DialogPrimitive.Overlay\n className=\"fixed inset-0 z-50 bg-brand-navy/50 backdrop-blur-sm\"\n />\n\n {/* Panel — DIWA visual treatment */}\n <DialogPrimitive.Content\n className={cn(\n 'fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2',\n 'flex w-full flex-col overflow-hidden',\n 'rounded-lg bg-white shadow-xl',\n 'max-h-[90vh] outline-none',\n 'p-4',\n SIZE_CLASS[size],\n )}\n // Suppress Radix's own Escape/outside-click when preventClose is set.\n // Radix calls these before triggering onOpenChange, so preventing here\n // ensures neither the overlay nor keyboard can dismiss the dialog.\n onEscapeKeyDown={preventClose ? (e) => e.preventDefault() : undefined}\n onInteractOutside={preventClose ? (e) => e.preventDefault() : undefined}\n // Remove padding added by className — override with our layout below\n style={{ padding: 0 }}\n >\n {/* Radix requires Dialog.Title for accessible naming.\n Render it visually when title prop is provided.\n When absent, render sr-only so Radix's ARIA wiring works\n without adding visible text the consumer didn't request. */}\n {hasTitle ? (\n // Header — visible when title or description are provided\n <div className=\"shrink-0 border-b-2 border-slate-100 px-6 py-5 pr-14\">\n <DialogPrimitive.Title className={cn(typography.titleOverlay, 'text-slate-900')}>\n {title}\n </DialogPrimitive.Title>\n {hasDesc && (\n <DialogPrimitive.Description className=\"mt-0.5 text-sm font-medium text-slate-500\">\n {description}\n </DialogPrimitive.Description>\n )}\n </div>\n ) : (\n // No visible title — render sr-only for Radix ARIA requirement\n <DialogPrimitive.Title className=\"sr-only\">\n Dialog\n </DialogPrimitive.Title>\n )}\n\n {/* Close button — uses Radix Dialog.Close so it participates\n in the Radix open/close flow. asChild avoids an extra DOM node. */}\n {!preventClose && (\n <DialogPrimitive.Close asChild>\n <button\n type=\"button\"\n aria-label=\"Close dialog\"\n className=\"absolute right-4 top-4 rounded-lg p-1.5 text-slate-400 transition hover:bg-slate-100 hover:text-slate-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n <X size={16} aria-hidden=\"true\" />\n </button>\n </DialogPrimitive.Close>\n )}\n\n {/* Content — children contain DialogBody + DialogFooter */}\n {children}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,IAAM,aAAyC;CAC7C,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAIA,SAAgB,WAAW,EAAE,UAAU,YAAY,MAAuB;CACxE,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,WAAW,wDAAwD;EACrE;CACE,CAAA;AAET;AAEA,SAAgB,aAAa,EAAE,UAAU,YAAY,MAAyB;CAC5E,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,WAAW,2EAA2E;EACxF;CACE,CAAA;AAET;AAIA,SAAgB,OAAO,EACrB,MACA,SACA,OACA,aACA,eAAe,OACf,OAAO,MACP,YACc;CACd,MAAM,WAAW,QAAQ,KAAK;CAC9B,MAAM,UAAW,QAAQ,WAAW;CAEpC,OAIE,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,MAAjB;EACQ;EACN,eAAe,WAAW;GAGxB,IAAI,CAAC,UAAU,CAAC,cAAc,QAAQ;EACxC;EAEA,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,uBAAgB,QAAjB,EAAA,UAAA,CAEE,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,SAAjB,EACE,WAAU,uDACX,CAAA,GAGD,iBAAA,GAAA,kBAAA,KAAA,CAAC,uBAAgB,SAAjB;GACE,WAAW,cAAA,GACT,iEACA,wCACA,iCACA,6BACA,OACA,WAAW,KACb;GAIA,iBAAiB,gBAAgB,MAAM,EAAE,eAAe,IAAI,KAAA;GAC5D,mBAAmB,gBAAgB,MAAM,EAAE,eAAe,IAAI,KAAA;GAE9D,OAAO,EAAE,SAAS,EAAE;GAftB,UAAA;IAqBG,WAEC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,OAAjB;MAAuB,WAAW,cAAA,GAAG,mBAAA,WAAW,cAAc,gBAAgB;MAC3E,UAAA;KACoB,CAAA,GACtB,WACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,aAAjB;MAA6B,WAAU;MACpC,UAAA;KAC0B,CAAA,CAE5B;IAGL,CAAA,IAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,OAAjB;KAAuB,WAAU;KAAU,UAAA;IAEpB,CAAA;IAKxB,CAAC,gBACA,iBAAA,GAAA,kBAAA,IAAA,CAAC,uBAAgB,OAAjB;KAAuB,SAAA;KACrB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MACE,MAAK;MACL,cAAW;MACX,WAAU;MAEV,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,GAAD;OAAG,MAAM;OAAI,eAAY;MAAQ,CAAA;KAC3B,CAAA;IACa,CAAA;IAIxB;GACsB;EACH,CAAA,CAAA,EAAA,CAAA;CACJ,CAAA;AAE1B"}