@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index40.cjs","names":[],"sources":["../src/ui-library/gallery/popover/Popover.tsx"],"sourcesContent":["/**\n * Popover — Design System Component\n *\n * An anchored floating panel that opens on trigger interaction.\n *\n * Behavior is provided by @radix-ui/react-popover, which handles:\n * - Open/close state (both controlled and uncontrolled)\n * - Trigger click interaction\n * - Escape key closes the popover + focus returns to trigger\n * - Outside-click (pointer-down) closes the popover\n * - Focus moves into the content on open\n * - Portal rendering to document.body\n * - Viewport collision detection and automatic side-flip\n * - aria-expanded, aria-haspopup, aria-controls wiring\n * - role=\"dialog\" on the content panel\n *\n * DIWA continues to own:\n * - Public API (open, onOpenChange, defaultOpen, placement, title, maxWidth, className, children)\n * - Trigger visual treatment (button styling)\n * - Content panel visual treatment (bg, border, radius, shadow, padding)\n * - Placement vocabulary (DIWA names mapped internally to Radix side+align)\n * - Typography and color tokens\n * - Component anatomy and composition model\n *\n * Composition:\n * <Popover>\n * <PopoverTrigger>Open</PopoverTrigger>\n * <PopoverContent title=\"Settings\">…</PopoverContent>\n * </Popover>\n *\n * Placement: 'bottom-start' | 'bottom-end' | 'bottom' |\n * 'top-start' | 'top-end' | 'top'\n *\n * Difference from Dialog/Modal:\n * - Popover is anchored to a trigger and positioned near it.\n * - Dialog/Modal is centered in the viewport and blocks the page.\n * - Tooltip shows text-only hints on hover/focus without interaction.\n */\n\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { type ReactNode } from 'react';\nimport { cn } from '../../../lib/utils';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type PopoverPlacement =\n | 'bottom-start' | 'bottom' | 'bottom-end'\n | 'top-start' | 'top' | 'top-end';\n\n// ── Placement → Radix side + align mapping ────────────────────────────────────\n\ntype RadixSide = 'top' | 'bottom' | 'left' | 'right';\ntype RadixAlign = 'start' | 'center' | 'end';\n\nfunction toRadixPlacement(placement: PopoverPlacement): { side: RadixSide; align: RadixAlign } {\n switch (placement) {\n case 'bottom-start': return { side: 'bottom', align: 'start' };\n case 'bottom': return { side: 'bottom', align: 'center' };\n case 'bottom-end': return { side: 'bottom', align: 'end' };\n case 'top-start': return { side: 'top', align: 'start' };\n case 'top': return { side: 'top', align: 'center' };\n case 'top-end': return { side: 'top', align: 'end' };\n }\n}\n\n// ── Popover (root) ────────────────────────────────────────────────────────────\n\nexport interface PopoverProps {\n /** Controlled open state */\n open?: boolean;\n /** Controlled: called when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Uncontrolled initial open state */\n defaultOpen?: boolean;\n placement?: PopoverPlacement;\n children: ReactNode;\n}\n\nexport function Popover({\n open,\n onOpenChange,\n defaultOpen = false,\n placement = 'bottom-start',\n children,\n}: PopoverProps) {\n const { side, align } = toRadixPlacement(placement);\n\n return (\n // We pass side/align down via a context-like trick: wrap in a span-level\n // context provider so PopoverContent can pick up the resolved placement.\n // Radix manages open state internally; controlled mode uses open/onOpenChange.\n <PopoverPrimitive.Root\n open={open}\n onOpenChange={onOpenChange}\n defaultOpen={defaultOpen}\n >\n {/*\n Render children with the resolved placement accessible via React context.\n Because PopoverTrigger and PopoverContent are co-located in this file,\n we can pass side/align as props through a thin internal context.\n */}\n <PopoverPlacementContext.Provider value={{ side, align }}>\n {children}\n </PopoverPlacementContext.Provider>\n </PopoverPrimitive.Root>\n );\n}\n\n// ── Internal placement context ────────────────────────────────────────────────\n// Not exported — Radix placement details never surface to consumers.\n\nimport { createContext, useContext } from 'react';\n\ninterface PlacementCtx { side: RadixSide; align: RadixAlign }\nconst PopoverPlacementContext = createContext<PlacementCtx>({ side: 'bottom', align: 'start' });\nfunction usePlacement() { return useContext(PopoverPlacementContext); }\n\n// ── PopoverTrigger ────────────────────────────────────────────────────────────\n\nexport interface PopoverTriggerProps {\n children: ReactNode;\n}\n\nexport function PopoverTrigger({ children }: PopoverTriggerProps) {\n return (\n // asChild forwards Radix's accessibility props (aria-expanded, aria-haspopup,\n // aria-controls) onto the inner <button> rather than creating a wrapper element.\n <PopoverPrimitive.Trigger asChild>\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-2 rounded-xl border border-slate-200 bg-white px-4 py-2 text-sm font-semibold text-slate-700 shadow-sm transition hover:border-slate-300 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n {children}\n </button>\n </PopoverPrimitive.Trigger>\n );\n}\n\n// ── PopoverContent ────────────────────────────────────────────────────────────\n\nexport interface PopoverContentProps {\n /** Optional title rendered in the popover header */\n title?: string;\n children: ReactNode;\n /** Tailwind max-width class */\n maxWidth?: string;\n className?: string;\n}\n\nexport function PopoverContent({\n title,\n children,\n maxWidth = 'max-w-xs',\n className = '',\n}: PopoverContentProps) {\n const { side, align } = usePlacement();\n\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n side={side}\n align={align}\n sideOffset={8}\n // Radix will flip to the opposite side if the preferred side clips\n // the viewport (collision detection via @floating-ui/dom internally).\n // avoidCollisions is true by default.\n className={cn(\n // DIWA visual treatment — matches the pre-migration appearance exactly\n 'z-50 w-max rounded-xl border border-slate-200 bg-white shadow-lg',\n 'outline-none',\n maxWidth,\n className,\n )}\n >\n {title && (\n <div className=\"border-b border-slate-100 px-4 py-3\">\n <h3 className=\"font-heading text-sm font-bold text-slate-900\">{title}</h3>\n </div>\n )}\n <div className=\"px-4 py-3\">{children}</div>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,SAAS,iBAAiB,WAAqE;CAC7F,QAAQ,WAAR;EACE,KAAK,gBAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EAC7D,KAAK,UAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAS;EAC9D,KAAK,cAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAM;EAC3D,KAAK,aAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EAC7D,KAAK,OAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAS;EAC9D,KAAK,WAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAM;CAC7D;AACF;AAeA,SAAgB,QAAQ,EACtB,MACA,cACA,cAAc,OACd,YAAY,gBACZ,YACe;CACf,MAAM,EAAE,MAAM,UAAU,iBAAiB,SAAS;CAElD,OAIE,iBAAA,GAAA,kBAAA,IAAA,CAAC,wBAAiB,MAAlB;EACQ;EACQ;EACD;EAOb,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,wBAAwB,UAAzB;GAAkC,OAAO;IAAE;IAAM;GAAM;GACpD;EAC+B,CAAA;CACb,CAAA;AAE3B;AAQA,IAAM,2BAAA,GAA0B,MAAA,cAAA,CAA4B;CAAE,MAAM;CAAU,OAAO;AAAQ,CAAC;AAC9F,SAAS,eAAe;CAAE,QAAA,GAAO,MAAA,WAAA,CAAW,uBAAuB;AAAG;AAQtE,SAAgB,eAAe,EAAE,YAAiC;CAChE,OAGE,iBAAA,GAAA,kBAAA,IAAA,CAAC,wBAAiB,SAAlB;EAA0B,SAAA;EACxB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,MAAK;GACL,WAAU;GAET;EACK,CAAA;CACgB,CAAA;AAE9B;AAaA,SAAgB,eAAe,EAC7B,OACA,UACA,WAAW,YACX,YAAY,MACU;CACtB,MAAM,EAAE,MAAM,UAAU,aAAa;CAErC,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,wBAAiB,QAAlB,EAAA,UACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,wBAAiB,SAAlB;EACQ;EACC;EACP,YAAY;EAIZ,WAAW,cAAA,GAET,oEACA,gBACA,UACA,SACF;EAbF,UAAA,CAeG,SACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;GAAK,WAAU;GACb,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;IAAI,WAAU;IAAiD,UAAA;GAAU,CAAA;EACtE,CAAA,GAEP,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;GAAK,WAAU;GAAa;EAAc,CAAA,CAClB;CACH,CAAA,EAAA,CAAA;AAE7B"}
|
package/lib/index40.mjs
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { cn } from "./index14.mjs";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
5
|
+
//#region src/ui-library/gallery/popover/Popover.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Popover — Design System Component
|
|
8
|
+
*
|
|
9
|
+
* An anchored floating panel that opens on trigger interaction.
|
|
10
|
+
*
|
|
11
|
+
* Behavior is provided by @radix-ui/react-popover, which handles:
|
|
12
|
+
* - Open/close state (both controlled and uncontrolled)
|
|
13
|
+
* - Trigger click interaction
|
|
14
|
+
* - Escape key closes the popover + focus returns to trigger
|
|
15
|
+
* - Outside-click (pointer-down) closes the popover
|
|
16
|
+
* - Focus moves into the content on open
|
|
17
|
+
* - Portal rendering to document.body
|
|
18
|
+
* - Viewport collision detection and automatic side-flip
|
|
19
|
+
* - aria-expanded, aria-haspopup, aria-controls wiring
|
|
20
|
+
* - role="dialog" on the content panel
|
|
21
|
+
*
|
|
22
|
+
* DIWA continues to own:
|
|
23
|
+
* - Public API (open, onOpenChange, defaultOpen, placement, title, maxWidth, className, children)
|
|
24
|
+
* - Trigger visual treatment (button styling)
|
|
25
|
+
* - Content panel visual treatment (bg, border, radius, shadow, padding)
|
|
26
|
+
* - Placement vocabulary (DIWA names mapped internally to Radix side+align)
|
|
27
|
+
* - Typography and color tokens
|
|
28
|
+
* - Component anatomy and composition model
|
|
29
|
+
*
|
|
30
|
+
* Composition:
|
|
31
|
+
* <Popover>
|
|
32
|
+
* <PopoverTrigger>Open</PopoverTrigger>
|
|
33
|
+
* <PopoverContent title="Settings">…</PopoverContent>
|
|
34
|
+
* </Popover>
|
|
35
|
+
*
|
|
36
|
+
* Placement: 'bottom-start' | 'bottom-end' | 'bottom' |
|
|
37
|
+
* 'top-start' | 'top-end' | 'top'
|
|
38
|
+
*
|
|
39
|
+
* Difference from Dialog/Modal:
|
|
40
|
+
* - Popover is anchored to a trigger and positioned near it.
|
|
41
|
+
* - Dialog/Modal is centered in the viewport and blocks the page.
|
|
42
|
+
* - Tooltip shows text-only hints on hover/focus without interaction.
|
|
43
|
+
*/
|
|
44
|
+
function toRadixPlacement(placement) {
|
|
45
|
+
switch (placement) {
|
|
46
|
+
case "bottom-start": return {
|
|
47
|
+
side: "bottom",
|
|
48
|
+
align: "start"
|
|
49
|
+
};
|
|
50
|
+
case "bottom": return {
|
|
51
|
+
side: "bottom",
|
|
52
|
+
align: "center"
|
|
53
|
+
};
|
|
54
|
+
case "bottom-end": return {
|
|
55
|
+
side: "bottom",
|
|
56
|
+
align: "end"
|
|
57
|
+
};
|
|
58
|
+
case "top-start": return {
|
|
59
|
+
side: "top",
|
|
60
|
+
align: "start"
|
|
61
|
+
};
|
|
62
|
+
case "top": return {
|
|
63
|
+
side: "top",
|
|
64
|
+
align: "center"
|
|
65
|
+
};
|
|
66
|
+
case "top-end": return {
|
|
67
|
+
side: "top",
|
|
68
|
+
align: "end"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function Popover({ open, onOpenChange, defaultOpen = false, placement = "bottom-start", children }) {
|
|
73
|
+
const { side, align } = toRadixPlacement(placement);
|
|
74
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Root, {
|
|
75
|
+
open,
|
|
76
|
+
onOpenChange,
|
|
77
|
+
defaultOpen,
|
|
78
|
+
children: /* @__PURE__ */ jsx(PopoverPlacementContext.Provider, {
|
|
79
|
+
value: {
|
|
80
|
+
side,
|
|
81
|
+
align
|
|
82
|
+
},
|
|
83
|
+
children
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
var PopoverPlacementContext = createContext({
|
|
88
|
+
side: "bottom",
|
|
89
|
+
align: "start"
|
|
90
|
+
});
|
|
91
|
+
function usePlacement() {
|
|
92
|
+
return useContext(PopoverPlacementContext);
|
|
93
|
+
}
|
|
94
|
+
function PopoverTrigger({ children }) {
|
|
95
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Trigger, {
|
|
96
|
+
asChild: true,
|
|
97
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
98
|
+
type: "button",
|
|
99
|
+
className: "inline-flex items-center gap-2 rounded-xl border border-slate-200 bg-white px-4 py-2 text-sm font-semibold text-slate-700 shadow-sm transition hover:border-slate-300 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
100
|
+
children
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function PopoverContent({ title, children, maxWidth = "max-w-xs", className = "" }) {
|
|
105
|
+
const { side, align } = usePlacement();
|
|
106
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsxs(PopoverPrimitive.Content, {
|
|
107
|
+
side,
|
|
108
|
+
align,
|
|
109
|
+
sideOffset: 8,
|
|
110
|
+
className: cn("z-50 w-max rounded-xl border border-slate-200 bg-white shadow-lg", "outline-none", maxWidth, className),
|
|
111
|
+
children: [title && /* @__PURE__ */ jsx("div", {
|
|
112
|
+
className: "border-b border-slate-100 px-4 py-3",
|
|
113
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
114
|
+
className: "font-heading text-sm font-bold text-slate-900",
|
|
115
|
+
children: title
|
|
116
|
+
})
|
|
117
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
118
|
+
className: "px-4 py-3",
|
|
119
|
+
children
|
|
120
|
+
})]
|
|
121
|
+
}) });
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
export { Popover, PopoverContent, PopoverTrigger };
|
|
125
|
+
|
|
126
|
+
//# sourceMappingURL=index40.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index40.mjs","names":[],"sources":["../src/ui-library/gallery/popover/Popover.tsx"],"sourcesContent":["/**\n * Popover — Design System Component\n *\n * An anchored floating panel that opens on trigger interaction.\n *\n * Behavior is provided by @radix-ui/react-popover, which handles:\n * - Open/close state (both controlled and uncontrolled)\n * - Trigger click interaction\n * - Escape key closes the popover + focus returns to trigger\n * - Outside-click (pointer-down) closes the popover\n * - Focus moves into the content on open\n * - Portal rendering to document.body\n * - Viewport collision detection and automatic side-flip\n * - aria-expanded, aria-haspopup, aria-controls wiring\n * - role=\"dialog\" on the content panel\n *\n * DIWA continues to own:\n * - Public API (open, onOpenChange, defaultOpen, placement, title, maxWidth, className, children)\n * - Trigger visual treatment (button styling)\n * - Content panel visual treatment (bg, border, radius, shadow, padding)\n * - Placement vocabulary (DIWA names mapped internally to Radix side+align)\n * - Typography and color tokens\n * - Component anatomy and composition model\n *\n * Composition:\n * <Popover>\n * <PopoverTrigger>Open</PopoverTrigger>\n * <PopoverContent title=\"Settings\">…</PopoverContent>\n * </Popover>\n *\n * Placement: 'bottom-start' | 'bottom-end' | 'bottom' |\n * 'top-start' | 'top-end' | 'top'\n *\n * Difference from Dialog/Modal:\n * - Popover is anchored to a trigger and positioned near it.\n * - Dialog/Modal is centered in the viewport and blocks the page.\n * - Tooltip shows text-only hints on hover/focus without interaction.\n */\n\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { type ReactNode } from 'react';\nimport { cn } from '../../../lib/utils';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type PopoverPlacement =\n | 'bottom-start' | 'bottom' | 'bottom-end'\n | 'top-start' | 'top' | 'top-end';\n\n// ── Placement → Radix side + align mapping ────────────────────────────────────\n\ntype RadixSide = 'top' | 'bottom' | 'left' | 'right';\ntype RadixAlign = 'start' | 'center' | 'end';\n\nfunction toRadixPlacement(placement: PopoverPlacement): { side: RadixSide; align: RadixAlign } {\n switch (placement) {\n case 'bottom-start': return { side: 'bottom', align: 'start' };\n case 'bottom': return { side: 'bottom', align: 'center' };\n case 'bottom-end': return { side: 'bottom', align: 'end' };\n case 'top-start': return { side: 'top', align: 'start' };\n case 'top': return { side: 'top', align: 'center' };\n case 'top-end': return { side: 'top', align: 'end' };\n }\n}\n\n// ── Popover (root) ────────────────────────────────────────────────────────────\n\nexport interface PopoverProps {\n /** Controlled open state */\n open?: boolean;\n /** Controlled: called when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Uncontrolled initial open state */\n defaultOpen?: boolean;\n placement?: PopoverPlacement;\n children: ReactNode;\n}\n\nexport function Popover({\n open,\n onOpenChange,\n defaultOpen = false,\n placement = 'bottom-start',\n children,\n}: PopoverProps) {\n const { side, align } = toRadixPlacement(placement);\n\n return (\n // We pass side/align down via a context-like trick: wrap in a span-level\n // context provider so PopoverContent can pick up the resolved placement.\n // Radix manages open state internally; controlled mode uses open/onOpenChange.\n <PopoverPrimitive.Root\n open={open}\n onOpenChange={onOpenChange}\n defaultOpen={defaultOpen}\n >\n {/*\n Render children with the resolved placement accessible via React context.\n Because PopoverTrigger and PopoverContent are co-located in this file,\n we can pass side/align as props through a thin internal context.\n */}\n <PopoverPlacementContext.Provider value={{ side, align }}>\n {children}\n </PopoverPlacementContext.Provider>\n </PopoverPrimitive.Root>\n );\n}\n\n// ── Internal placement context ────────────────────────────────────────────────\n// Not exported — Radix placement details never surface to consumers.\n\nimport { createContext, useContext } from 'react';\n\ninterface PlacementCtx { side: RadixSide; align: RadixAlign }\nconst PopoverPlacementContext = createContext<PlacementCtx>({ side: 'bottom', align: 'start' });\nfunction usePlacement() { return useContext(PopoverPlacementContext); }\n\n// ── PopoverTrigger ────────────────────────────────────────────────────────────\n\nexport interface PopoverTriggerProps {\n children: ReactNode;\n}\n\nexport function PopoverTrigger({ children }: PopoverTriggerProps) {\n return (\n // asChild forwards Radix's accessibility props (aria-expanded, aria-haspopup,\n // aria-controls) onto the inner <button> rather than creating a wrapper element.\n <PopoverPrimitive.Trigger asChild>\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-2 rounded-xl border border-slate-200 bg-white px-4 py-2 text-sm font-semibold text-slate-700 shadow-sm transition hover:border-slate-300 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30\"\n >\n {children}\n </button>\n </PopoverPrimitive.Trigger>\n );\n}\n\n// ── PopoverContent ────────────────────────────────────────────────────────────\n\nexport interface PopoverContentProps {\n /** Optional title rendered in the popover header */\n title?: string;\n children: ReactNode;\n /** Tailwind max-width class */\n maxWidth?: string;\n className?: string;\n}\n\nexport function PopoverContent({\n title,\n children,\n maxWidth = 'max-w-xs',\n className = '',\n}: PopoverContentProps) {\n const { side, align } = usePlacement();\n\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n side={side}\n align={align}\n sideOffset={8}\n // Radix will flip to the opposite side if the preferred side clips\n // the viewport (collision detection via @floating-ui/dom internally).\n // avoidCollisions is true by default.\n className={cn(\n // DIWA visual treatment — matches the pre-migration appearance exactly\n 'z-50 w-max rounded-xl border border-slate-200 bg-white shadow-lg',\n 'outline-none',\n maxWidth,\n className,\n )}\n >\n {title && (\n <div className=\"border-b border-slate-100 px-4 py-3\">\n <h3 className=\"font-heading text-sm font-bold text-slate-900\">{title}</h3>\n </div>\n )}\n <div className=\"px-4 py-3\">{children}</div>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,SAAS,iBAAiB,WAAqE;CAC7F,QAAQ,WAAR;EACE,KAAK,gBAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EAC7D,KAAK,UAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAS;EAC9D,KAAK,cAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAM;EAC3D,KAAK,aAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EAC7D,KAAK,OAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAS;EAC9D,KAAK,WAAgB,OAAO;GAAE,MAAM;GAAU,OAAO;EAAM;CAC7D;AACF;AAeA,SAAgB,QAAQ,EACtB,MACA,cACA,cAAc,OACd,YAAY,gBACZ,YACe;CACf,MAAM,EAAE,MAAM,UAAU,iBAAiB,SAAS;CAElD,OAIE,oBAAC,iBAAiB,MAAlB;EACQ;EACQ;EACD;EAOb,UAAA,oBAAC,wBAAwB,UAAzB;GAAkC,OAAO;IAAE;IAAM;GAAM;GACpD;EAC+B,CAAA;CACb,CAAA;AAE3B;AAQA,IAAM,0BAA0B,cAA4B;CAAE,MAAM;CAAU,OAAO;AAAQ,CAAC;AAC9F,SAAS,eAAe;CAAE,OAAO,WAAW,uBAAuB;AAAG;AAQtE,SAAgB,eAAe,EAAE,YAAiC;CAChE,OAGE,oBAAC,iBAAiB,SAAlB;EAA0B,SAAA;EACxB,UAAA,oBAAC,UAAD;GACE,MAAK;GACL,WAAU;GAET;EACK,CAAA;CACgB,CAAA;AAE9B;AAaA,SAAgB,eAAe,EAC7B,OACA,UACA,WAAW,YACX,YAAY,MACU;CACtB,MAAM,EAAE,MAAM,UAAU,aAAa;CAErC,OACE,oBAAC,iBAAiB,QAAlB,EAAA,UACE,qBAAC,iBAAiB,SAAlB;EACQ;EACC;EACP,YAAY;EAIZ,WAAW,GAET,oEACA,gBACA,UACA,SACF;EAbF,UAAA,CAeG,SACC,oBAAC,OAAD;GAAK,WAAU;GACb,UAAA,oBAAC,MAAD;IAAI,WAAU;IAAiD,UAAA;GAAU,CAAA;EACtE,CAAA,GAEP,oBAAC,OAAD;GAAK,WAAU;GAAa;EAAc,CAAA,CAClB;CACH,CAAA,EAAA,CAAA;AAE7B"}
|
package/lib/index41.cjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const require_runtime = require("./index2.cjs");
|
|
2
|
+
const require_utils = require("./index14.cjs");
|
|
3
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
4
|
+
require("react");
|
|
5
|
+
let _radix_ui_react_tooltip = require("@radix-ui/react-tooltip");
|
|
6
|
+
_radix_ui_react_tooltip = require_runtime.__toESM(_radix_ui_react_tooltip, 1);
|
|
7
|
+
//#region src/ui-library/gallery/tooltip/Tooltip.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Tooltip — Design System Component
|
|
10
|
+
*
|
|
11
|
+
* A short, text-only label that appears on hover or focus to describe
|
|
12
|
+
* an element.
|
|
13
|
+
*
|
|
14
|
+
* Behavior is provided by @radix-ui/react-tooltip, which handles:
|
|
15
|
+
* - Trigger scoping (fires only on the direct trigger, not descendants)
|
|
16
|
+
* - Viewport collision detection and automatic side-flip
|
|
17
|
+
* - Correct aria-describedby wiring
|
|
18
|
+
* - Keyboard: Escape closes, focus/blur open/close
|
|
19
|
+
* - Pointer events (hover, touch)
|
|
20
|
+
* - Portal rendering to document.body
|
|
21
|
+
* - Delay management
|
|
22
|
+
*
|
|
23
|
+
* DIWA continues to own:
|
|
24
|
+
* - Component API (content, placement, delayMs, children, className)
|
|
25
|
+
* - Visual classes (dark surface, brand-sky bottom accent)
|
|
26
|
+
* - Typography and spacing
|
|
27
|
+
*
|
|
28
|
+
* Rules:
|
|
29
|
+
* - Tooltip content must be TEXT only. No interactive elements inside.
|
|
30
|
+
* - Do not put ESSENTIAL information only in a tooltip.
|
|
31
|
+
* - Use for supplementary descriptions: icon button labels, abbrev. meanings.
|
|
32
|
+
*
|
|
33
|
+
* Placement: 'top' | 'bottom' | 'left' | 'right'
|
|
34
|
+
*
|
|
35
|
+
* Difference from Popover:
|
|
36
|
+
* - Tooltip is text-only, triggered by hover/focus, no interaction inside.
|
|
37
|
+
* - Popover contains interactive content, triggered by explicit click.
|
|
38
|
+
*/
|
|
39
|
+
function Tooltip({ content, placement = "top", delayMs = 400, children, className = "" }) {
|
|
40
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Provider, {
|
|
41
|
+
delayDuration: delayMs,
|
|
42
|
+
skipDelayDuration: 0,
|
|
43
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_radix_ui_react_tooltip.Root, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Trigger, {
|
|
44
|
+
asChild: true,
|
|
45
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
46
|
+
className: require_utils.cn("inline-flex", className),
|
|
47
|
+
children
|
|
48
|
+
})
|
|
49
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Content, {
|
|
50
|
+
side: placement,
|
|
51
|
+
sideOffset: 8,
|
|
52
|
+
className: require_utils.cn("pointer-events-none z-50 whitespace-nowrap", "rounded-lg bg-slate-900 px-2.5 py-1.5 text-xs font-semibold text-white shadow-lg", "border-b-2 border-brand-sky/60"),
|
|
53
|
+
children: content
|
|
54
|
+
}) })] })
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
exports.Tooltip = Tooltip;
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=index41.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index41.cjs","names":[],"sources":["../src/ui-library/gallery/tooltip/Tooltip.tsx"],"sourcesContent":["/**\n * Tooltip — Design System Component\n *\n * A short, text-only label that appears on hover or focus to describe\n * an element.\n *\n * Behavior is provided by @radix-ui/react-tooltip, which handles:\n * - Trigger scoping (fires only on the direct trigger, not descendants)\n * - Viewport collision detection and automatic side-flip\n * - Correct aria-describedby wiring\n * - Keyboard: Escape closes, focus/blur open/close\n * - Pointer events (hover, touch)\n * - Portal rendering to document.body\n * - Delay management\n *\n * DIWA continues to own:\n * - Component API (content, placement, delayMs, children, className)\n * - Visual classes (dark surface, brand-sky bottom accent)\n * - Typography and spacing\n *\n * Rules:\n * - Tooltip content must be TEXT only. No interactive elements inside.\n * - Do not put ESSENTIAL information only in a tooltip.\n * - Use for supplementary descriptions: icon button labels, abbrev. meanings.\n *\n * Placement: 'top' | 'bottom' | 'left' | 'right'\n *\n * Difference from Popover:\n * - Tooltip is text-only, triggered by hover/focus, no interaction inside.\n * - Popover contains interactive content, triggered by explicit click.\n */\n\nimport * as TooltipPrimitive from '@radix-ui/react-tooltip';\nimport { type ReactNode } from 'react';\nimport { cn } from '../../../lib/utils';\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface TooltipProps {\n /** The text content shown in the tooltip */\n content: string;\n /** Placement relative to the trigger */\n placement?: TooltipPlacement;\n /** Delay before showing the tooltip, in ms */\n delayMs?: number;\n /** The element that triggers the tooltip */\n children: ReactNode;\n className?: string;\n}\n\n// ── Radix side → DIWA placement mapping ──────────────────────────────────────\n// Radix uses 'top' | 'bottom' | 'left' | 'right' directly — exact match.\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function Tooltip({\n content,\n placement = 'top',\n delayMs = 400,\n children,\n className = '',\n}: TooltipProps) {\n return (\n // Provider is wrapped here so each Tooltip instance is self-contained.\n // Radix supports nested providers; this avoids requiring app-level setup.\n <TooltipPrimitive.Provider delayDuration={delayMs} skipDelayDuration={0}>\n <TooltipPrimitive.Root>\n {/*\n asChild forwards all props (including focus/hover handling) to the\n actual child element rather than wrapping it in an extra DOM node.\n This eliminates the previous issue where onFocusCapture on the wrapper\n span would fire for any focusable descendant.\n */}\n <TooltipPrimitive.Trigger asChild>\n <span className={cn('inline-flex', className)}>\n {children}\n </span>\n </TooltipPrimitive.Trigger>\n\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n side={placement}\n sideOffset={8}\n // Radix adds data-state and data-side attributes for animation hooks.\n // We keep the presentation simple: instant visibility, no transition.\n className={cn(\n // Base layout\n 'pointer-events-none z-50 whitespace-nowrap',\n // DIWA visual treatment — dark surface, brand typography\n 'rounded-lg bg-slate-900 px-2.5 py-1.5 text-xs font-semibold text-white shadow-lg',\n // DIWA structural accent (Phase 2 visual signature)\n 'border-b-2 border-brand-sky/60',\n )}\n >\n {content}\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n </TooltipPrimitive.Root>\n </TooltipPrimitive.Provider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,SAAgB,QAAQ,EACtB,SACA,YAAY,OACZ,UAAU,KACV,UACA,YAAY,MACG;CACf,OAGE,iBAAA,GAAA,kBAAA,IAAA,CAAC,wBAAiB,UAAlB;EAA2B,eAAe;EAAS,mBAAmB;EACpE,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,wBAAiB,MAAlB,EAAA,UAAA,CAOE,iBAAA,GAAA,kBAAA,IAAA,CAAC,wBAAiB,SAAlB;GAA0B,SAAA;GACxB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IAAM,WAAW,cAAA,GAAG,eAAe,SAAS;IACzC;GACG,CAAA;EACkB,CAAA,GAE1B,iBAAA,GAAA,kBAAA,IAAA,CAAC,wBAAiB,QAAlB,EAAA,UACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,wBAAiB,SAAlB;GACE,MAAM;GACN,YAAY;GAGZ,WAAW,cAAA,GAET,8CAEA,oFAEA,gCACF;GAEC,UAAA;EACuB,CAAA,EACH,CAAA,CACJ,EAAA,CAAA;CACE,CAAA;AAE/B"}
|
package/lib/index41.mjs
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { cn } from "./index14.mjs";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import "react";
|
|
4
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
5
|
+
//#region src/ui-library/gallery/tooltip/Tooltip.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Tooltip — Design System Component
|
|
8
|
+
*
|
|
9
|
+
* A short, text-only label that appears on hover or focus to describe
|
|
10
|
+
* an element.
|
|
11
|
+
*
|
|
12
|
+
* Behavior is provided by @radix-ui/react-tooltip, which handles:
|
|
13
|
+
* - Trigger scoping (fires only on the direct trigger, not descendants)
|
|
14
|
+
* - Viewport collision detection and automatic side-flip
|
|
15
|
+
* - Correct aria-describedby wiring
|
|
16
|
+
* - Keyboard: Escape closes, focus/blur open/close
|
|
17
|
+
* - Pointer events (hover, touch)
|
|
18
|
+
* - Portal rendering to document.body
|
|
19
|
+
* - Delay management
|
|
20
|
+
*
|
|
21
|
+
* DIWA continues to own:
|
|
22
|
+
* - Component API (content, placement, delayMs, children, className)
|
|
23
|
+
* - Visual classes (dark surface, brand-sky bottom accent)
|
|
24
|
+
* - Typography and spacing
|
|
25
|
+
*
|
|
26
|
+
* Rules:
|
|
27
|
+
* - Tooltip content must be TEXT only. No interactive elements inside.
|
|
28
|
+
* - Do not put ESSENTIAL information only in a tooltip.
|
|
29
|
+
* - Use for supplementary descriptions: icon button labels, abbrev. meanings.
|
|
30
|
+
*
|
|
31
|
+
* Placement: 'top' | 'bottom' | 'left' | 'right'
|
|
32
|
+
*
|
|
33
|
+
* Difference from Popover:
|
|
34
|
+
* - Tooltip is text-only, triggered by hover/focus, no interaction inside.
|
|
35
|
+
* - Popover contains interactive content, triggered by explicit click.
|
|
36
|
+
*/
|
|
37
|
+
function Tooltip({ content, placement = "top", delayMs = 400, children, className = "" }) {
|
|
38
|
+
return /* @__PURE__ */ jsx(TooltipPrimitive.Provider, {
|
|
39
|
+
delayDuration: delayMs,
|
|
40
|
+
skipDelayDuration: 0,
|
|
41
|
+
children: /* @__PURE__ */ jsxs(TooltipPrimitive.Root, { children: [/* @__PURE__ */ jsx(TooltipPrimitive.Trigger, {
|
|
42
|
+
asChild: true,
|
|
43
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
44
|
+
className: cn("inline-flex", className),
|
|
45
|
+
children
|
|
46
|
+
})
|
|
47
|
+
}), /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Content, {
|
|
48
|
+
side: placement,
|
|
49
|
+
sideOffset: 8,
|
|
50
|
+
className: cn("pointer-events-none z-50 whitespace-nowrap", "rounded-lg bg-slate-900 px-2.5 py-1.5 text-xs font-semibold text-white shadow-lg", "border-b-2 border-brand-sky/60"),
|
|
51
|
+
children: content
|
|
52
|
+
}) })] })
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { Tooltip };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=index41.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index41.mjs","names":[],"sources":["../src/ui-library/gallery/tooltip/Tooltip.tsx"],"sourcesContent":["/**\n * Tooltip — Design System Component\n *\n * A short, text-only label that appears on hover or focus to describe\n * an element.\n *\n * Behavior is provided by @radix-ui/react-tooltip, which handles:\n * - Trigger scoping (fires only on the direct trigger, not descendants)\n * - Viewport collision detection and automatic side-flip\n * - Correct aria-describedby wiring\n * - Keyboard: Escape closes, focus/blur open/close\n * - Pointer events (hover, touch)\n * - Portal rendering to document.body\n * - Delay management\n *\n * DIWA continues to own:\n * - Component API (content, placement, delayMs, children, className)\n * - Visual classes (dark surface, brand-sky bottom accent)\n * - Typography and spacing\n *\n * Rules:\n * - Tooltip content must be TEXT only. No interactive elements inside.\n * - Do not put ESSENTIAL information only in a tooltip.\n * - Use for supplementary descriptions: icon button labels, abbrev. meanings.\n *\n * Placement: 'top' | 'bottom' | 'left' | 'right'\n *\n * Difference from Popover:\n * - Tooltip is text-only, triggered by hover/focus, no interaction inside.\n * - Popover contains interactive content, triggered by explicit click.\n */\n\nimport * as TooltipPrimitive from '@radix-ui/react-tooltip';\nimport { type ReactNode } from 'react';\nimport { cn } from '../../../lib/utils';\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface TooltipProps {\n /** The text content shown in the tooltip */\n content: string;\n /** Placement relative to the trigger */\n placement?: TooltipPlacement;\n /** Delay before showing the tooltip, in ms */\n delayMs?: number;\n /** The element that triggers the tooltip */\n children: ReactNode;\n className?: string;\n}\n\n// ── Radix side → DIWA placement mapping ──────────────────────────────────────\n// Radix uses 'top' | 'bottom' | 'left' | 'right' directly — exact match.\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function Tooltip({\n content,\n placement = 'top',\n delayMs = 400,\n children,\n className = '',\n}: TooltipProps) {\n return (\n // Provider is wrapped here so each Tooltip instance is self-contained.\n // Radix supports nested providers; this avoids requiring app-level setup.\n <TooltipPrimitive.Provider delayDuration={delayMs} skipDelayDuration={0}>\n <TooltipPrimitive.Root>\n {/*\n asChild forwards all props (including focus/hover handling) to the\n actual child element rather than wrapping it in an extra DOM node.\n This eliminates the previous issue where onFocusCapture on the wrapper\n span would fire for any focusable descendant.\n */}\n <TooltipPrimitive.Trigger asChild>\n <span className={cn('inline-flex', className)}>\n {children}\n </span>\n </TooltipPrimitive.Trigger>\n\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n side={placement}\n sideOffset={8}\n // Radix adds data-state and data-side attributes for animation hooks.\n // We keep the presentation simple: instant visibility, no transition.\n className={cn(\n // Base layout\n 'pointer-events-none z-50 whitespace-nowrap',\n // DIWA visual treatment — dark surface, brand typography\n 'rounded-lg bg-slate-900 px-2.5 py-1.5 text-xs font-semibold text-white shadow-lg',\n // DIWA structural accent (Phase 2 visual signature)\n 'border-b-2 border-brand-sky/60',\n )}\n >\n {content}\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n </TooltipPrimitive.Root>\n </TooltipPrimitive.Provider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,SAAgB,QAAQ,EACtB,SACA,YAAY,OACZ,UAAU,KACV,UACA,YAAY,MACG;CACf,OAGE,oBAAC,iBAAiB,UAAlB;EAA2B,eAAe;EAAS,mBAAmB;EACpE,UAAA,qBAAC,iBAAiB,MAAlB,EAAA,UAAA,CAOE,oBAAC,iBAAiB,SAAlB;GAA0B,SAAA;GACxB,UAAA,oBAAC,QAAD;IAAM,WAAW,GAAG,eAAe,SAAS;IACzC;GACG,CAAA;EACkB,CAAA,GAE1B,oBAAC,iBAAiB,QAAlB,EAAA,UACE,oBAAC,iBAAiB,SAAlB;GACE,MAAM;GACN,YAAY;GAGZ,WAAW,GAET,8CAEA,oFAEA,gCACF;GAEC,UAAA;EACuB,CAAA,EACH,CAAA,CACJ,EAAA,CAAA;CACE,CAAA;AAE/B"}
|
package/lib/index42.cjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const require_Dialog = require("./index38.cjs");
|
|
2
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
let lucide_react = require("lucide-react");
|
|
5
|
+
//#region src/ui-library/gallery/confirm-dialog/ConfirmDialog.tsx
|
|
6
|
+
/**
|
|
7
|
+
* ConfirmDialog — Design System Component
|
|
8
|
+
*
|
|
9
|
+
* A focused confirmation dialog for irreversible or high-impact actions.
|
|
10
|
+
* Composes the gallery Dialog component.
|
|
11
|
+
*
|
|
12
|
+
* Accessibility:
|
|
13
|
+
* - role="dialog" + aria-modal from Dialog
|
|
14
|
+
* - title → aria-labelledby (Dialog's header id)
|
|
15
|
+
* - description → aria-describedby (Dialog's desc id)
|
|
16
|
+
* - Focus trapped within dialog
|
|
17
|
+
* - Escape dismisses (unless loading)
|
|
18
|
+
* - Confirm button is focused on open (first focusable element)
|
|
19
|
+
* - Destructive tone uses both color AND a warning icon — never color alone
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* <ConfirmDialog
|
|
23
|
+
* open={open}
|
|
24
|
+
* title="Delete department?"
|
|
25
|
+
* description="This cannot be undone."
|
|
26
|
+
* confirmLabel="Delete"
|
|
27
|
+
* tone="danger"
|
|
28
|
+
* onConfirm={handleDelete}
|
|
29
|
+
* onClose={() => setOpen(false)}
|
|
30
|
+
* />
|
|
31
|
+
*/
|
|
32
|
+
var ICON_WRAPPER = {
|
|
33
|
+
danger: "bg-rose-50 text-rose-500",
|
|
34
|
+
primary: "bg-blue-50 text-brand-blue"
|
|
35
|
+
};
|
|
36
|
+
var BTN_CONFIRM = {
|
|
37
|
+
danger: [
|
|
38
|
+
"inline-flex items-center justify-center gap-2 rounded-md",
|
|
39
|
+
"bg-rose-600 px-4 py-2.5 text-sm font-bold text-white shadow-lg shadow-rose-100",
|
|
40
|
+
"transition hover:bg-rose-700 active:scale-[.98]",
|
|
41
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-300",
|
|
42
|
+
"disabled:cursor-not-allowed disabled:bg-slate-300 disabled:shadow-none"
|
|
43
|
+
].join(" "),
|
|
44
|
+
primary: [
|
|
45
|
+
"inline-flex items-center justify-center gap-2 rounded-md",
|
|
46
|
+
"bg-brand-blue px-4 py-2.5 text-sm font-bold text-white shadow-lg shadow-brand-blue/20",
|
|
47
|
+
"transition hover:bg-brand-navy active:scale-[.98]",
|
|
48
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
49
|
+
"disabled:cursor-not-allowed disabled:bg-slate-300 disabled:shadow-none"
|
|
50
|
+
].join(" ")
|
|
51
|
+
};
|
|
52
|
+
var BTN_CANCEL = [
|
|
53
|
+
"inline-flex items-center justify-center gap-2 rounded-md border border-slate-300 bg-white",
|
|
54
|
+
"px-4 py-2.5 text-sm font-bold text-slate-600 shadow-sm transition",
|
|
55
|
+
"hover:border-slate-400 hover:bg-slate-50 active:scale-[.98]",
|
|
56
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/15",
|
|
57
|
+
"disabled:cursor-not-allowed disabled:opacity-60"
|
|
58
|
+
].join(" ");
|
|
59
|
+
function ConfirmDialog({ open, onClose, onConfirm, title, description, confirmLabel = "Confirm", cancelLabel = "Cancel", tone = "danger" }) {
|
|
60
|
+
const [loading, setLoading] = (0, react.useState)(false);
|
|
61
|
+
const handleConfirm = async () => {
|
|
62
|
+
setLoading(true);
|
|
63
|
+
try {
|
|
64
|
+
await onConfirm();
|
|
65
|
+
onClose();
|
|
66
|
+
} catch {} finally {
|
|
67
|
+
setLoading(false);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_Dialog.Dialog, {
|
|
71
|
+
open,
|
|
72
|
+
onClose: loading ? () => void 0 : onClose,
|
|
73
|
+
preventClose: loading,
|
|
74
|
+
title,
|
|
75
|
+
description: typeof description === "string" ? description : void 0,
|
|
76
|
+
size: "sm",
|
|
77
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Dialog.DialogBody, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
78
|
+
className: "flex items-start gap-3",
|
|
79
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
80
|
+
className: `mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-md ${ICON_WRAPPER[tone]}`,
|
|
81
|
+
"aria-hidden": "true",
|
|
82
|
+
children: tone === "danger" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlertTriangle, { size: 20 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Info, { size: 20 })
|
|
83
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
84
|
+
className: "text-sm font-medium leading-relaxed text-slate-600",
|
|
85
|
+
children: description
|
|
86
|
+
})]
|
|
87
|
+
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_Dialog.DialogFooter, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
88
|
+
type: "button",
|
|
89
|
+
className: BTN_CANCEL,
|
|
90
|
+
onClick: onClose,
|
|
91
|
+
disabled: loading,
|
|
92
|
+
children: cancelLabel
|
|
93
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
94
|
+
type: "button",
|
|
95
|
+
className: BTN_CONFIRM[tone],
|
|
96
|
+
onClick: () => void handleConfirm(),
|
|
97
|
+
disabled: loading,
|
|
98
|
+
children: loading ? "Processing…" : confirmLabel
|
|
99
|
+
})] })]
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
exports.ConfirmDialog = ConfirmDialog;
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=index42.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index42.cjs","names":[],"sources":["../src/ui-library/gallery/confirm-dialog/ConfirmDialog.tsx"],"sourcesContent":["/**\n * ConfirmDialog — Design System Component\n *\n * A focused confirmation dialog for irreversible or high-impact actions.\n * Composes the gallery Dialog component.\n *\n * Accessibility:\n * - role=\"dialog\" + aria-modal from Dialog\n * - title → aria-labelledby (Dialog's header id)\n * - description → aria-describedby (Dialog's desc id)\n * - Focus trapped within dialog\n * - Escape dismisses (unless loading)\n * - Confirm button is focused on open (first focusable element)\n * - Destructive tone uses both color AND a warning icon — never color alone\n *\n * Usage:\n * <ConfirmDialog\n * open={open}\n * title=\"Delete department?\"\n * description=\"This cannot be undone.\"\n * confirmLabel=\"Delete\"\n * tone=\"danger\"\n * onConfirm={handleDelete}\n * onClose={() => setOpen(false)}\n * />\n */\n\nimport { useState, type ReactNode } from 'react';\nimport { AlertTriangle, Info } from 'lucide-react';\nimport { Dialog, DialogBody, DialogFooter } from '../dialog/Dialog';\n\nexport type ConfirmDialogTone = 'danger' | 'primary';\n\nexport interface ConfirmDialogProps {\n /** Controls dialog visibility. */\n open: boolean;\n /** Called when the dialog should close (cancel, Escape, backdrop). */\n onClose: () => void;\n /**\n * Called when the user confirms. May be async — ConfirmDialog shows a\n * loading state until the promise resolves or rejects.\n * Any thrown errors are swallowed here; surface them via ErrorBanner / toast.\n */\n onConfirm: () => void | Promise<void>;\n /** Dialog heading. Required for accessible labeling. */\n title: string;\n /** Secondary explanation shown below the icon row. */\n description?: ReactNode;\n /** Confirm button label. Default: \"Confirm\" */\n confirmLabel?: string;\n /** Cancel button label. Default: \"Cancel\" */\n cancelLabel?: string;\n /**\n * Visual tone for the confirm button and icon.\n * \"danger\" — rose/destructive. \"primary\" — brand-blue.\n * Default: \"danger\"\n */\n tone?: ConfirmDialogTone;\n}\n\n// ── Style maps ─────────────────────────────────────────────────────────────\n\nconst ICON_WRAPPER: Record<ConfirmDialogTone, string> = {\n danger: 'bg-rose-50 text-rose-500',\n primary: 'bg-blue-50 text-brand-blue',\n};\n\nconst BTN_CONFIRM: Record<ConfirmDialogTone, string> = {\n danger: [\n 'inline-flex items-center justify-center gap-2 rounded-md',\n 'bg-rose-600 px-4 py-2.5 text-sm font-bold text-white shadow-lg shadow-rose-100',\n 'transition hover:bg-rose-700 active:scale-[.98]',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-300',\n 'disabled:cursor-not-allowed disabled:bg-slate-300 disabled:shadow-none',\n ].join(' '),\n primary: [\n 'inline-flex items-center justify-center gap-2 rounded-md',\n 'bg-brand-blue px-4 py-2.5 text-sm font-bold text-white shadow-lg shadow-brand-blue/20',\n 'transition hover:bg-brand-navy active:scale-[.98]',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30',\n 'disabled:cursor-not-allowed disabled:bg-slate-300 disabled:shadow-none',\n ].join(' '),\n};\n\nconst BTN_CANCEL = [\n 'inline-flex items-center justify-center gap-2 rounded-md border border-slate-300 bg-white',\n 'px-4 py-2.5 text-sm font-bold text-slate-600 shadow-sm transition',\n 'hover:border-slate-400 hover:bg-slate-50 active:scale-[.98]',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/15',\n 'disabled:cursor-not-allowed disabled:opacity-60',\n].join(' ');\n\n// ── Component ──────────────────────────────────────────────────────────────\n\nexport function ConfirmDialog({\n open,\n onClose,\n onConfirm,\n title,\n description,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n tone = 'danger',\n}: ConfirmDialogProps) {\n const [loading, setLoading] = useState(false);\n\n const handleConfirm = async () => {\n setLoading(true);\n try {\n await onConfirm();\n onClose();\n } catch {\n // Errors are the caller's responsibility to surface\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <Dialog\n open={open}\n onClose={loading ? () => undefined : onClose}\n preventClose={loading}\n title={title}\n description={typeof description === 'string' ? description : undefined}\n size=\"sm\"\n >\n <DialogBody>\n <div className=\"flex items-start gap-3\">\n <span\n className={`mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-md ${ICON_WRAPPER[tone]}`}\n aria-hidden=\"true\"\n >\n {tone === 'danger'\n ? <AlertTriangle size={20} />\n : <Info size={20} />\n }\n </span>\n <div className=\"text-sm font-medium leading-relaxed text-slate-600\">\n {description}\n </div>\n </div>\n </DialogBody>\n\n <DialogFooter>\n <button\n type=\"button\"\n className={BTN_CANCEL}\n onClick={onClose}\n disabled={loading}\n >\n {cancelLabel}\n </button>\n <button\n type=\"button\"\n className={BTN_CONFIRM[tone]}\n onClick={() => void handleConfirm()}\n disabled={loading}\n >\n {loading ? 'Processing…' : confirmLabel}\n </button>\n </DialogFooter>\n </Dialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAM,eAAkD;CACtD,QAAS;CACT,SAAS;AACX;AAEA,IAAM,cAAiD;CACrD,QAAQ;EACN;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,GAAG;CACV,SAAS;EACP;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,IAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG;AAIV,SAAgB,cAAc,EAC5B,MACA,SACA,WACA,OACA,aACA,eAAe,WACf,cAAc,UACd,OAAO,YACc;CACrB,MAAM,CAAC,SAAS,eAAA,GAAc,MAAA,SAAA,CAAS,KAAK;CAE5C,MAAM,gBAAgB,YAAY;EAChC,WAAW,IAAI;EACf,IAAI;GACF,MAAM,UAAU;GAChB,QAAQ;EACV,QAAQ,CAER,UAAU;GACR,WAAW,KAAK;EAClB;CACF;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,eAAA,QAAD;EACQ;EACN,SAAS,gBAAgB,KAAA,IAAY;EACrC,cAAc;EACP;EACP,aAAa,OAAO,gBAAgB,WAAW,cAAc,KAAA;EAC7D,MAAK;EANP,UAAA,CAQE,iBAAA,GAAA,kBAAA,IAAA,CAAC,eAAA,YAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IACE,WAAW,uEAAuE,aAAa;IAC/F,eAAY;IAEX,UAAA,SAAS,WACN,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,eAAD,EAAe,MAAM,GAAK,CAAA,IAC1B,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAA,MAAD,EAAM,MAAM,GAAK,CAAA;GAEjB,CAAA,GACN,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAU;IACZ,UAAA;GACE,CAAA,CACF;EACK,CAAA,EAAA,CAAA,GAEZ,iBAAA,GAAA,kBAAA,KAAA,CAAC,eAAA,cAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,MAAK;GACL,WAAW;GACX,SAAS;GACT,UAAU;GAET,UAAA;EACK,CAAA,GACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,MAAK;GACL,WAAW,YAAY;GACvB,eAAe,KAAK,cAAc;GAClC,UAAU;GAET,UAAA,UAAU,gBAAgB;EACrB,CAAA,CACI,EAAA,CAAA,CACR;;AAEZ"}
|
package/lib/index42.mjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Dialog, DialogBody, DialogFooter } from "./index38.mjs";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { AlertTriangle, Info } from "lucide-react";
|
|
5
|
+
//#region src/ui-library/gallery/confirm-dialog/ConfirmDialog.tsx
|
|
6
|
+
/**
|
|
7
|
+
* ConfirmDialog — Design System Component
|
|
8
|
+
*
|
|
9
|
+
* A focused confirmation dialog for irreversible or high-impact actions.
|
|
10
|
+
* Composes the gallery Dialog component.
|
|
11
|
+
*
|
|
12
|
+
* Accessibility:
|
|
13
|
+
* - role="dialog" + aria-modal from Dialog
|
|
14
|
+
* - title → aria-labelledby (Dialog's header id)
|
|
15
|
+
* - description → aria-describedby (Dialog's desc id)
|
|
16
|
+
* - Focus trapped within dialog
|
|
17
|
+
* - Escape dismisses (unless loading)
|
|
18
|
+
* - Confirm button is focused on open (first focusable element)
|
|
19
|
+
* - Destructive tone uses both color AND a warning icon — never color alone
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* <ConfirmDialog
|
|
23
|
+
* open={open}
|
|
24
|
+
* title="Delete department?"
|
|
25
|
+
* description="This cannot be undone."
|
|
26
|
+
* confirmLabel="Delete"
|
|
27
|
+
* tone="danger"
|
|
28
|
+
* onConfirm={handleDelete}
|
|
29
|
+
* onClose={() => setOpen(false)}
|
|
30
|
+
* />
|
|
31
|
+
*/
|
|
32
|
+
var ICON_WRAPPER = {
|
|
33
|
+
danger: "bg-rose-50 text-rose-500",
|
|
34
|
+
primary: "bg-blue-50 text-brand-blue"
|
|
35
|
+
};
|
|
36
|
+
var BTN_CONFIRM = {
|
|
37
|
+
danger: [
|
|
38
|
+
"inline-flex items-center justify-center gap-2 rounded-md",
|
|
39
|
+
"bg-rose-600 px-4 py-2.5 text-sm font-bold text-white shadow-lg shadow-rose-100",
|
|
40
|
+
"transition hover:bg-rose-700 active:scale-[.98]",
|
|
41
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-300",
|
|
42
|
+
"disabled:cursor-not-allowed disabled:bg-slate-300 disabled:shadow-none"
|
|
43
|
+
].join(" "),
|
|
44
|
+
primary: [
|
|
45
|
+
"inline-flex items-center justify-center gap-2 rounded-md",
|
|
46
|
+
"bg-brand-blue px-4 py-2.5 text-sm font-bold text-white shadow-lg shadow-brand-blue/20",
|
|
47
|
+
"transition hover:bg-brand-navy active:scale-[.98]",
|
|
48
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
49
|
+
"disabled:cursor-not-allowed disabled:bg-slate-300 disabled:shadow-none"
|
|
50
|
+
].join(" ")
|
|
51
|
+
};
|
|
52
|
+
var BTN_CANCEL = [
|
|
53
|
+
"inline-flex items-center justify-center gap-2 rounded-md border border-slate-300 bg-white",
|
|
54
|
+
"px-4 py-2.5 text-sm font-bold text-slate-600 shadow-sm transition",
|
|
55
|
+
"hover:border-slate-400 hover:bg-slate-50 active:scale-[.98]",
|
|
56
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/15",
|
|
57
|
+
"disabled:cursor-not-allowed disabled:opacity-60"
|
|
58
|
+
].join(" ");
|
|
59
|
+
function ConfirmDialog({ open, onClose, onConfirm, title, description, confirmLabel = "Confirm", cancelLabel = "Cancel", tone = "danger" }) {
|
|
60
|
+
const [loading, setLoading] = useState(false);
|
|
61
|
+
const handleConfirm = async () => {
|
|
62
|
+
setLoading(true);
|
|
63
|
+
try {
|
|
64
|
+
await onConfirm();
|
|
65
|
+
onClose();
|
|
66
|
+
} catch {} finally {
|
|
67
|
+
setLoading(false);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
return /* @__PURE__ */ jsxs(Dialog, {
|
|
71
|
+
open,
|
|
72
|
+
onClose: loading ? () => void 0 : onClose,
|
|
73
|
+
preventClose: loading,
|
|
74
|
+
title,
|
|
75
|
+
description: typeof description === "string" ? description : void 0,
|
|
76
|
+
size: "sm",
|
|
77
|
+
children: [/* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsxs("div", {
|
|
78
|
+
className: "flex items-start gap-3",
|
|
79
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
80
|
+
className: `mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-md ${ICON_WRAPPER[tone]}`,
|
|
81
|
+
"aria-hidden": "true",
|
|
82
|
+
children: tone === "danger" ? /* @__PURE__ */ jsx(AlertTriangle, { size: 20 }) : /* @__PURE__ */ jsx(Info, { size: 20 })
|
|
83
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
84
|
+
className: "text-sm font-medium leading-relaxed text-slate-600",
|
|
85
|
+
children: description
|
|
86
|
+
})]
|
|
87
|
+
}) }), /* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx("button", {
|
|
88
|
+
type: "button",
|
|
89
|
+
className: BTN_CANCEL,
|
|
90
|
+
onClick: onClose,
|
|
91
|
+
disabled: loading,
|
|
92
|
+
children: cancelLabel
|
|
93
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
94
|
+
type: "button",
|
|
95
|
+
className: BTN_CONFIRM[tone],
|
|
96
|
+
onClick: () => void handleConfirm(),
|
|
97
|
+
disabled: loading,
|
|
98
|
+
children: loading ? "Processing…" : confirmLabel
|
|
99
|
+
})] })]
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { ConfirmDialog };
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=index42.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index42.mjs","names":[],"sources":["../src/ui-library/gallery/confirm-dialog/ConfirmDialog.tsx"],"sourcesContent":["/**\n * ConfirmDialog — Design System Component\n *\n * A focused confirmation dialog for irreversible or high-impact actions.\n * Composes the gallery Dialog component.\n *\n * Accessibility:\n * - role=\"dialog\" + aria-modal from Dialog\n * - title → aria-labelledby (Dialog's header id)\n * - description → aria-describedby (Dialog's desc id)\n * - Focus trapped within dialog\n * - Escape dismisses (unless loading)\n * - Confirm button is focused on open (first focusable element)\n * - Destructive tone uses both color AND a warning icon — never color alone\n *\n * Usage:\n * <ConfirmDialog\n * open={open}\n * title=\"Delete department?\"\n * description=\"This cannot be undone.\"\n * confirmLabel=\"Delete\"\n * tone=\"danger\"\n * onConfirm={handleDelete}\n * onClose={() => setOpen(false)}\n * />\n */\n\nimport { useState, type ReactNode } from 'react';\nimport { AlertTriangle, Info } from 'lucide-react';\nimport { Dialog, DialogBody, DialogFooter } from '../dialog/Dialog';\n\nexport type ConfirmDialogTone = 'danger' | 'primary';\n\nexport interface ConfirmDialogProps {\n /** Controls dialog visibility. */\n open: boolean;\n /** Called when the dialog should close (cancel, Escape, backdrop). */\n onClose: () => void;\n /**\n * Called when the user confirms. May be async — ConfirmDialog shows a\n * loading state until the promise resolves or rejects.\n * Any thrown errors are swallowed here; surface them via ErrorBanner / toast.\n */\n onConfirm: () => void | Promise<void>;\n /** Dialog heading. Required for accessible labeling. */\n title: string;\n /** Secondary explanation shown below the icon row. */\n description?: ReactNode;\n /** Confirm button label. Default: \"Confirm\" */\n confirmLabel?: string;\n /** Cancel button label. Default: \"Cancel\" */\n cancelLabel?: string;\n /**\n * Visual tone for the confirm button and icon.\n * \"danger\" — rose/destructive. \"primary\" — brand-blue.\n * Default: \"danger\"\n */\n tone?: ConfirmDialogTone;\n}\n\n// ── Style maps ─────────────────────────────────────────────────────────────\n\nconst ICON_WRAPPER: Record<ConfirmDialogTone, string> = {\n danger: 'bg-rose-50 text-rose-500',\n primary: 'bg-blue-50 text-brand-blue',\n};\n\nconst BTN_CONFIRM: Record<ConfirmDialogTone, string> = {\n danger: [\n 'inline-flex items-center justify-center gap-2 rounded-md',\n 'bg-rose-600 px-4 py-2.5 text-sm font-bold text-white shadow-lg shadow-rose-100',\n 'transition hover:bg-rose-700 active:scale-[.98]',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-300',\n 'disabled:cursor-not-allowed disabled:bg-slate-300 disabled:shadow-none',\n ].join(' '),\n primary: [\n 'inline-flex items-center justify-center gap-2 rounded-md',\n 'bg-brand-blue px-4 py-2.5 text-sm font-bold text-white shadow-lg shadow-brand-blue/20',\n 'transition hover:bg-brand-navy active:scale-[.98]',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30',\n 'disabled:cursor-not-allowed disabled:bg-slate-300 disabled:shadow-none',\n ].join(' '),\n};\n\nconst BTN_CANCEL = [\n 'inline-flex items-center justify-center gap-2 rounded-md border border-slate-300 bg-white',\n 'px-4 py-2.5 text-sm font-bold text-slate-600 shadow-sm transition',\n 'hover:border-slate-400 hover:bg-slate-50 active:scale-[.98]',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/15',\n 'disabled:cursor-not-allowed disabled:opacity-60',\n].join(' ');\n\n// ── Component ──────────────────────────────────────────────────────────────\n\nexport function ConfirmDialog({\n open,\n onClose,\n onConfirm,\n title,\n description,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n tone = 'danger',\n}: ConfirmDialogProps) {\n const [loading, setLoading] = useState(false);\n\n const handleConfirm = async () => {\n setLoading(true);\n try {\n await onConfirm();\n onClose();\n } catch {\n // Errors are the caller's responsibility to surface\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <Dialog\n open={open}\n onClose={loading ? () => undefined : onClose}\n preventClose={loading}\n title={title}\n description={typeof description === 'string' ? description : undefined}\n size=\"sm\"\n >\n <DialogBody>\n <div className=\"flex items-start gap-3\">\n <span\n className={`mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-md ${ICON_WRAPPER[tone]}`}\n aria-hidden=\"true\"\n >\n {tone === 'danger'\n ? <AlertTriangle size={20} />\n : <Info size={20} />\n }\n </span>\n <div className=\"text-sm font-medium leading-relaxed text-slate-600\">\n {description}\n </div>\n </div>\n </DialogBody>\n\n <DialogFooter>\n <button\n type=\"button\"\n className={BTN_CANCEL}\n onClick={onClose}\n disabled={loading}\n >\n {cancelLabel}\n </button>\n <button\n type=\"button\"\n className={BTN_CONFIRM[tone]}\n onClick={() => void handleConfirm()}\n disabled={loading}\n >\n {loading ? 'Processing…' : confirmLabel}\n </button>\n </DialogFooter>\n </Dialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAM,eAAkD;CACtD,QAAS;CACT,SAAS;AACX;AAEA,IAAM,cAAiD;CACrD,QAAQ;EACN;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,GAAG;CACV,SAAS;EACP;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,IAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG;AAIV,SAAgB,cAAc,EAC5B,MACA,SACA,WACA,OACA,aACA,eAAe,WACf,cAAc,UACd,OAAO,YACc;CACrB,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,MAAM,gBAAgB,YAAY;EAChC,WAAW,IAAI;EACf,IAAI;GACF,MAAM,UAAU;GAChB,QAAQ;EACV,QAAQ,CAER,UAAU;GACR,WAAW,KAAK;EAClB;CACF;CAEA,OACE,qBAAC,QAAD;EACQ;EACN,SAAS,gBAAgB,KAAA,IAAY;EACrC,cAAc;EACP;EACP,aAAa,OAAO,gBAAgB,WAAW,cAAc,KAAA;EAC7D,MAAK;EANP,UAAA,CAQE,oBAAC,YAAD,EAAA,UACE,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,oBAAC,QAAD;IACE,WAAW,uEAAuE,aAAa;IAC/F,eAAY;IAEX,UAAA,SAAS,WACN,oBAAC,eAAD,EAAe,MAAM,GAAK,CAAA,IAC1B,oBAAC,MAAD,EAAM,MAAM,GAAK,CAAA;GAEjB,CAAA,GACN,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA;GACE,CAAA,CACF;EACK,CAAA,EAAA,CAAA,GAEZ,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,UAAD;GACE,MAAK;GACL,WAAW;GACX,SAAS;GACT,UAAU;GAET,UAAA;EACK,CAAA,GACR,oBAAC,UAAD;GACE,MAAK;GACL,WAAW,YAAY;GACvB,eAAe,KAAK,cAAc;GAClC,UAAU;GAET,UAAA,UAAU,gBAAgB;EACrB,CAAA,CACI,EAAA,CAAA,CACR;;AAEZ"}
|
package/lib/index43.cjs
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/ui-library/tokens/dimension.ts
|
|
2
|
+
var dimension_exports = /* @__PURE__ */ require("./index2.cjs").__exportAll({ dimension: () => dimension });
|
|
3
|
+
/**
|
|
4
|
+
* DIWA Dimension Tokens
|
|
5
|
+
*
|
|
6
|
+
* Formalizes repeating dimension values identified in the Phase 9 audit.
|
|
7
|
+
* Values are numeric (pixels) or Tailwind class strings as appropriate.
|
|
8
|
+
*
|
|
9
|
+
* Resolved in Phase 11–12:
|
|
10
|
+
* - Dialog close icon aligned to size={16} (Phase 11).
|
|
11
|
+
* - ConfirmAction icon badge aligned to h-8 w-8 / rounded-lg (Phase 12).
|
|
12
|
+
*/
|
|
13
|
+
var dimension = {
|
|
14
|
+
icon: {
|
|
15
|
+
/** Standard close button icon — target 16, currently Dialog uses 18 */
|
|
16
|
+
close: 16,
|
|
17
|
+
/** Icon inside 32×32 header badge */
|
|
18
|
+
badgeSm: 16,
|
|
19
|
+
/** Alert/ConfirmDialog tone icons */
|
|
20
|
+
alert: 20,
|
|
21
|
+
/** Inline small action icons */
|
|
22
|
+
actionSm: 14,
|
|
23
|
+
/** Standard action icons in toolbars, buttons */
|
|
24
|
+
action: 16
|
|
25
|
+
},
|
|
26
|
+
iconBadge: {
|
|
27
|
+
/** Standard 32×32 header icon container — h-8 w-8 */
|
|
28
|
+
size: "h-8 w-8" },
|
|
29
|
+
control: {
|
|
30
|
+
/** Small control height */
|
|
31
|
+
sm: "h-9",
|
|
32
|
+
/** Default control height */
|
|
33
|
+
md: "h-10",
|
|
34
|
+
/** Large control height */
|
|
35
|
+
lg: "h-11"
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
exports.dimension = dimension;
|
|
40
|
+
Object.defineProperty(exports, "dimension_exports", {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
get: function() {
|
|
43
|
+
return dimension_exports;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=index43.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index43.cjs","names":[],"sources":["../src/ui-library/tokens/dimension.ts"],"sourcesContent":["/**\n * DIWA Dimension Tokens\n *\n * Formalizes repeating dimension values identified in the Phase 9 audit.\n * Values are numeric (pixels) or Tailwind class strings as appropriate.\n *\n * Resolved in Phase 11–12:\n * - Dialog close icon aligned to size={16} (Phase 11).\n * - ConfirmAction icon badge aligned to h-8 w-8 / rounded-lg (Phase 12).\n */\n\nexport const dimension = {\n // ── Icon sizes (lucide-react `size` prop values) ──────────────────────────\n\n icon: {\n /** Standard close button icon — target 16, currently Dialog uses 18 */\n close: 16,\n /** Icon inside 32×32 header badge */\n badgeSm: 16,\n /** Alert/ConfirmDialog tone icons */\n alert: 20,\n /** Inline small action icons */\n actionSm: 14,\n /** Standard action icons in toolbars, buttons */\n action: 16,\n },\n\n // ── Icon badge containers ─────────────────────────────────────────────────\n\n iconBadge: {\n /** Standard 32×32 header icon container — h-8 w-8 */\n size: 'h-8 w-8',\n },\n\n // ── Component heights ─────────────────────────────────────────────────────\n\n control: {\n /** Small control height */\n sm: 'h-9',\n /** Default control height */\n md: 'h-10',\n /** Large control height */\n lg: 'h-11',\n },\n} as const;\n"],"mappings":";;;;;;;;;;;;AAWA,IAAa,YAAY;CAGvB,MAAM;;EAEJ,OAAa;;EAEb,SAAa;;EAEb,OAAa;;EAEb,UAAa;;EAEb,QAAa;CACf;CAIA,WAAW;;AAET,MAAQ,UACV;CAIA,SAAS;;EAEP,IAAI;;EAEJ,IAAI;;EAEJ,IAAI;CACN;AACF"}
|