@manhphi1309/dialog 0.1.1 → 0.2.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/dist/index.cjs CHANGED
@@ -1,35 +1,102 @@
1
- "use client";
2
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#endregion
3
+ require("react");
3
4
  let lucide_react = require("lucide-react");
4
5
  let radix_ui = require("radix-ui");
5
6
  let _manhphi1309_button = require("@manhphi1309/button");
6
7
  let _manhphi1309_utils = require("@manhphi1309/utils");
7
8
  let react_jsx_runtime = require("react/jsx-runtime");
8
- //#region index.tsx
9
+ let _manhphi1309_drawer = require("@manhphi1309/drawer");
10
+ let _manhphi1309_hooks = require("@manhphi1309/hooks");
11
+ //#region src/dialog.tsx
12
+ /**
13
+ * Root dialog component. Controls open/close state and provides context
14
+ * to all child primitives via Radix `Dialog.Root`.
15
+ *
16
+ * Can be used as **uncontrolled** (via `defaultOpen`) or **controlled**
17
+ * (via `open` + `onOpenChange`).
18
+ *
19
+ * @example
20
+ * // Uncontrolled
21
+ * <Dialog>
22
+ * <DialogTrigger>Open</DialogTrigger>
23
+ * <DialogContent>...</DialogContent>
24
+ * </Dialog>
25
+ *
26
+ * @example
27
+ * // Controlled
28
+ * const [open, setOpen] = useState(false)
29
+ * <Dialog open={open} onOpenChange={setOpen}>
30
+ * <DialogContent>...</DialogContent>
31
+ * </Dialog>
32
+ */
9
33
  function Dialog({ ...props }) {
10
34
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(radix_ui.Dialog.Root, {
11
35
  "data-slot": "dialog",
12
36
  ...props
13
37
  });
14
38
  }
39
+ /**
40
+ * The element that opens the dialog when activated (clicked / keyboard).
41
+ * Thin wrapper around Radix `Dialog.Trigger`.
42
+ *
43
+ * Use `asChild` to compose with your own element instead of the default button.
44
+ *
45
+ * @example
46
+ * <DialogTrigger asChild>
47
+ * <Button variant="outline">Open</Button>
48
+ * </DialogTrigger>
49
+ */
15
50
  function DialogTrigger({ ...props }) {
16
51
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(radix_ui.Dialog.Trigger, {
17
52
  "data-slot": "dialog-trigger",
18
53
  ...props
19
54
  });
20
55
  }
56
+ /**
57
+ * Renders its children into a **portal** appended to `document.body`,
58
+ * outside the current DOM tree. This ensures the dialog always appears
59
+ * above other content and is never clipped by parent `overflow` or `z-index`.
60
+ *
61
+ * You rarely need to use this directly — `DialogContent` already wraps its
62
+ * output in a portal automatically.
63
+ */
21
64
  function DialogPortal({ ...props }) {
22
65
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(radix_ui.Dialog.Portal, {
23
66
  "data-slot": "dialog-portal",
24
67
  ...props
25
68
  });
26
69
  }
70
+ /**
71
+ * A primitive close trigger. Closes the dialog when activated.
72
+ * Use `asChild` to compose with a custom element.
73
+ *
74
+ * Prefer the built-in `showCloseButton` prop on `DialogContent` or
75
+ * `DialogFooter` for standard close affordances. Use this component
76
+ * when you need a completely custom close action inside the dialog body.
77
+ *
78
+ * @example
79
+ * <DialogClose asChild>
80
+ * <Button variant="ghost">Cancel</Button>
81
+ * </DialogClose>
82
+ */
27
83
  function DialogClose({ ...props }) {
28
84
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(radix_ui.Dialog.Close, {
29
85
  "data-slot": "dialog-close",
30
86
  ...props
31
87
  });
32
88
  }
