@marianmeres/stuic 2.61.0 → 2.62.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/actions/popover/popover.svelte.js +18 -4
- package/dist/actions/tooltip/tooltip.svelte.js +15 -11
- package/dist/components/AssetsPreview/AssetsPreview.svelte +15 -17
- package/dist/components/AssetsPreview/AssetsPreview.svelte.d.ts +2 -4
- package/dist/components/CommandMenu/CommandMenu.svelte +145 -147
- package/dist/components/CommandMenu/README.md +6 -0
- package/dist/components/DropdownMenu/README.md +2 -2
- package/dist/components/Input/FieldOptions.svelte +255 -252
- package/dist/components/Input/FieldOptions.svelte.d.ts +2 -2
- package/dist/components/Modal/Modal.svelte +37 -48
- package/dist/components/Modal/Modal.svelte.d.ts +3 -13
- package/dist/components/Modal/README.md +17 -6
- package/dist/components/ModalDialog/ModalDialog.svelte +50 -13
- package/dist/components/ModalDialog/ModalDialog.svelte.d.ts +9 -0
- package/dist/components/ModalDialog/README.md +27 -8
- package/dist/components/Notifications/Notifications.svelte +135 -96
- package/dist/components/Notifications/notifications-icons.js +4 -4
- package/dist/components/X/X.svelte +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
|
-
import type { FocusTrapOptions } from "../../actions/focus-trap.js";
|
|
4
3
|
|
|
5
4
|
export interface Props {
|
|
6
5
|
visible?: boolean;
|
|
7
6
|
children: Snippet;
|
|
8
7
|
header?: Snippet;
|
|
9
8
|
footer?: Snippet;
|
|
10
|
-
/** Classes for the backdrop overlay element */
|
|
11
|
-
classBackdrop?: string;
|
|
12
9
|
/** Classes for the inner container (constrains content width) */
|
|
13
10
|
classInner?: string;
|
|
14
11
|
class?: string;
|
|
@@ -19,34 +16,23 @@
|
|
|
19
16
|
labelledby?: string;
|
|
20
17
|
/** ID reference for aria-describedby */
|
|
21
18
|
describedby?: string;
|
|
22
|
-
/** Transition duration in ms (respects prefers-reduced-motion) */
|
|
23
|
-
transitionDuration?: number;
|
|
24
|
-
elBackdrop?: HTMLDivElement;
|
|
25
19
|
el?: HTMLDivElement;
|
|
26
|
-
/** Enable focus trap, or pass options to customize behavior */
|
|
27
|
-
focusTrap?: boolean | FocusTrapOptions;
|
|
28
20
|
/** Called when Escape key is pressed while modal is open */
|
|
29
|
-
onEscape?:
|
|
21
|
+
onEscape?: () => void;
|
|
30
22
|
/** Disable body scroll lock when modal is open */
|
|
31
23
|
noScrollLock?: boolean;
|
|
32
|
-
/** Fires when the backdrop is clicked "directly" */
|
|
33
|
-
onBackdropClick?: undefined | (() => void);
|
|
34
24
|
}
|
|
35
25
|
</script>
|
|
36
26
|
|
|
37
27
|
<script lang="ts">
|
|
38
|
-
import
|
|
39
|
-
import { prefersReducedMotion } from "../../utils/prefers-reduced-motion.svelte.js";
|
|
28
|
+
import ModalDialog from "../ModalDialog/ModalDialog.svelte";
|
|
40
29
|
import { twMerge } from "../../utils/tw-merge.js";
|
|
41
30
|
|
|
42
|
-
const prefersReduced = prefersReducedMotion();
|
|
43
|
-
|
|
44
31
|
let {
|
|
45
32
|
visible = $bindable(false),
|
|
46
33
|
children,
|
|
47
34
|
header,
|
|
48
35
|
footer,
|
|
49
|
-
classBackdrop,
|
|
50
36
|
classInner,
|
|
51
37
|
class: classProp,
|
|
52
38
|
classHeader,
|
|
@@ -54,61 +40,64 @@
|
|
|
54
40
|
classFooter,
|
|
55
41
|
labelledby,
|
|
56
42
|
describedby,
|
|
57
|
-
transitionDuration = 100,
|
|
58
|
-
// transitionEnabled = true,
|
|
59
|
-
elBackdrop = $bindable(),
|
|
60
43
|
el = $bindable(),
|
|
61
|
-
focusTrap = true,
|
|
62
44
|
onEscape,
|
|
63
45
|
noScrollLock = false,
|
|
64
|
-
onBackdropClick,
|
|
65
46
|
}: Props = $props();
|
|
66
47
|
|
|
67
|
-
let
|
|
48
|
+
let modalDialog: ModalDialog = $state()!;
|
|
68
49
|
|
|
69
50
|
export function close() {
|
|
70
|
-
|
|
51
|
+
modalDialog.close();
|
|
71
52
|
}
|
|
72
53
|
|
|
73
54
|
export function open(openerOrEvent?: null | HTMLElement | MouseEvent) {
|
|
74
|
-
|
|
55
|
+
modalDialog.open(openerOrEvent);
|
|
56
|
+
visible = true;
|
|
75
57
|
}
|
|
76
58
|
|
|
77
59
|
export function setOpener(opener?: null | HTMLElement) {
|
|
78
|
-
|
|
60
|
+
modalDialog.setOpener(opener);
|
|
79
61
|
}
|
|
80
62
|
|
|
81
63
|
export function visibility() {
|
|
82
|
-
return
|
|
64
|
+
return modalDialog.visibility();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Sync visible prop with ModalDialog - when visible becomes true externally, open the dialog
|
|
68
|
+
$effect(() => {
|
|
69
|
+
if (visible && !modalDialog?.visibility().visible) {
|
|
70
|
+
modalDialog?.open();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
function handlePreClose() {
|
|
75
|
+
visible = false;
|
|
76
|
+
// return undefined to allow close
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function handlePreEscapeClose() {
|
|
80
|
+
onEscape?.();
|
|
81
|
+
// return undefined to allow close (preClose will set visible = false)
|
|
83
82
|
}
|
|
84
83
|
</script>
|
|
85
84
|
|
|
86
|
-
<
|
|
87
|
-
bind:this={
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
class={twMerge(
|
|
91
|
-
// "justify-center items-center bg-black/25 p-2 sm:p-4 md:p-[10vh] transition-all",
|
|
92
|
-
"justify-center items-center bg-black/25 transition-all",
|
|
93
|
-
"md:p-[10vh]",
|
|
94
|
-
classBackdrop
|
|
95
|
-
)}
|
|
96
|
-
{focusTrap}
|
|
97
|
-
fadeOutDuration={transitionDuration}
|
|
98
|
-
{onEscape}
|
|
85
|
+
<ModalDialog
|
|
86
|
+
bind:this={modalDialog}
|
|
87
|
+
ariaLabelledby={labelledby}
|
|
88
|
+
ariaDescribedby={describedby}
|
|
99
89
|
{noScrollLock}
|
|
100
|
-
{
|
|
90
|
+
preEscapeClose={handlePreEscapeClose}
|
|
91
|
+
preClose={handlePreClose}
|
|
92
|
+
class="bg-transparent size-full md:size-auto pointer-events-none"
|
|
101
93
|
>
|
|
102
94
|
<div
|
|
103
95
|
bind:this={el}
|
|
104
|
-
role="dialog"
|
|
105
|
-
aria-modal="true"
|
|
106
|
-
aria-labelledby={labelledby}
|
|
107
|
-
aria-describedby={describedby}
|
|
108
96
|
class={twMerge(
|
|
109
97
|
"overflow-x-hidden overflow-y-hidden flex flex-col",
|
|
110
|
-
"w-full max-w-
|
|
111
|
-
"h-
|
|
98
|
+
"w-full md:w-3xl md:max-w-[calc(100vw-2rem)]",
|
|
99
|
+
"h-full md:h-auto md:min-h-48 md:max-h-[80dvh]",
|
|
100
|
+
"pointer-events-auto",
|
|
112
101
|
classInner
|
|
113
102
|
)}
|
|
114
103
|
>
|
|
@@ -117,7 +106,7 @@
|
|
|
117
106
|
"bg-white dark:bg-neutral-800",
|
|
118
107
|
"flex flex-col overflow-hidden",
|
|
119
108
|
"rounded-none md:rounded-md",
|
|
120
|
-
"w-full flex-1
|
|
109
|
+
"w-full flex-1",
|
|
121
110
|
classProp
|
|
122
111
|
)}
|
|
123
112
|
>
|
|
@@ -136,7 +125,7 @@
|
|
|
136
125
|
{/if}
|
|
137
126
|
</div>
|
|
138
127
|
</div>
|
|
139
|
-
</
|
|
128
|
+
</ModalDialog>
|
|
140
129
|
|
|
141
130
|
<style>
|
|
142
131
|
.main {
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
|
-
import type { FocusTrapOptions } from "../../actions/focus-trap.js";
|
|
3
2
|
export interface Props {
|
|
4
3
|
visible?: boolean;
|
|
5
4
|
children: Snippet;
|
|
6
5
|
header?: Snippet;
|
|
7
6
|
footer?: Snippet;
|
|
8
|
-
/** Classes for the backdrop overlay element */
|
|
9
|
-
classBackdrop?: string;
|
|
10
7
|
/** Classes for the inner container (constrains content width) */
|
|
11
8
|
classInner?: string;
|
|
12
9
|
class?: string;
|
|
@@ -17,26 +14,19 @@ export interface Props {
|
|
|
17
14
|
labelledby?: string;
|
|
18
15
|
/** ID reference for aria-describedby */
|
|
19
16
|
describedby?: string;
|
|
20
|
-
/** Transition duration in ms (respects prefers-reduced-motion) */
|
|
21
|
-
transitionDuration?: number;
|
|
22
|
-
elBackdrop?: HTMLDivElement;
|
|
23
17
|
el?: HTMLDivElement;
|
|
24
|
-
/** Enable focus trap, or pass options to customize behavior */
|
|
25
|
-
focusTrap?: boolean | FocusTrapOptions;
|
|
26
18
|
/** Called when Escape key is pressed while modal is open */
|
|
27
|
-
onEscape?:
|
|
19
|
+
onEscape?: () => void;
|
|
28
20
|
/** Disable body scroll lock when modal is open */
|
|
29
21
|
noScrollLock?: boolean;
|
|
30
|
-
/** Fires when the backdrop is clicked "directly" */
|
|
31
|
-
onBackdropClick?: undefined | (() => void);
|
|
32
22
|
}
|
|
33
23
|
declare const Modal: import("svelte").Component<Props, {
|
|
34
24
|
close: () => void;
|
|
35
25
|
open: (openerOrEvent?: null | HTMLElement | MouseEvent) => void;
|
|
36
26
|
setOpener: (opener?: null | HTMLElement) => void;
|
|
37
27
|
visibility: () => {
|
|
38
|
-
readonly visible: boolean;
|
|
28
|
+
readonly visible: boolean | undefined;
|
|
39
29
|
};
|
|
40
|
-
}, "el" | "visible"
|
|
30
|
+
}, "el" | "visible">;
|
|
41
31
|
type Modal = ReturnType<typeof Modal>;
|
|
42
32
|
export default Modal;
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
# Modal
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A styled modal dialog with optional header and footer sections. Built on top of `ModalDialog` (native `<dialog>` element) with focus trap and scroll locking.
|
|
4
4
|
|
|
5
5
|
## Props
|
|
6
6
|
|
|
7
7
|
| Prop | Type | Default | Description |
|
|
8
8
|
|------|------|---------|-------------|
|
|
9
9
|
| `visible` | `boolean` | `false` | Controls visibility (bindable) |
|
|
10
|
-
| `focusTrap` | `boolean \| FocusTrapOptions` | `true` | Enable focus trapping |
|
|
11
|
-
| `transitionDuration` | `number` | `100` | Fade transition duration (ms) |
|
|
12
10
|
| `onEscape` | `() => void` | - | Callback on Escape key |
|
|
13
11
|
| `noScrollLock` | `boolean` | `false` | Disable body scroll lock |
|
|
14
|
-
| `classBackdrop` | `string` | - | CSS for backdrop overlay |
|
|
15
12
|
| `classInner` | `string` | - | CSS for inner width container |
|
|
16
13
|
| `class` | `string` | - | CSS for modal box |
|
|
17
14
|
| `classHeader` | `string` | - | CSS for header section |
|
|
@@ -20,7 +17,6 @@ A centered modal dialog with optional header and footer sections. Built on top o
|
|
|
20
17
|
| `labelledby` | `string` | - | ARIA labelledby ID |
|
|
21
18
|
| `describedby` | `string` | - | ARIA describedby ID |
|
|
22
19
|
| `el` | `HTMLDivElement` | - | Modal element reference (bindable) |
|
|
23
|
-
| `elBackdrop` | `HTMLDivElement` | - | Backdrop element reference (bindable) |
|
|
24
20
|
|
|
25
21
|
## Snippets
|
|
26
22
|
|
|
@@ -109,9 +105,24 @@ A centered modal dialog with optional header and footer sections. Built on top o
|
|
|
109
105
|
```svelte
|
|
110
106
|
<Modal
|
|
111
107
|
bind:this={modal}
|
|
112
|
-
classInner="
|
|
108
|
+
classInner="md:w-lg"
|
|
113
109
|
class="rounded-lg"
|
|
114
110
|
>
|
|
115
111
|
<div class="p-6">Smaller modal</div>
|
|
116
112
|
</Modal>
|
|
117
113
|
```
|
|
114
|
+
|
|
115
|
+
## Responsive Behavior
|
|
116
|
+
|
|
117
|
+
By default, Modal is:
|
|
118
|
+
- **Mobile**: Full screen with 1rem margins from viewport edges
|
|
119
|
+
- **Desktop (md+)**: Centered, max-width 768px, auto height with max 80vh
|
|
120
|
+
|
|
121
|
+
## Relationship to ModalDialog
|
|
122
|
+
|
|
123
|
+
Modal is a higher-level component built on `ModalDialog`. It provides:
|
|
124
|
+
- Pre-styled header/main/footer layout
|
|
125
|
+
- Responsive sizing (fullscreen mobile, centered desktop)
|
|
126
|
+
- Conventional styling (background, rounded corners, etc.)
|
|
127
|
+
|
|
128
|
+
Use `ModalDialog` directly when you need full control over the content layout.
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
preEscapeClose?: () => any;
|
|
14
14
|
/** Pre-close hook. Return false to prevent close. */
|
|
15
15
|
preClose?: () => any;
|
|
16
|
+
/** ID reference for aria-labelledby */
|
|
17
|
+
ariaLabelledby?: string;
|
|
18
|
+
/** ID reference for aria-describedby */
|
|
19
|
+
ariaDescribedby?: string;
|
|
20
|
+
/** Disable body scroll lock when dialog is open */
|
|
21
|
+
noScrollLock?: boolean;
|
|
16
22
|
}
|
|
17
23
|
</script>
|
|
18
24
|
|
|
@@ -37,6 +43,9 @@
|
|
|
37
43
|
noEscapeClose,
|
|
38
44
|
preEscapeClose,
|
|
39
45
|
preClose,
|
|
46
|
+
ariaLabelledby,
|
|
47
|
+
ariaDescribedby,
|
|
48
|
+
noScrollLock,
|
|
40
49
|
}: Props = $props();
|
|
41
50
|
|
|
42
51
|
// important to start as undefined (because of scroll save/restore)
|
|
@@ -45,30 +54,42 @@
|
|
|
45
54
|
let dialog = $state<HTMLDialogElement>()!;
|
|
46
55
|
let box = $state<HTMLDivElement>()!;
|
|
47
56
|
let _opener: undefined | null | HTMLElement = $state();
|
|
57
|
+
let _isClosing = false;
|
|
48
58
|
|
|
49
59
|
export function open(openerOrEvent?: null | HTMLElement | MouseEvent) {
|
|
60
|
+
if (visible) return; // Already open
|
|
50
61
|
visible = true;
|
|
51
62
|
setOpener(
|
|
52
|
-
|
|
63
|
+
openerOrEvent instanceof MouseEvent
|
|
64
|
+
? (openerOrEvent.currentTarget as HTMLElement)
|
|
65
|
+
: openerOrEvent ?? (document.activeElement as HTMLElement)
|
|
53
66
|
);
|
|
54
|
-
// clog("will showModal");
|
|
55
67
|
// dialog must be rendered in the DOM before it can be opened...
|
|
56
68
|
waitForNextRepaint().then(() => {
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
try {
|
|
70
|
+
dialog?.showModal();
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.error("ModalDialog: Failed to open dialog:", e);
|
|
73
|
+
visible = false;
|
|
74
|
+
}
|
|
59
75
|
});
|
|
60
76
|
}
|
|
61
77
|
|
|
62
78
|
export function close() {
|
|
79
|
+
if (_isClosing || !visible) return;
|
|
80
|
+
_isClosing = true;
|
|
63
81
|
(async () => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
try {
|
|
83
|
+
const allowed = await preClose?.();
|
|
84
|
+
// explicit false prevents close
|
|
85
|
+
if (allowed !== false) {
|
|
86
|
+
dialog?.close();
|
|
87
|
+
visible = false;
|
|
88
|
+
_opener?.focus();
|
|
89
|
+
_opener = null;
|
|
90
|
+
}
|
|
91
|
+
} finally {
|
|
92
|
+
_isClosing = false;
|
|
72
93
|
}
|
|
73
94
|
})();
|
|
74
95
|
}
|
|
@@ -77,6 +98,14 @@
|
|
|
77
98
|
_opener = opener;
|
|
78
99
|
}
|
|
79
100
|
|
|
101
|
+
export function visibility() {
|
|
102
|
+
return {
|
|
103
|
+
get visible() {
|
|
104
|
+
return visible;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
80
109
|
onClickOutside(
|
|
81
110
|
() => box,
|
|
82
111
|
() => !noClickOutsideClose && close()
|
|
@@ -84,7 +113,7 @@
|
|
|
84
113
|
|
|
85
114
|
$effect(() => {
|
|
86
115
|
// noop if we're undefined ($effect runs immediately as onMount)
|
|
87
|
-
if (visible === undefined) return;
|
|
116
|
+
if (visible === undefined || noScrollLock) return;
|
|
88
117
|
visible ? BodyScroll.lock() : BodyScroll.unlock();
|
|
89
118
|
});
|
|
90
119
|
|
|
@@ -101,6 +130,8 @@
|
|
|
101
130
|
bind:this={dialog}
|
|
102
131
|
use:focusTrap
|
|
103
132
|
data-type={type}
|
|
133
|
+
aria-labelledby={ariaLabelledby}
|
|
134
|
+
aria-describedby={ariaDescribedby}
|
|
104
135
|
class={twMerge(
|
|
105
136
|
"stuic-modal-dialog",
|
|
106
137
|
"fixed inset-4 m-auto size-auto",
|
|
@@ -109,6 +140,12 @@
|
|
|
109
140
|
"bg-transparent backdrop:bg-black/40",
|
|
110
141
|
classDialog
|
|
111
142
|
)}
|
|
143
|
+
onclick={(e) => {
|
|
144
|
+
// Close when clicking directly on the dialog (backdrop area), not its children
|
|
145
|
+
if (e.target === dialog && !noClickOutsideClose) {
|
|
146
|
+
close();
|
|
147
|
+
}
|
|
148
|
+
}}
|
|
112
149
|
onkeydown={async (e) => {
|
|
113
150
|
if (e.key === "Escape" && visible) {
|
|
114
151
|
// clog("on Escape keydown, preventing default and stopping propagation");
|
|
@@ -11,11 +11,20 @@ export interface Props {
|
|
|
11
11
|
preEscapeClose?: () => any;
|
|
12
12
|
/** Pre-close hook. Return false to prevent close. */
|
|
13
13
|
preClose?: () => any;
|
|
14
|
+
/** ID reference for aria-labelledby */
|
|
15
|
+
ariaLabelledby?: string;
|
|
16
|
+
/** ID reference for aria-describedby */
|
|
17
|
+
ariaDescribedby?: string;
|
|
18
|
+
/** Disable body scroll lock when dialog is open */
|
|
19
|
+
noScrollLock?: boolean;
|
|
14
20
|
}
|
|
15
21
|
declare const ModalDialog: import("svelte").Component<Props, {
|
|
16
22
|
open: (openerOrEvent?: null | HTMLElement | MouseEvent) => void;
|
|
17
23
|
close: () => void;
|
|
18
24
|
setOpener: (opener?: null | HTMLElement) => void;
|
|
25
|
+
visibility: () => {
|
|
26
|
+
readonly visible: boolean | undefined;
|
|
27
|
+
};
|
|
19
28
|
}, "">;
|
|
20
29
|
type ModalDialog = ReturnType<typeof ModalDialog>;
|
|
21
30
|
export default ModalDialog;
|
|
@@ -8,11 +8,14 @@ A modal component using the native HTML `<dialog>` element with `showModal()`. P
|
|
|
8
8
|
|------|------|---------|-------------|
|
|
9
9
|
| `noClickOutsideClose` | `boolean` | `false` | Disable close on outside click |
|
|
10
10
|
| `noEscapeClose` | `boolean` | `false` | Disable close on Escape key |
|
|
11
|
+
| `noScrollLock` | `boolean` | `false` | Disable body scroll lock |
|
|
11
12
|
| `preEscapeClose` | `() => any` | - | Hook before Escape close (return `false` to prevent) |
|
|
12
13
|
| `preClose` | `() => any` | - | Hook before any close (return `false` to prevent) |
|
|
13
14
|
| `type` | `string` | - | Optional UI hint (added as `data-type` attribute) |
|
|
14
15
|
| `class` | `string` | - | CSS for content box |
|
|
15
16
|
| `classDialog` | `string` | - | CSS for dialog element |
|
|
17
|
+
| `ariaLabelledby` | `string` | - | ID reference for aria-labelledby |
|
|
18
|
+
| `ariaDescribedby` | `string` | - | ID reference for aria-describedby |
|
|
16
19
|
|
|
17
20
|
## Methods
|
|
18
21
|
|
|
@@ -21,6 +24,7 @@ A modal component using the native HTML `<dialog>` element with `showModal()`. P
|
|
|
21
24
|
| `open(opener?)` | Open modal with `showModal()`, optionally track opener |
|
|
22
25
|
| `close()` | Close modal |
|
|
23
26
|
| `setOpener(el)` | Set element to refocus when closed |
|
|
27
|
+
| `visibility()` | Returns object with `visible` getter |
|
|
24
28
|
|
|
25
29
|
## Usage
|
|
26
30
|
|
|
@@ -92,12 +96,27 @@ A modal component using the native HTML `<dialog>` element with `showModal()`. P
|
|
|
92
96
|
</ModalDialog>
|
|
93
97
|
```
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
### Check Visibility State
|
|
96
100
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
```svelte
|
|
102
|
+
<script lang="ts">
|
|
103
|
+
let dialog: ModalDialog;
|
|
104
|
+
|
|
105
|
+
function logVisibility() {
|
|
106
|
+
console.log('Is visible:', dialog.visibility().visible);
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Relationship to Modal
|
|
112
|
+
|
|
113
|
+
`Modal` is a higher-level component built on top of `ModalDialog`.
|
|
114
|
+
|
|
115
|
+
| Feature | ModalDialog | Modal |
|
|
116
|
+
|---------|-------------|-------|
|
|
117
|
+
| Layout | Raw content | Header/main/footer structure |
|
|
118
|
+
| Styling | Minimal | Pre-styled box with backgrounds |
|
|
119
|
+
| Sizing | Manual | Responsive (fullscreen mobile, centered desktop) |
|
|
120
|
+
| Use case | Full control | Quick conventional modals |
|
|
121
|
+
|
|
122
|
+
Use `ModalDialog` when you need complete control over the modal content and styling. Use `Modal` for conventional modal dialogs with header/footer sections.
|