@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.
- package/README.md +75 -0
- package/lib/assets/ui.css +154 -0
- package/lib/index.cjs +183 -0
- package/lib/index.mjs +65 -0
- package/lib/index10.cjs +33 -0
- package/lib/index10.cjs.map +1 -0
- package/lib/index10.mjs +33 -0
- package/lib/index10.mjs.map +1 -0
- package/lib/index11.cjs +16 -0
- package/lib/index11.cjs.map +1 -0
- package/lib/index11.mjs +16 -0
- package/lib/index11.mjs.map +1 -0
- package/lib/index12.cjs +16 -0
- package/lib/index12.cjs.map +1 -0
- package/lib/index12.mjs +15 -0
- package/lib/index12.mjs.map +1 -0
- package/lib/index13.cjs +57 -0
- package/lib/index13.cjs.map +1 -0
- package/lib/index13.mjs +57 -0
- package/lib/index13.mjs.map +1 -0
- package/lib/index14.cjs +10 -0
- package/lib/index14.cjs.map +1 -0
- package/lib/index14.mjs +10 -0
- package/lib/index14.mjs.map +1 -0
- package/lib/index15.cjs +30 -0
- package/lib/index15.cjs.map +1 -0
- package/lib/index15.mjs +30 -0
- package/lib/index15.mjs.map +1 -0
- package/lib/index16.cjs +28 -0
- package/lib/index16.cjs.map +1 -0
- package/lib/index16.mjs +26 -0
- package/lib/index16.mjs.map +1 -0
- package/lib/index17.cjs +43 -0
- package/lib/index17.cjs.map +1 -0
- package/lib/index17.mjs +43 -0
- package/lib/index17.mjs.map +1 -0
- package/lib/index18.cjs +59 -0
- package/lib/index18.cjs.map +1 -0
- package/lib/index18.mjs +59 -0
- package/lib/index18.mjs.map +1 -0
- package/lib/index19.cjs +67 -0
- package/lib/index19.cjs.map +1 -0
- package/lib/index19.mjs +67 -0
- package/lib/index19.mjs.map +1 -0
- package/lib/index2.cjs +33 -0
- package/lib/index2.mjs +13 -0
- package/lib/index20.cjs +79 -0
- package/lib/index20.cjs.map +1 -0
- package/lib/index20.mjs +79 -0
- package/lib/index20.mjs.map +1 -0
- package/lib/index21.cjs +105 -0
- package/lib/index21.cjs.map +1 -0
- package/lib/index21.mjs +104 -0
- package/lib/index21.mjs.map +1 -0
- package/lib/index22.cjs +50 -0
- package/lib/index22.cjs.map +1 -0
- package/lib/index22.mjs +50 -0
- package/lib/index22.mjs.map +1 -0
- package/lib/index23.cjs +143 -0
- package/lib/index23.cjs.map +1 -0
- package/lib/index23.mjs +143 -0
- package/lib/index23.mjs.map +1 -0
- package/lib/index25.cjs +177 -0
- package/lib/index25.cjs.map +1 -0
- package/lib/index25.mjs +175 -0
- package/lib/index25.mjs.map +1 -0
- package/lib/index26.cjs +108 -0
- package/lib/index26.cjs.map +1 -0
- package/lib/index26.mjs +108 -0
- package/lib/index26.mjs.map +1 -0
- package/lib/index27.cjs +120 -0
- package/lib/index27.cjs.map +1 -0
- package/lib/index27.mjs +120 -0
- package/lib/index27.mjs.map +1 -0
- package/lib/index28.cjs +54 -0
- package/lib/index28.cjs.map +1 -0
- package/lib/index28.mjs +54 -0
- package/lib/index28.mjs.map +1 -0
- package/lib/index29.cjs +100 -0
- package/lib/index29.cjs.map +1 -0
- package/lib/index29.mjs +100 -0
- package/lib/index29.mjs.map +1 -0
- package/lib/index3.cjs +38 -0
- package/lib/index3.cjs.map +1 -0
- package/lib/index3.mjs +38 -0
- package/lib/index3.mjs.map +1 -0
- package/lib/index30.cjs +27 -0
- package/lib/index30.cjs.map +1 -0
- package/lib/index30.mjs +27 -0
- package/lib/index30.mjs.map +1 -0
- package/lib/index31.cjs +72 -0
- package/lib/index31.cjs.map +1 -0
- package/lib/index31.mjs +72 -0
- package/lib/index31.mjs.map +1 -0
- package/lib/index32.cjs +19 -0
- package/lib/index32.cjs.map +1 -0
- package/lib/index32.mjs +19 -0
- package/lib/index32.mjs.map +1 -0
- package/lib/index33.cjs +25 -0
- package/lib/index33.cjs.map +1 -0
- package/lib/index33.mjs +25 -0
- package/lib/index33.mjs.map +1 -0
- package/lib/index34.cjs +55 -0
- package/lib/index34.cjs.map +1 -0
- package/lib/index34.mjs +51 -0
- package/lib/index34.mjs.map +1 -0
- package/lib/index35.cjs +25 -0
- package/lib/index35.cjs.map +1 -0
- package/lib/index35.mjs +25 -0
- package/lib/index35.mjs.map +1 -0
- package/lib/index36.cjs +54 -0
- package/lib/index36.cjs.map +1 -0
- package/lib/index36.mjs +49 -0
- package/lib/index36.mjs.map +1 -0
- package/lib/index37.cjs +130 -0
- package/lib/index37.cjs.map +1 -0
- package/lib/index37.mjs +128 -0
- package/lib/index37.mjs.map +1 -0
- package/lib/index38.cjs +106 -0
- package/lib/index38.cjs.map +1 -0
- package/lib/index38.mjs +102 -0
- package/lib/index38.mjs.map +1 -0
- package/lib/index39.cjs +137 -0
- package/lib/index39.cjs.map +1 -0
- package/lib/index39.mjs +137 -0
- package/lib/index39.mjs.map +1 -0
- package/lib/index4.cjs +24 -0
- package/lib/index4.cjs.map +1 -0
- package/lib/index4.mjs +24 -0
- package/lib/index4.mjs.map +1 -0
- package/lib/index40.cjs +130 -0
- package/lib/index40.cjs.map +1 -0
- package/lib/index40.mjs +126 -0
- package/lib/index40.mjs.map +1 -0
- package/lib/index41.cjs +60 -0
- package/lib/index41.cjs.map +1 -0
- package/lib/index41.mjs +58 -0
- package/lib/index41.mjs.map +1 -0
- package/lib/index42.cjs +105 -0
- package/lib/index42.cjs.map +1 -0
- package/lib/index42.mjs +105 -0
- package/lib/index42.mjs.map +1 -0
- package/lib/index43.cjs +47 -0
- package/lib/index43.cjs.map +1 -0
- package/lib/index43.mjs +42 -0
- package/lib/index43.mjs.map +1 -0
- package/lib/index44.cjs +40 -0
- package/lib/index44.cjs.map +1 -0
- package/lib/index44.mjs +35 -0
- package/lib/index44.mjs.map +1 -0
- package/lib/index45.cjs +91 -0
- package/lib/index45.cjs.map +1 -0
- package/lib/index45.mjs +91 -0
- package/lib/index45.mjs.map +1 -0
- package/lib/index46.cjs +180 -0
- package/lib/index46.cjs.map +1 -0
- package/lib/index46.mjs +180 -0
- package/lib/index46.mjs.map +1 -0
- package/lib/index47.cjs +61 -0
- package/lib/index47.cjs.map +1 -0
- package/lib/index47.mjs +61 -0
- package/lib/index47.mjs.map +1 -0
- package/lib/index48.cjs +165 -0
- package/lib/index48.cjs.map +1 -0
- package/lib/index48.mjs +161 -0
- package/lib/index48.mjs.map +1 -0
- package/lib/index49.cjs +83 -0
- package/lib/index49.cjs.map +1 -0
- package/lib/index49.mjs +83 -0
- package/lib/index49.mjs.map +1 -0
- package/lib/index5.cjs +39 -0
- package/lib/index5.cjs.map +1 -0
- package/lib/index5.mjs +39 -0
- package/lib/index5.mjs.map +1 -0
- package/lib/index50.cjs +100 -0
- package/lib/index50.cjs.map +1 -0
- package/lib/index50.mjs +100 -0
- package/lib/index50.mjs.map +1 -0
- package/lib/index51.cjs +116 -0
- package/lib/index51.cjs.map +1 -0
- package/lib/index51.mjs +113 -0
- package/lib/index51.mjs.map +1 -0
- package/lib/index52.cjs +93 -0
- package/lib/index52.cjs.map +1 -0
- package/lib/index52.mjs +93 -0
- package/lib/index52.mjs.map +1 -0
- package/lib/index53.cjs +120 -0
- package/lib/index53.cjs.map +1 -0
- package/lib/index53.mjs +120 -0
- package/lib/index53.mjs.map +1 -0
- package/lib/index54.cjs +53 -0
- package/lib/index54.cjs.map +1 -0
- package/lib/index54.mjs +50 -0
- package/lib/index54.mjs.map +1 -0
- package/lib/index55.cjs +130 -0
- package/lib/index55.cjs.map +1 -0
- package/lib/index55.mjs +127 -0
- package/lib/index55.mjs.map +1 -0
- package/lib/index56.cjs +36 -0
- package/lib/index56.cjs.map +1 -0
- package/lib/index56.mjs +35 -0
- package/lib/index56.mjs.map +1 -0
- package/lib/index57.cjs +47 -0
- package/lib/index57.cjs.map +1 -0
- package/lib/index57.mjs +47 -0
- package/lib/index57.mjs.map +1 -0
- package/lib/index58.cjs +97 -0
- package/lib/index58.cjs.map +1 -0
- package/lib/index58.mjs +97 -0
- package/lib/index58.mjs.map +1 -0
- package/lib/index59.cjs +120 -0
- package/lib/index59.cjs.map +1 -0
- package/lib/index59.mjs +116 -0
- package/lib/index59.mjs.map +1 -0
- package/lib/index6.cjs +29 -0
- package/lib/index6.cjs.map +1 -0
- package/lib/index6.mjs +29 -0
- package/lib/index6.mjs.map +1 -0
- package/lib/index60.cjs +34 -0
- package/lib/index60.cjs.map +1 -0
- package/lib/index60.mjs +34 -0
- package/lib/index60.mjs.map +1 -0
- package/lib/index61.cjs +62 -0
- package/lib/index61.cjs.map +1 -0
- package/lib/index61.mjs +62 -0
- package/lib/index61.mjs.map +1 -0
- package/lib/index62.cjs +213 -0
- package/lib/index62.cjs.map +1 -0
- package/lib/index62.mjs +213 -0
- package/lib/index62.mjs.map +1 -0
- package/lib/index63.cjs +42 -0
- package/lib/index63.cjs.map +1 -0
- package/lib/index63.mjs +42 -0
- package/lib/index63.mjs.map +1 -0
- package/lib/index64.cjs +86 -0
- package/lib/index64.cjs.map +1 -0
- package/lib/index64.mjs +86 -0
- package/lib/index64.mjs.map +1 -0
- package/lib/index65.cjs +156 -0
- package/lib/index65.cjs.map +1 -0
- package/lib/index65.mjs +156 -0
- package/lib/index65.mjs.map +1 -0
- package/lib/index66.cjs +107 -0
- package/lib/index66.cjs.map +1 -0
- package/lib/index66.mjs +106 -0
- package/lib/index66.mjs.map +1 -0
- package/lib/index67.cjs +52 -0
- package/lib/index67.cjs.map +1 -0
- package/lib/index67.mjs +52 -0
- package/lib/index67.mjs.map +1 -0
- package/lib/index68.cjs +125 -0
- package/lib/index68.cjs.map +1 -0
- package/lib/index68.mjs +115 -0
- package/lib/index68.mjs.map +1 -0
- package/lib/index69.cjs +34 -0
- package/lib/index69.cjs.map +1 -0
- package/lib/index69.mjs +29 -0
- package/lib/index69.mjs.map +1 -0
- package/lib/index7.cjs +17 -0
- package/lib/index7.cjs.map +1 -0
- package/lib/index7.mjs +17 -0
- package/lib/index7.mjs.map +1 -0
- package/lib/index8.cjs +86 -0
- package/lib/index8.cjs.map +1 -0
- package/lib/index8.mjs +86 -0
- package/lib/index8.mjs.map +1 -0
- package/lib/index9.cjs +29 -0
- package/lib/index9.cjs.map +1 -0
- package/lib/index9.mjs +29 -0
- package/lib/index9.mjs.map +1 -0
- package/lib/types/index.d.ts +1 -0
- package/package.json +68 -0
package/lib/index36.cjs
ADDED
|
@@ -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"}
|
package/lib/index36.mjs
ADDED
|
@@ -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"}
|
package/lib/index37.cjs
ADDED
|
@@ -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"}
|
package/lib/index37.mjs
ADDED
|
@@ -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"}
|
package/lib/index38.cjs
ADDED
|
@@ -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"}
|