89
+ /**
90
+ * The semi-transparent **backdrop** rendered behind the dialog panel.
91
+ * Covers the full viewport (`fixed inset-0`) with a slight blur and dark tint.
92
+ *
93
+ * Animates:
94
+ * - **Open** → `fade-in-0`
95
+ * - **Close** → `fade-out-0`
96
+ *
97
+ * You rarely need to use this directly — `DialogContent` renders it
98
+ * automatically via `DialogPortal`.
99
+ */
33
100
  function DialogOverlay({ className, ...props }) {
34
101
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(radix_ui.Dialog.Overlay, {
35
102
  "data-slot": "dialog-overlay",
@@ -37,6 +104,29 @@ function DialogOverlay({ className, ...props }) {
37
104
  ...props
38
105
  });
39
106
  }
107
+ /**
108
+ * The **visible panel** of the dialog. Rendered centred on screen via
109
+ * `position: fixed; top: 50%; left: 50%; translate(-50%, -50%)`.
110
+ * Automatically wraps itself in `DialogPortal` and renders `DialogOverlay`
111
+ * behind it.
112
+ *
113
+ * By default a ghost ✕ button is positioned in the top-right corner.
114
+ * Pass `showCloseButton={false}` to remove it when you want to control
115
+ * close behaviour exclusively from the footer.
116
+ *
117
+ * Animates:
118
+ * - **Open** → `fade-in-0 + zoom-in-95`
119
+ * - **Close** → `fade-out-0 + zoom-out-95`
120
+ *
121
+ * @param showCloseButton - Whether to render the ✕ ghost button in the
122
+ * top-right corner. Defaults to `true`.
123
+ *
124
+ * @example
125
+ * <DialogContent showCloseButton={false}>
126
+ * <DialogHeader>...</DialogHeader>
127
+ * <DialogFooter showCloseButton>...</DialogFooter>
128
+ * </DialogContent>
129
+ */
40
130
  function DialogContent({ className, children, showCloseButton = true, ...props }) {
41
131
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DialogPortal, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogOverlay, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(radix_ui.Dialog.Content, {
42
132
  "data-slot": "dialog-content",
@@ -48,7 +138,8 @@ function DialogContent({ className, children, showCloseButton = true, ...props }
48
138
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_manhphi1309_button.Button, {
49
139
  variant: "ghost",
50
140
  className: "absolute top-2 right-2",
51
- size: "icon-sm",
141
+ size: "sm",
142
+ icon: true,
52
143
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XIcon, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53
144
  className: "sr-only",
54
145
  children: "Close"
@@ -57,6 +148,17 @@ function DialogContent({ className, children, showCloseButton = true, ...props }
57
148
  })]
58
149
  })] });
59
150
  }
151
+ /**
152
+ * Layout wrapper for the **top section** of a dialog.
153
+ * Stacks children vertically with `gap-2`. Typically contains
154
+ * `DialogTitle` and optionally `DialogDescription`.
155
+ *
156
+ * @example
157
+ * <DialogHeader>
158
+ * <DialogTitle>Confirm deletion</DialogTitle>
159
+ * <DialogDescription>This action is irreversible.</DialogDescription>
160
+ * </DialogHeader>
161
+ */
60
162
  function DialogHeader({ className, ...props }) {
61
163
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
62
164
  "data-slot": "dialog-header",
@@ -64,6 +166,20 @@ function DialogHeader({ className, ...props }) {
64
166
  ...props
65
167
  });
66
168
  }
169
+ /**
170
+ * Layout wrapper for the **bottom action area** of a dialog.
171
+ * Bleeds to the content panel edges (`-mx-4 -mb-4`), adds a top border
172
+ * and a subtle muted background tint. Actions stack vertically on mobile
173
+ * and align to the right on `sm+` screens.
174
+ *
175
+ * @param showCloseButton - When `true`, appends an outline `Close` button
176
+ * wired to `DialogClose` after any provided `children`. Defaults to `false`.
177
+ *
178
+ * @example
179
+ * <DialogFooter showCloseButton>
180
+ * <Button type="submit">Save</Button>
181
+ * </DialogFooter>
182
+ */
67
183
  function DialogFooter({ className, showCloseButton = false, children, ...props }) {
68
184
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
69
185
  "data-slot": "dialog-footer",
@@ -78,6 +194,16 @@ function DialogFooter({ className, showCloseButton = false, children, ...props }
78
194
  })]
