@fpkit/acss 6.4.2 → 6.5.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/libs/chunk-EARHB4PD.js +11 -0
- package/libs/chunk-EARHB4PD.js.map +1 -0
- package/libs/chunk-PSIXTYFY.cjs +19 -0
- package/libs/chunk-PSIXTYFY.cjs.map +1 -0
- package/libs/components/alert/alert.css +1 -0
- package/libs/components/alert/alert.css.map +1 -0
- package/libs/components/alert/alert.min.css +3 -0
- package/libs/components/badge/badge.css +1 -0
- package/libs/components/badge/badge.css.map +1 -0
- package/libs/components/badge/badge.min.css +3 -0
- package/libs/components/box/box.css +1 -0
- package/libs/components/box/box.css.map +1 -0
- package/libs/components/box/box.min.css +3 -0
- package/libs/components/breadcrumbs/breadcrumb.css +1 -0
- package/libs/components/breadcrumbs/breadcrumb.css.map +1 -0
- package/libs/components/breadcrumbs/breadcrumb.min.css +3 -0
- package/libs/components/buttons/button.css +1 -0
- package/libs/components/buttons/button.css.map +1 -0
- package/libs/components/buttons/button.min.css +3 -0
- package/libs/components/buttons/icon-button.css +1 -0
- package/libs/components/buttons/icon-button.css.map +1 -0
- package/libs/components/buttons/icon-button.min.css +3 -0
- package/libs/components/cards/card-style.css +1 -0
- package/libs/components/cards/card-style.css.map +1 -0
- package/libs/components/cards/card-style.min.css +3 -0
- package/libs/components/cards/card.css +1 -0
- package/libs/components/cards/card.css.map +1 -0
- package/libs/components/cards/card.min.css +3 -0
- package/libs/components/cluster/cluster.css +1 -0
- package/libs/components/cluster/cluster.css.map +1 -0
- package/libs/components/cluster/cluster.min.css +3 -0
- package/libs/components/details/details.css +1 -0
- package/libs/components/details/details.css.map +1 -0
- package/libs/components/details/details.min.css +3 -0
- package/libs/components/dialog/dialog.cjs +3 -3
- package/libs/components/dialog/dialog.css +1 -0
- package/libs/components/dialog/dialog.css.map +1 -0
- package/libs/components/dialog/dialog.d.cts +1 -1
- package/libs/components/dialog/dialog.d.ts +1 -1
- package/libs/components/dialog/dialog.js +1 -1
- package/libs/components/dialog/dialog.min.css +3 -0
- package/libs/components/flexbox/flex.css +1 -0
- package/libs/components/flexbox/flex.css.map +1 -0
- package/libs/components/flexbox/flex.min.css +3 -0
- package/libs/components/form/checkbox.css +1 -0
- package/libs/components/form/checkbox.css.map +1 -0
- package/libs/components/form/checkbox.min.css +3 -0
- package/libs/components/form/form.css +1 -0
- package/libs/components/form/form.css.map +1 -0
- package/libs/components/form/form.min.css +3 -0
- package/libs/components/form/select.css +1 -0
- package/libs/components/form/select.css.map +1 -0
- package/libs/components/form/select.min.css +3 -0
- package/libs/components/grid/grid.css +1 -0
- package/libs/components/grid/grid.css.map +1 -0
- package/libs/components/grid/grid.min.css +3 -0
- package/libs/components/icons/icon.css +1 -0
- package/libs/components/icons/icon.css.map +1 -0
- package/libs/components/icons/icon.min.css +3 -0
- package/libs/components/images/img.css +1 -0
- package/libs/components/images/img.css.map +1 -0
- package/libs/components/images/img.min.css +3 -0
- package/libs/components/layout/landmarks.css +1 -0
- package/libs/components/layout/landmarks.css.map +1 -0
- package/libs/components/layout/landmarks.min.css +3 -0
- package/libs/components/link/link.css +1 -0
- package/libs/components/link/link.css.map +1 -0
- package/libs/components/link/link.min.css +3 -0
- package/libs/components/list/list.css +1 -0
- package/libs/components/list/list.css.map +1 -0
- package/libs/components/list/list.min.css +3 -0
- package/libs/components/nav/nav.css +1 -0
- package/libs/components/nav/nav.css.map +1 -0
- package/libs/components/nav/nav.min.css +3 -0
- package/libs/components/popover/popover.css +1 -0
- package/libs/components/popover/popover.css.map +1 -0
- package/libs/components/popover/popover.min.css +3 -0
- package/libs/components/progress/progress.css +1 -0
- package/libs/components/progress/progress.css.map +1 -0
- package/libs/components/progress/progress.min.css +3 -0
- package/libs/components/stack/stack.css +1 -0
- package/libs/components/stack/stack.css.map +1 -0
- package/libs/components/stack/stack.min.css +3 -0
- package/libs/components/styles/index.css +1 -0
- package/libs/components/styles/index.css.map +1 -0
- package/libs/components/styles/index.min.css +3 -0
- package/libs/components/tag/tag.css +1 -0
- package/libs/components/tag/tag.css.map +1 -0
- package/libs/components/tag/tag.min.css +3 -0
- package/libs/components/text-to-speech/text-to-speech.css +1 -0
- package/libs/components/text-to-speech/text-to-speech.css.map +1 -0
- package/libs/components/text-to-speech/text-to-speech.min.css +3 -0
- package/libs/components/title/title.css +1 -0
- package/libs/components/title/title.css.map +1 -0
- package/libs/components/title/title.min.css +3 -0
- package/libs/{dialog-6c6b3588.d.ts → dialog-e28c085f.d.ts} +14 -1
- package/libs/index.cjs +38 -36
- package/libs/index.cjs.map +1 -1
- package/libs/index.css +1 -0
- package/libs/index.css.map +1 -0
- package/libs/index.d.cts +3 -32
- package/libs/index.d.ts +3 -32
- package/libs/index.js +13 -14
- package/libs/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/dialog/README.mdx +87 -2
- package/src/components/dialog/STYLES.mdx +119 -12
- package/src/components/dialog/dialog-modal.stories.tsx +107 -2
- package/src/components/dialog/dialog-modal.tsx +6 -0
- package/src/components/dialog/dialog.scss +89 -2
- package/src/components/dialog/dialog.stories.tsx +30 -3
- package/src/components/dialog/dialog.test.tsx +96 -0
- package/src/components/dialog/dialog.tsx +7 -1
- package/src/components/dialog/dialog.types.ts +25 -0
- package/src/components/dialog/views/dialog-header.tsx +10 -8
- package/src/index.ts +6 -2
- package/src/styles/dialog/dialog.css +88 -2
- package/src/styles/dialog/dialog.css.map +1 -1
- package/src/styles/index.css +87 -2
- package/src/styles/index.css.map +1 -1
- package/libs/chunk-VQTCTLFN.js +0 -11
- package/libs/chunk-VQTCTLFN.js.map +0 -1
- package/libs/chunk-ZOPHCNFD.cjs +0 -18
- package/libs/chunk-ZOPHCNFD.cjs.map +0 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@fpkit/acss",
|
|
3
3
|
"description": "A lightweight React UI library for building modern and accessible components that leverage CSS custom properties for reactive Styles.",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "6.
|
|
5
|
+
"version": "6.5.0",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.12.0",
|
|
8
8
|
"npm": ">=8.0.0"
|
|
@@ -123,5 +123,5 @@
|
|
|
123
123
|
"publishConfig": {
|
|
124
124
|
"access": "public"
|
|
125
125
|
},
|
|
126
|
-
"gitHead": "
|
|
126
|
+
"gitHead": "9063512fa822963d8151c972bed9f5b0e531df0f"
|
|
127
127
|
}
|
|
@@ -15,6 +15,8 @@ A modern, accessible dialog component system built with React and TypeScript, le
|
|
|
15
15
|
✅ **Two Modes** - Modal dialogs (overlay) and inline alert dialogs
|
|
16
16
|
✅ **Focus Management** - Automatic focus restoration to trigger element
|
|
17
17
|
✅ **Flexible API** - Controlled `Dialog` or uncontrolled `DialogModal` wrapper
|
|
18
|
+
✅ **Size Variants** - Small, medium, large, and full-screen dialog sizes
|
|
19
|
+
✅ **Position Control** - Center, top, bottom, left/right drawers, and corner positioning
|
|
18
20
|
✅ **Customizable** - CSS custom properties for theming
|
|
19
21
|
✅ **Keyboard Accessible** - `:focus-visible` styles, Escape key support
|
|
20
22
|
✅ **High Contrast Mode** - Supports `prefers-contrast: high` media query
|
|
@@ -116,6 +118,8 @@ function MyComponent() {
|
|
|
116
118
|
| `className` | `string` | `""` | Additional CSS classes to apply to the dialog |
|
|
117
119
|
| `dialogLabel` | `string` | - | Optional `aria-label` for the dialog |
|
|
118
120
|
| `styles` | `CSSProperties` | - | Inline styles to apply to the dialog element |
|
|
121
|
+
| `size` | `"sm" \| "md" \| "lg" \| "full"` | - | Size variant controlling dialog dimensions |
|
|
122
|
+
| `position` | `"center" \| "top" \| "bottom" \| "left" \| "right" \| "top-left" \| "top-right" \| "bottom-left" \| "bottom-right"` | `"center"` | Position of the dialog on screen |
|
|
119
123
|
|
|
120
124
|
---
|
|
121
125
|
|
|
@@ -251,6 +255,70 @@ function SimpleExample() {
|
|
|
251
255
|
}
|
|
252
256
|
```
|
|
253
257
|
|
|
258
|
+
### Size Variants
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { DialogModal } from "@fpkit/acss";
|
|
262
|
+
|
|
263
|
+
function SizeExample() {
|
|
264
|
+
return (
|
|
265
|
+
<>
|
|
266
|
+
{/* Small dialog (25rem) */}
|
|
267
|
+
<DialogModal dialogTitle="Small" btnLabel="Open Small" size="sm">
|
|
268
|
+
<p>A compact dialog for simple actions.</p>
|
|
269
|
+
</DialogModal>
|
|
270
|
+
|
|
271
|
+
{/* Medium dialog (32rem) */}
|
|
272
|
+
<DialogModal dialogTitle="Medium" btnLabel="Open Medium" size="md">
|
|
273
|
+
<p>A standard dialog for forms and content.</p>
|
|
274
|
+
</DialogModal>
|
|
275
|
+
|
|
276
|
+
{/* Large dialog (48rem) */}
|
|
277
|
+
<DialogModal dialogTitle="Large" btnLabel="Open Large" size="lg">
|
|
278
|
+
<p>A wide dialog for complex content.</p>
|
|
279
|
+
</DialogModal>
|
|
280
|
+
|
|
281
|
+
{/* Full-screen dialog */}
|
|
282
|
+
<DialogModal dialogTitle="Full Screen" btnLabel="Open Full" size="full">
|
|
283
|
+
<p>This dialog fills the entire viewport.</p>
|
|
284
|
+
</DialogModal>
|
|
285
|
+
</>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Position Variants
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
import { DialogModal } from "@fpkit/acss";
|
|
294
|
+
|
|
295
|
+
function PositionExample() {
|
|
296
|
+
return (
|
|
297
|
+
<>
|
|
298
|
+
{/* Top-positioned dialog */}
|
|
299
|
+
<DialogModal dialogTitle="Top" btnLabel="Open Top" position="top">
|
|
300
|
+
<p>Positioned at the top center of the viewport.</p>
|
|
301
|
+
</DialogModal>
|
|
302
|
+
|
|
303
|
+
{/* Bottom sheet */}
|
|
304
|
+
<DialogModal dialogTitle="Bottom Sheet" btnLabel="Open Bottom" position="bottom">
|
|
305
|
+
<p>Positioned at the bottom, like a mobile bottom sheet.</p>
|
|
306
|
+
</DialogModal>
|
|
307
|
+
|
|
308
|
+
{/* Right drawer (full-height side panel) */}
|
|
309
|
+
<DialogModal dialogTitle="Right Drawer" btnLabel="Open Drawer" size="sm" position="right">
|
|
310
|
+
<p>A full-height drawer sliding in from the right.</p>
|
|
311
|
+
</DialogModal>
|
|
312
|
+
|
|
313
|
+
{/* Corner positioning */}
|
|
314
|
+
<DialogModal dialogTitle="Corner" btnLabel="Open Corner" size="sm" position="bottom-right">
|
|
315
|
+
<p>Positioned in the bottom-right corner.</p>
|
|
316
|
+
</DialogModal>
|
|
317
|
+
</>
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
254
322
|
### Custom Styling
|
|
255
323
|
|
|
256
324
|
```tsx
|
|
@@ -370,7 +438,13 @@ Override these variables to customize the dialog appearance:
|
|
|
370
438
|
```css
|
|
371
439
|
:root {
|
|
372
440
|
/* Dimensions */
|
|
373
|
-
--dialog-min-
|
|
441
|
+
--dialog-min-width: max(20rem, 80%);
|
|
442
|
+
--dialog-width: var(--dialog-min-width);
|
|
443
|
+
--dialog-max-width: 90vw;
|
|
444
|
+
--dialog-height: auto;
|
|
445
|
+
--dialog-max-height: 85vh;
|
|
446
|
+
--dialog-margin: auto;
|
|
447
|
+
--dialog-inset: 0;
|
|
374
448
|
--dialog-gap: 0.625rem;
|
|
375
449
|
--dialog-padding: 1.5rem;
|
|
376
450
|
--dialog-padding-inline: 1rem;
|
|
@@ -421,6 +495,8 @@ import type {
|
|
|
421
495
|
DialogModalProps,
|
|
422
496
|
DialogHeaderProps,
|
|
423
497
|
DialogFooterProps,
|
|
498
|
+
DialogSize,
|
|
499
|
+
DialogPosition,
|
|
424
500
|
} from "@fpkit/acss";
|
|
425
501
|
```
|
|
426
502
|
|
|
@@ -429,6 +505,13 @@ import type {
|
|
|
429
505
|
All components are fully typed with comprehensive JSDoc comments:
|
|
430
506
|
|
|
431
507
|
```tsx
|
|
508
|
+
type DialogSize = "sm" | "md" | "lg" | "full";
|
|
509
|
+
type DialogPosition =
|
|
510
|
+
| "center" | "top" | "bottom"
|
|
511
|
+
| "left" | "right"
|
|
512
|
+
| "top-left" | "top-right"
|
|
513
|
+
| "bottom-left" | "bottom-right";
|
|
514
|
+
|
|
432
515
|
interface DialogProps extends BaseDialogProps {
|
|
433
516
|
isOpen: boolean;
|
|
434
517
|
onOpenChange: (open: boolean) => void;
|
|
@@ -438,6 +521,8 @@ interface DialogProps extends BaseDialogProps {
|
|
|
438
521
|
confirmLabel?: string;
|
|
439
522
|
cancelLabel?: string;
|
|
440
523
|
hideFooter?: boolean;
|
|
524
|
+
size?: DialogSize;
|
|
525
|
+
position?: DialogPosition;
|
|
441
526
|
}
|
|
442
527
|
```
|
|
443
528
|
|
|
@@ -554,7 +639,7 @@ For older browsers, consider using a polyfill like [`dialog-polyfill`](https://g
|
|
|
554
639
|
## Testing
|
|
555
640
|
|
|
556
641
|
The dialog component has comprehensive test coverage:
|
|
557
|
-
- ✓
|
|
642
|
+
- ✓ 40 tests covering all scenarios
|
|
558
643
|
- ✓ Controlled vs uncontrolled behavior
|
|
559
644
|
- ✓ Modal vs non-modal rendering
|
|
560
645
|
- ✓ ARIA attributes validation
|
|
@@ -18,6 +18,8 @@ accessibility and high contrast mode support.
|
|
|
18
18
|
|
|
19
19
|
- **Native `<dialog>` element** - Built on HTML5 dialog with proper semantics
|
|
20
20
|
- **Flexible layout** - Flexbox-based column layout with customizable gap
|
|
21
|
+
- **Size variants** - Small, medium, large, and full-screen via `data-size` attribute
|
|
22
|
+
- **Position variants** - Center, top, bottom, left/right drawers, and corners via `data-position` attribute
|
|
21
23
|
- **Header with close button** - Pre-styled header section with close control
|
|
22
24
|
- **Action footer** - Flexible footer for action buttons
|
|
23
25
|
- **Keyboard accessibility** - Full keyboard navigation with visible focus
|
|
@@ -38,6 +40,12 @@ Root-level variables define the dialog's overall appearance:
|
|
|
38
40
|
:root {
|
|
39
41
|
/* Dimensions */
|
|
40
42
|
--dialog-min-width: max(20rem, 80%); /* Responsive width: min 320px or 80% */
|
|
43
|
+
--dialog-width: var(--dialog-min-width); /* Actual applied width */
|
|
44
|
+
--dialog-max-width: 90vw; /* Maximum width cap */
|
|
45
|
+
--dialog-height: auto; /* Dialog height */
|
|
46
|
+
--dialog-max-height: 85vh; /* Maximum height cap */
|
|
47
|
+
--dialog-margin: auto; /* Positioning margin */
|
|
48
|
+
--dialog-inset: 0; /* Positioning inset */
|
|
41
49
|
|
|
42
50
|
/* Layout */
|
|
43
51
|
--dialog-display: flex;
|
|
@@ -739,7 +747,8 @@ All dialog CSS variables follow specific patterns:
|
|
|
739
747
|
|
|
740
748
|
| Category | Variable Pattern | Example |
|
|
741
749
|
| -------------- | ---------------------------- | ------------------------------------------------ |
|
|
742
|
-
| **Dimensions** | `--dialog-{dimension}` | `--dialog-
|
|
750
|
+
| **Dimensions** | `--dialog-{dimension}` | `--dialog-width`, `--dialog-max-width`, `--dialog-height` |
|
|
751
|
+
| **Position** | `--dialog-{position-prop}` | `--dialog-margin`, `--dialog-inset` |
|
|
743
752
|
| **Layout** | `--dialog-{layout-property}` | `--dialog-display`, `--dialog-flex-direction` |
|
|
744
753
|
| **Spacing** | `--dialog-{spacing-type}` | `--dialog-padding`, `--dialog-gap` |
|
|
745
754
|
| **Borders** | `--dialog-border-{property}` | `--dialog-border-color`, `--dialog-border-width` |
|
|
@@ -752,6 +761,12 @@ All dialog CSS variables follow specific patterns:
|
|
|
752
761
|
```css
|
|
753
762
|
/* Dimensions */
|
|
754
763
|
--dialog-min-width /* Minimum width (responsive) */
|
|
764
|
+
--dialog-width /* Applied width (defaults to min-width) */
|
|
765
|
+
--dialog-max-width /* Maximum width cap (90vw) */
|
|
766
|
+
--dialog-height /* Dialog height (auto) */
|
|
767
|
+
--dialog-max-height /* Maximum height cap (85vh) */
|
|
768
|
+
--dialog-margin /* Positioning margin (auto) */
|
|
769
|
+
--dialog-inset /* Positioning inset (0) */
|
|
755
770
|
|
|
756
771
|
/* Layout */
|
|
757
772
|
--dialog-display /* Display mode (flex) */
|
|
@@ -815,29 +830,121 @@ For browsers without native `<dialog>` support, use:
|
|
|
815
830
|
</script>
|
|
816
831
|
```
|
|
817
832
|
|
|
818
|
-
##
|
|
833
|
+
## Size Variants
|
|
834
|
+
|
|
835
|
+
Control dialog dimensions using the `size` prop (React) or `data-size` attribute (HTML).
|
|
836
|
+
Sizes are applied via `data-size` attribute selectors in CSS.
|
|
819
837
|
|
|
820
|
-
|
|
838
|
+
| Size | Width | Description |
|
|
839
|
+
|------|-------|-------------|
|
|
840
|
+
| `sm` | 25rem (400px) | Compact dialog for simple confirmations |
|
|
841
|
+
| `md` | 32rem (512px) | Standard dialog for forms |
|
|
842
|
+
| `lg` | 48rem (768px) | Wide dialog for complex content |
|
|
843
|
+
| `full` | 100vw/100vh | Full-screen dialog filling the viewport |
|
|
821
844
|
|
|
822
|
-
|
|
845
|
+
### CSS for Size Variants
|
|
823
846
|
|
|
824
847
|
```css
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
848
|
+
dialog[data-size="sm"] { --dialog-width: 25rem; }
|
|
849
|
+
dialog[data-size="md"] { --dialog-width: 32rem; }
|
|
850
|
+
dialog[data-size="lg"] { --dialog-width: 48rem; }
|
|
851
|
+
|
|
852
|
+
/* Full-screen uses direct values to override browser UA stylesheet */
|
|
853
|
+
dialog[data-size="full"] {
|
|
854
|
+
box-sizing: border-box;
|
|
855
|
+
width: 100vw;
|
|
856
|
+
max-width: 100vw;
|
|
857
|
+
height: 100vh;
|
|
858
|
+
max-height: 100vh;
|
|
859
|
+
margin: 0;
|
|
860
|
+
inset: 0;
|
|
861
|
+
border: 0;
|
|
862
|
+
border-radius: 0;
|
|
828
863
|
}
|
|
864
|
+
```
|
|
829
865
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
866
|
+
### React Usage
|
|
867
|
+
|
|
868
|
+
```tsx
|
|
869
|
+
<Dialog size="sm" ... />
|
|
870
|
+
<DialogModal size="lg" btnLabel="Open Large" ... />
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### HTML Usage
|
|
874
|
+
|
|
875
|
+
```html
|
|
876
|
+
<dialog data-size="sm">Small dialog</dialog>
|
|
877
|
+
<dialog data-size="full">Full-screen dialog</dialog>
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
## Position Variants
|
|
881
|
+
|
|
882
|
+
Control dialog placement using the `position` prop (React) or `data-position` attribute (HTML).
|
|
883
|
+
Left/right positions create full-height drawer panels.
|
|
884
|
+
|
|
885
|
+
| Position | Behavior |
|
|
886
|
+
|----------|----------|
|
|
887
|
+
| `center` | Centered on screen (default) |
|
|
888
|
+
| `top` | Anchored to top center |
|
|
889
|
+
| `bottom` | Anchored to bottom center (bottom sheet) |
|
|
890
|
+
| `left` | Full-height left drawer |
|
|
891
|
+
| `right` | Full-height right drawer |
|
|
892
|
+
| `top-left` | Anchored to top-left corner |
|
|
893
|
+
| `top-right` | Anchored to top-right corner |
|
|
894
|
+
| `bottom-left` | Anchored to bottom-left corner |
|
|
895
|
+
| `bottom-right` | Anchored to bottom-right corner |
|
|
896
|
+
|
|
897
|
+
### CSS for Position Variants
|
|
898
|
+
|
|
899
|
+
Position variants override `--dialog-margin` and `--dialog-inset` to control placement:
|
|
900
|
+
|
|
901
|
+
```css
|
|
902
|
+
dialog[data-position="top"] {
|
|
903
|
+
--dialog-margin: 0 auto auto auto;
|
|
904
|
+
--dialog-inset: 0;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
dialog[data-position="right"] {
|
|
908
|
+
--dialog-margin: 0 0 0 auto;
|
|
909
|
+
--dialog-inset: 0;
|
|
910
|
+
--dialog-height: 100vh;
|
|
911
|
+
--dialog-max-height: 100vh;
|
|
912
|
+
--dialog-border-radius: 0;
|
|
833
913
|
}
|
|
834
914
|
```
|
|
835
915
|
|
|
916
|
+
### React Usage
|
|
917
|
+
|
|
918
|
+
```tsx
|
|
919
|
+
<Dialog position="top" ... />
|
|
920
|
+
<DialogModal size="sm" position="right" btnLabel="Open Drawer" ... />
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
### HTML Usage
|
|
924
|
+
|
|
836
925
|
```html
|
|
837
|
-
<dialog
|
|
838
|
-
<dialog
|
|
926
|
+
<dialog data-position="bottom">Bottom sheet</dialog>
|
|
927
|
+
<dialog data-size="sm" data-position="right">Right drawer</dialog>
|
|
839
928
|
```
|
|
840
929
|
|
|
930
|
+
### Combining Size and Position
|
|
931
|
+
|
|
932
|
+
Size and position can be combined for precise control:
|
|
933
|
+
|
|
934
|
+
```tsx
|
|
935
|
+
{/* Right drawer panel */}
|
|
936
|
+
<DialogModal size="sm" position="right" dialogTitle="Settings" btnLabel="Open">
|
|
937
|
+
<p>Drawer content</p>
|
|
938
|
+
</DialogModal>
|
|
939
|
+
|
|
940
|
+
{/* Small bottom-right notification */}
|
|
941
|
+
<DialogModal size="sm" position="bottom-right" dialogTitle="Notice" btnLabel="Show">
|
|
942
|
+
<p>Corner notification</p>
|
|
943
|
+
</DialogModal>
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
## Performance Tips
|
|
947
|
+
|
|
841
948
|
### Use Native Dialog Methods
|
|
842
949
|
|
|
843
950
|
Prefer `.showModal()` over custom implementations:
|
|
@@ -38,6 +38,18 @@ const meta: Meta<typeof DialogModal> = {
|
|
|
38
38
|
isOpen: false,
|
|
39
39
|
onClose: () => {},
|
|
40
40
|
},
|
|
41
|
+
argTypes: {
|
|
42
|
+
size: {
|
|
43
|
+
control: "select",
|
|
44
|
+
options: ["sm", "md", "lg", "full"],
|
|
45
|
+
description: "Size variant controlling dialog dimensions",
|
|
46
|
+
},
|
|
47
|
+
position: {
|
|
48
|
+
control: "select",
|
|
49
|
+
options: ["center", "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"],
|
|
50
|
+
description: "Position of the dialog on screen",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
41
53
|
} as Meta;
|
|
42
54
|
|
|
43
55
|
export default meta;
|
|
@@ -49,9 +61,14 @@ export const Default: Story = {
|
|
|
49
61
|
"DialogModal is a modal dialog component that provides an accessible overlay for displaying content.",
|
|
50
62
|
},
|
|
51
63
|
decorators: [WithInstructions()],
|
|
52
|
-
play: async ({ canvasElement }) => {
|
|
64
|
+
play: async ({ canvasElement, step }) => {
|
|
53
65
|
const canvas = within(canvasElement);
|
|
54
|
-
|
|
66
|
+
|
|
67
|
+
await step("Dialog renders with default center position", async () => {
|
|
68
|
+
const dialog = canvas.getByRole("dialog");
|
|
69
|
+
expect(dialog).toBeInTheDocument();
|
|
70
|
+
expect(dialog).toHaveAttribute("data-position", "center");
|
|
71
|
+
});
|
|
55
72
|
},
|
|
56
73
|
} as Story;
|
|
57
74
|
|
|
@@ -181,3 +198,91 @@ export const IconTriggerWithOutlineVariant: Story = {
|
|
|
181
198
|
});
|
|
182
199
|
},
|
|
183
200
|
} as Story;
|
|
201
|
+
|
|
202
|
+
/* ── Size variant stories ──────────────────── */
|
|
203
|
+
|
|
204
|
+
export const SmallDialog: Story = {
|
|
205
|
+
args: {
|
|
206
|
+
dialogTitle: "Small Dialog",
|
|
207
|
+
btnLabel: "Open Small",
|
|
208
|
+
size: "sm",
|
|
209
|
+
children: "This is a small (25rem) dialog.",
|
|
210
|
+
},
|
|
211
|
+
} as Story;
|
|
212
|
+
|
|
213
|
+
export const MediumDialog: Story = {
|
|
214
|
+
args: {
|
|
215
|
+
dialogTitle: "Medium Dialog",
|
|
216
|
+
btnLabel: "Open Medium",
|
|
217
|
+
size: "md",
|
|
218
|
+
children: "This is a medium (32rem) dialog for forms and standard content.",
|
|
219
|
+
},
|
|
220
|
+
} as Story;
|
|
221
|
+
|
|
222
|
+
export const LargeDialog: Story = {
|
|
223
|
+
args: {
|
|
224
|
+
dialogTitle: "Large Dialog",
|
|
225
|
+
btnLabel: "Open Large",
|
|
226
|
+
size: "lg",
|
|
227
|
+
children: "This is a large (48rem) dialog with more room for complex content.",
|
|
228
|
+
},
|
|
229
|
+
} as Story;
|
|
230
|
+
|
|
231
|
+
export const FullScreenDialog: Story = {
|
|
232
|
+
args: {
|
|
233
|
+
dialogTitle: "Full Screen Dialog",
|
|
234
|
+
btnLabel: "Open Full Screen",
|
|
235
|
+
size: "full",
|
|
236
|
+
children: "This dialog takes up the entire viewport.",
|
|
237
|
+
},
|
|
238
|
+
} as Story;
|
|
239
|
+
|
|
240
|
+
/* ── Position variant stories ──────────────── */
|
|
241
|
+
|
|
242
|
+
export const TopPositioned: Story = {
|
|
243
|
+
args: {
|
|
244
|
+
dialogTitle: "Top Dialog",
|
|
245
|
+
btnLabel: "Open Top",
|
|
246
|
+
position: "top",
|
|
247
|
+
children: "This dialog is positioned at the top center of the viewport.",
|
|
248
|
+
},
|
|
249
|
+
} as Story;
|
|
250
|
+
|
|
251
|
+
export const BottomSheet: Story = {
|
|
252
|
+
args: {
|
|
253
|
+
dialogTitle: "Bottom Sheet",
|
|
254
|
+
btnLabel: "Open Bottom Sheet",
|
|
255
|
+
position: "bottom",
|
|
256
|
+
children: "This dialog is positioned at the bottom, like a mobile bottom sheet.",
|
|
257
|
+
},
|
|
258
|
+
} as Story;
|
|
259
|
+
|
|
260
|
+
export const RightDrawer: Story = {
|
|
261
|
+
args: {
|
|
262
|
+
dialogTitle: "Right Drawer",
|
|
263
|
+
btnLabel: "Open Right Drawer",
|
|
264
|
+
size: "sm",
|
|
265
|
+
position: "right",
|
|
266
|
+
children: "This dialog slides in from the right as a full-height drawer panel.",
|
|
267
|
+
},
|
|
268
|
+
} as Story;
|
|
269
|
+
|
|
270
|
+
export const LeftDrawer: Story = {
|
|
271
|
+
args: {
|
|
272
|
+
dialogTitle: "Left Drawer",
|
|
273
|
+
btnLabel: "Open Left Drawer",
|
|
274
|
+
size: "sm",
|
|
275
|
+
position: "left",
|
|
276
|
+
children: "This dialog slides in from the left as a full-height drawer panel.",
|
|
277
|
+
},
|
|
278
|
+
} as Story;
|
|
279
|
+
|
|
280
|
+
export const BottomRightPositioned: Story = {
|
|
281
|
+
args: {
|
|
282
|
+
dialogTitle: "Bottom Right",
|
|
283
|
+
btnLabel: "Open Bottom Right",
|
|
284
|
+
size: "sm",
|
|
285
|
+
position: "bottom-right",
|
|
286
|
+
children: "This dialog appears in the bottom-right corner.",
|
|
287
|
+
},
|
|
288
|
+
} as Story;
|
|
@@ -81,6 +81,9 @@ export const DialogModal: React.FC<DialogModalProps> = ({
|
|
|
81
81
|
hideFooter = false,
|
|
82
82
|
btnProps,
|
|
83
83
|
icon,
|
|
84
|
+
size,
|
|
85
|
+
position,
|
|
86
|
+
closeIconSize,
|
|
84
87
|
}) => {
|
|
85
88
|
const [isOpen, setIsOpen] = useState(false);
|
|
86
89
|
const lastFocusedElement = useRef<HTMLElement | null>(null);
|
|
@@ -150,6 +153,9 @@ export const DialogModal: React.FC<DialogModalProps> = ({
|
|
|
150
153
|
confirmLabel={confirmLabel}
|
|
151
154
|
cancelLabel={cancelLabel}
|
|
152
155
|
hideFooter={hideFooter}
|
|
156
|
+
size={size}
|
|
157
|
+
position={position}
|
|
158
|
+
closeIconSize={closeIconSize}
|
|
153
159
|
>
|
|
154
160
|
{children}
|
|
155
161
|
</Dialog>
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
:root {
|
|
2
2
|
--dialog-min-width: max(20rem, 80%);
|
|
3
|
+
--dialog-width: var(--dialog-min-width);
|
|
4
|
+
--dialog-max-width: 90vw;
|
|
5
|
+
--dialog-max-height: 85vh;
|
|
6
|
+
--dialog-margin: auto;
|
|
7
|
+
--dialog-inset: 0;
|
|
3
8
|
--dialog-gap: 0.625rem;
|
|
4
9
|
--dialog-border-color: var(--color-border);
|
|
5
10
|
--dialog-border-width: thin;
|
|
@@ -33,8 +38,11 @@
|
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
dialog {
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
margin: var(--dialog-margin);
|
|
42
|
+
inset: var(--dialog-inset);
|
|
43
|
+
width: var(--dialog-width);
|
|
44
|
+
max-width: var(--dialog-max-width);
|
|
45
|
+
max-height: var(--dialog-max-height);
|
|
38
46
|
gap: var(--dialog-gap);
|
|
39
47
|
border: var(--dialog-border-color) var(--dialog-border-width) solid;
|
|
40
48
|
border-radius: var(--dialog-border-radius);
|
|
@@ -125,3 +133,82 @@ dialog {
|
|
|
125
133
|
outline: none;
|
|
126
134
|
}
|
|
127
135
|
}
|
|
136
|
+
|
|
137
|
+
/* ── Size variants ─────────────────────────── */
|
|
138
|
+
|
|
139
|
+
dialog[data-size="sm"] {
|
|
140
|
+
--dialog-width: 25rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
dialog[data-size="md"] {
|
|
144
|
+
--dialog-width: 32rem;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
dialog[data-size="lg"] {
|
|
148
|
+
--dialog-width: 48rem;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
dialog[data-size="full"] {
|
|
152
|
+
box-sizing: border-box;
|
|
153
|
+
width: 100vw;
|
|
154
|
+
max-width: 100vw;
|
|
155
|
+
height: 100vh;
|
|
156
|
+
max-height: 100vh;
|
|
157
|
+
margin: 0;
|
|
158
|
+
inset: 0;
|
|
159
|
+
border: 0;
|
|
160
|
+
border-radius: 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* ── Position variants ─────────────────────── */
|
|
164
|
+
|
|
165
|
+
dialog[data-position="center"] {
|
|
166
|
+
--dialog-margin: auto;
|
|
167
|
+
--dialog-inset: 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
dialog[data-position="top"] {
|
|
171
|
+
--dialog-margin: 0 auto auto auto;
|
|
172
|
+
--dialog-inset: 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
dialog[data-position="bottom"] {
|
|
176
|
+
--dialog-margin: auto auto 0 auto;
|
|
177
|
+
--dialog-inset: 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
dialog[data-position="left"] {
|
|
181
|
+
--dialog-margin: 0 auto 0 0;
|
|
182
|
+
--dialog-inset: 0;
|
|
183
|
+
height: 100vh;
|
|
184
|
+
max-height: 100vh;
|
|
185
|
+
border-radius: 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
dialog[data-position="right"] {
|
|
189
|
+
--dialog-margin: 0 0 0 auto;
|
|
190
|
+
--dialog-inset: 0;
|
|
191
|
+
height: 100vh;
|
|
192
|
+
max-height: 100vh;
|
|
193
|
+
border-radius: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
dialog[data-position="top-left"] {
|
|
197
|
+
--dialog-margin: 0 auto auto 0;
|
|
198
|
+
--dialog-inset: 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
dialog[data-position="top-right"] {
|
|
202
|
+
--dialog-margin: 0 0 auto auto;
|
|
203
|
+
--dialog-inset: 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
dialog[data-position="bottom-left"] {
|
|
207
|
+
--dialog-margin: auto auto 0 0;
|
|
208
|
+
--dialog-inset: 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
dialog[data-position="bottom-right"] {
|
|
212
|
+
--dialog-margin: auto 0 0 auto;
|
|
213
|
+
--dialog-inset: 0;
|
|
214
|
+
}
|
|
@@ -41,6 +41,18 @@ const meta: Meta<typeof Dialog> = {
|
|
|
41
41
|
args: {
|
|
42
42
|
children: content,
|
|
43
43
|
},
|
|
44
|
+
argTypes: {
|
|
45
|
+
size: {
|
|
46
|
+
control: "select",
|
|
47
|
+
options: ["sm", "md", "lg", "full"],
|
|
48
|
+
description: "Size variant controlling dialog dimensions",
|
|
49
|
+
},
|
|
50
|
+
position: {
|
|
51
|
+
control: "select",
|
|
52
|
+
options: ["center", "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"],
|
|
53
|
+
description: "Position of the dialog on screen (defaults to center)",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
44
56
|
decorators: [
|
|
45
57
|
(Story) => {
|
|
46
58
|
return (
|
|
@@ -71,6 +83,15 @@ export const BasicDialog: Story = {
|
|
|
71
83
|
onOpenChange: () => {},
|
|
72
84
|
dialogTitle: "Basic Dialog",
|
|
73
85
|
},
|
|
86
|
+
play: async ({ canvasElement, step }) => {
|
|
87
|
+
const canvas = within(canvasElement);
|
|
88
|
+
|
|
89
|
+
await step("Dialog defaults to center position", async () => {
|
|
90
|
+
const dialog = canvas.getByRole("dialog");
|
|
91
|
+
await expect(dialog).toBeInTheDocument();
|
|
92
|
+
await expect(dialog).toHaveAttribute("data-position", "center");
|
|
93
|
+
});
|
|
94
|
+
},
|
|
74
95
|
} as Story;
|
|
75
96
|
|
|
76
97
|
/**
|
|
@@ -84,9 +105,14 @@ export const NonModalDialog: Story = {
|
|
|
84
105
|
isAlertDialog: true,
|
|
85
106
|
dialogTitle: "Non Modal Dialog",
|
|
86
107
|
},
|
|
87
|
-
play: async ({ canvasElement }) => {
|
|
108
|
+
play: async ({ canvasElement, step }) => {
|
|
88
109
|
const canvas = within(canvasElement);
|
|
89
|
-
|
|
110
|
+
|
|
111
|
+
await step("Alert dialog renders with default center position", async () => {
|
|
112
|
+
const dialog = canvas.getByRole("alertdialog");
|
|
113
|
+
await expect(dialog).toBeInTheDocument();
|
|
114
|
+
await expect(dialog).toHaveAttribute("data-position", "center");
|
|
115
|
+
});
|
|
90
116
|
},
|
|
91
117
|
} as Story;
|
|
92
118
|
|
|
@@ -106,8 +132,9 @@ export const DialogInteractions: Story = {
|
|
|
106
132
|
const dialog = canvas.getByRole("dialog");
|
|
107
133
|
const closeButton = canvas.getByRole("button", { name: /close dialog/i });
|
|
108
134
|
|
|
109
|
-
await step("Modal is rendered", async () => {
|
|
135
|
+
await step("Modal is rendered with default center position", async () => {
|
|
110
136
|
await expect(dialog).toBeInTheDocument();
|
|
137
|
+
await expect(dialog).toHaveAttribute("data-position", "center");
|
|
111
138
|
await expect(closeButton).toBeInTheDocument();
|
|
112
139
|
});
|
|
113
140
|
|