79
195
  });
80
196
  }
197
+ /**
198
+ * The **accessible title** of the dialog. Rendered as a Radix `Dialog.Title`,
199
+ * which is automatically linked to the dialog content via `aria-labelledby`.
200
+ * Screen readers announce this text when the dialog opens.
201
+ *
202
+ * Styled with the heading font token at `text-base font-medium`.
203
+ *
204
+ * @remarks Required for accessibility. Every dialog should have a title,
205
+ * even if visually hidden with `className="sr-only"`.
206
+ */
81
207
  function DialogTitle({ className, ...props }) {
82
208
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(radix_ui.Dialog.Title, {
83
209
  "data-slot": "dialog-title",
@@ -85,6 +211,17 @@ function DialogTitle({ className, ...props }) {
85
211
  ...props
86
212
  });
87
213
  }
214
+ /**
215
+ * The **accessible description** of the dialog. Rendered as a Radix
216
+ * `Dialog.Description`, which is linked to the dialog content via
217
+ * `aria-describedby`. Screen readers read this after the title.
218
+ *
219
+ * Styled as small muted text. Anchor tags (`<a>`) inside the description
220
+ * are automatically underlined and change colour on hover.
221
+ *
222
+ * @remarks Optional but recommended. Omit only if the dialog content itself
223
+ * is sufficiently self-descriptive.
224
+ */
88
225
  function DialogDescription({ className, ...props }) {
89
226
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(radix_ui.Dialog.Description, {
90
227
  "data-slot": "dialog-description",
@@ -93,6 +230,214 @@ function DialogDescription({ className, ...props }) {
93
230
  });
94
231
  }
95
232
  //#endregion
233
+ //#region src/responsive-dialog.tsx
234
+ /**
235
+ * Adaptive root component that switches between `Dialog` and `Drawer`
236
+ * depending on screen width:
237
+ * - **Desktop (≥ 768 px)** → renders `Dialog` (centred modal overlay)
238
+ * - **Mobile (< 768 px)** → renders `Drawer` (bottom-sheet panel)
239
+ *
240
+ * All props are forwarded unchanged to the active primitive, so `open`,
241
+ * `defaultOpen`, `onOpenChange`, and `modal` all behave identically to
242
+ * the standard `Dialog` root.
243
+ *
244
+ * @example
245
+ * <ResponsiveDialog>
246
+ * <ResponsiveDialogTrigger asChild>
247
+ * <Button>Open</Button>
248
+ * </ResponsiveDialogTrigger>
249
+ * <ResponsiveDialogContent>
250
+ * <ResponsiveDialogHeader>
251
+ * <ResponsiveDialogTitle>Title</ResponsiveDialogTitle>
252
+ * </ResponsiveDialogHeader>
253
+ * </ResponsiveDialogContent>
254
+ * </ResponsiveDialog>
255
+ */
256
+ function ResponsiveDialog({ children, ...props }) {
257
+ if ((0, _manhphi1309_hooks.useIsMobile)()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_drawer.Drawer, {
258
+ ...props,
259
+ children
260
+ });
261
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dialog, {
262
+ ...props,
263
+ children
264
+ });
265
+ }
266
+ /**
267
+ * The element that opens the responsive dialog/drawer when activated.
268
+ *
269
+ * - **Desktop** → `DialogTrigger` (Radix `Dialog.Trigger`)
270
+ * - **Mobile** → `DrawerTrigger` (Vaul `Drawer.Trigger`)
271
+ *
272
+ * Use `asChild` to render your own element as the trigger instead of
273
+ * the default button wrapper.
274
+ *
275
+ * @example
276
+ * <ResponsiveDialogTrigger asChild>
277
+ * <Button variant="outline">Open</Button>
278
+ * </ResponsiveDialogTrigger>
279
+ */
280
+ function ResponsiveDialogTrigger({ children, ...props }) {
281
+ if ((0, _manhphi1309_hooks.useIsMobile)()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_drawer.DrawerTrigger, {
282
+ ...props,
283
+ children
284
+ });
285
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogTrigger, {
286
+ ...props,
287
+ children
288
+ });
289
+ }
290
+ /**
291
+ * A close trigger that targets the correct primitive for the current
292
+ * viewport:
293
+ * - **Desktop** → `DialogClose` (Radix `Dialog.Close`)
294
+ * - **Mobile** → `DrawerClose` (Vaul `Drawer.Close`)
295
+ *
296
+ * Use `asChild` to wrap a custom element.
297
+ *
298
+ * @example
299
+ * <ResponsiveDialogClose asChild>
300
+ * <Button variant="ghost">Cancel</Button>
301
+ * </ResponsiveDialogClose>
302
+ */
303
+ function ResponsiveDialogClose({ children, ...props }) {
304
+ if ((0, _manhphi1309_hooks.useIsMobile)()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_drawer.DrawerClose, {
305
+ ...props,
306
+ children
307
+ });
308
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogClose, {
309
+ ...props,
310
+ children
311
+ });
312
+ }
313
+ /**
314
+ * The visible panel of the responsive dialog. Delegates to the
315
+ * appropriate primitive:
316
+ * - **Desktop** → `DialogContent` — centred modal with fade + zoom animations.
317
+ * The ✕ close button is shown by default (`showCloseButton={true}`).
318
+ * - **Mobile** → `DrawerContent` — bottom-sheet panel with a drag handle.
319
+ * `showCloseButton` has **no effect** on mobile; the drawer is closed
320
+ * via swipe gesture or the drag handle.
321
+ *
322
+ * @param showCloseButton - Show/hide the ✕ button in the top-right corner
323
+ * on desktop. Has no effect on mobile. Defaults to `true`.
324
+ *
325
+ * @example
326
+ * // Hide the ✕ button and force close via footer only (desktop)
327
+ * <ResponsiveDialogContent showCloseButton={false}>
328
+ * ...
329
+ * </ResponsiveDialogContent>
330
+ */
331
+ function ResponsiveDialogContent({ className, children, showCloseButton = true, ...props }) {
332
+ if ((0, _manhphi1309_hooks.useIsMobile)()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_drawer.DrawerContent, {
333
+ className,
334
+ children
335
+ });
336
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogContent, {
337
+ className,
338
+ showCloseButton,
339
+ ...props,
340
+ children
341
+ });
342
+ }
343
+ /**
344
+ * Layout wrapper for the **top section** of the responsive dialog/drawer.
345
+ *
346
+ * - **Desktop** → `DialogHeader` — vertical stack with `gap-2`
347
+ * - **Mobile** → `DrawerHeader` — vertically centred text for bottom/top
348
+ * drawers, left-aligned for side drawers
349
+ *
350
+ * Typically contains `ResponsiveDialogTitle` and optionally
351
+ * `ResponsiveDialogDescription`.
352
+ */
353
+ function ResponsiveDialogHeader({ className, ...props }) {
354
+ if ((0, _manhphi1309_hooks.useIsMobile)()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_drawer.DrawerHeader, {
355
+ className,
356
+ ...props
357
+ });
358
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogHeader, {
359
+ className,
360
+ ...props
361
+ });
362
+ }
363
+ /**
364
+ * Layout wrapper for the **bottom action area** of the responsive
365
+ * dialog/drawer.
366
+ *
367
+ * - **Desktop** → `DialogFooter` — bleeds to panel edges, top border,
368
+ * actions aligned right on `sm+`
369
+ * - **Mobile** → `DrawerFooter` — `mt-auto` stacked column at the bottom
370
+ * of the drawer
371
+ *
372
+ * @param showCloseButton - When `true`, appends a close button after
373
+ * `children`. On **desktop** this is a `DialogClose`-wrapped outline
374
+ * button; on **mobile** this is a `DrawerClose`-wrapped outline button.
375
+ * Defaults to `false`.
376
+ *
377
+ * @example
378
+ * <ResponsiveDialogFooter showCloseButton>
379
+ * <Button type="submit">Save</Button>
380
+ * </ResponsiveDialogFooter>
381
+ */
382
+ function ResponsiveDialogFooter({ className, children, showCloseButton = false, ...props }) {
383
+ if ((0, _manhphi1309_hooks.useIsMobile)()) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_manhphi1309_drawer.DrawerFooter, {
384
+ className,
385
+ ...props,
386
+ children: [children, showCloseButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_drawer.DrawerClose, {
387
+ asChild: true,
388
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_button.Button, {
389
+ variant: "outline",
390
+ children: "Close"
391
+ })
392
+ })]
393
+ });
394
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogFooter, {
395
+ className,
396
+ showCloseButton,
397
+ ...props,
398
+ children
399
+ });
400
+ }
401
+ /**
402
+ * The **accessible title** of the responsive dialog/drawer.
403
+ *
404
+ * - **Desktop** → `DialogTitle` (linked via `aria-labelledby`)
405
+ * - **Mobile** → `DrawerTitle` (linked via `aria-labelledby`)
406
+ *
407
+ * Screen readers announce this text when the panel opens.
408
+ *
409
+ * @remarks Required for accessibility on both desktop and mobile.
410
+ */
411
+ function ResponsiveDialogTitle({ className, ...props }) {
412
+ if ((0, _manhphi1309_hooks.useIsMobile)()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_drawer.DrawerTitle, {
413
+ className,
414
+ ...props
415
+ });
416
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogTitle, {
417
+ className,
418
+ ...props
419
+ });
420
+ }
421
+ /**
422
+ * The **accessible description** of the responsive dialog/drawer.
423
+ *
424
+ * - **Desktop** → `DialogDescription` (linked via `aria-describedby`)
425
+ * - **Mobile** → `DrawerDescription` (linked via `aria-describedby`)
426
+ *
427
+ * @remarks Optional but recommended. Provides supplementary context for
428
+ * screen reader users after the title is announced.
429
+ */
430
+ function ResponsiveDialogDescription({ className, ...props }) {
431
+ if ((0, _manhphi1309_hooks.useIsMobile)()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_manhphi1309_drawer.DrawerDescription, {
432
+ className,
433
+ ...props
434
+ });
435
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogDescription, {
436
+ className,
437
+ ...props
438
+ });
439
+ }
440
+ //#endregion
96
441
  exports.Dialog = Dialog;
97
442
  exports.DialogClose = DialogClose;
98
443
  exports.DialogContent = DialogContent;
@@ -103,3 +448,11 @@ exports.DialogOverlay = DialogOverlay;
103
448
  exports.DialogPortal = DialogPortal;
104
449
  exports.DialogTitle = DialogTitle;
105
450
  exports.DialogTrigger = DialogTrigger;
451
+ exports.ResponsiveDialog = ResponsiveDialog;
452
+ exports.ResponsiveDialogClose = ResponsiveDialogClose;
453
+ exports.ResponsiveDialogContent = ResponsiveDialogContent;
454
+ exports.ResponsiveDialogDescription = ResponsiveDialogDescription;
455
+ exports.ResponsiveDialogFooter = ResponsiveDialogFooter;
456
+ exports.ResponsiveDialogHeader = ResponsiveDialogHeader;
457
+ exports.ResponsiveDialogTitle = ResponsiveDialogTitle;
458
+ exports.ResponsiveDialogTrigger = ResponsiveDialogTrigger;