universal-modal-monorepo 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.
Files changed (106) hide show
  1. package/AUTHORS +5 -0
  2. package/CHANGELOG.md +10 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE +6 -0
  5. package/README.md +239 -0
  6. package/SECURITY.md +21 -0
  7. package/docs/ACCESSIBILITY.md +43 -0
  8. package/docs/API.md +56 -0
  9. package/docs/THEMING.md +65 -0
  10. package/examples/angular/app.component.html +137 -0
  11. package/examples/angular/app.component.ts +53 -0
  12. package/examples/angular/index.html +99 -0
  13. package/examples/angular/main.js +121 -0
  14. package/examples/angular/styles.css +25 -0
  15. package/examples/index.html +53 -0
  16. package/examples/react/App.tsx +242 -0
  17. package/examples/react/index.html +26 -0
  18. package/examples/react/main.js +144 -0
  19. package/examples/react/styles.css +1 -0
  20. package/examples/shared/gallery.css +177 -0
  21. package/examples/vanilla/index.html +89 -0
  22. package/examples/vanilla/main.js +174 -0
  23. package/examples/vanilla/styles.css +1 -0
  24. package/examples/vue/App.vue +190 -0
  25. package/examples/vue/index.html +23 -0
  26. package/examples/vue/main.js +174 -0
  27. package/examples/vue/styles.css +1 -0
  28. package/examples/web-component/index.html +190 -0
  29. package/examples/web-component/main.js +58 -0
  30. package/examples/web-component/styles.css +11 -0
  31. package/package.json +36 -0
  32. package/packages/universal-modal/AUTHORS +5 -0
  33. package/packages/universal-modal/CHANGELOG.md +10 -0
  34. package/packages/universal-modal/LICENSE +21 -0
  35. package/packages/universal-modal/NOTICE +6 -0
  36. package/packages/universal-modal/README.md +29 -0
  37. package/packages/universal-modal/package.json +83 -0
  38. package/packages/universal-modal/scripts/copy-styles.mjs +11 -0
  39. package/packages/universal-modal/src/core/constants.ts +50 -0
  40. package/packages/universal-modal/src/core/event-emitter.ts +46 -0
  41. package/packages/universal-modal/src/core/focus-trap.ts +101 -0
  42. package/packages/universal-modal/src/core/index.ts +39 -0
  43. package/packages/universal-modal/src/core/modal-engine.ts +410 -0
  44. package/packages/universal-modal/src/core/modal-stack.ts +68 -0
  45. package/packages/universal-modal/src/core/scroll-lock.ts +78 -0
  46. package/packages/universal-modal/src/core/theme.ts +50 -0
  47. package/packages/universal-modal/src/core/types.ts +196 -0
  48. package/packages/universal-modal/src/core/utils.ts +114 -0
  49. package/packages/universal-modal/src/dom/index.ts +1 -0
  50. package/packages/universal-modal/src/dom/modal-renderer.ts +191 -0
  51. package/packages/universal-modal/src/index.ts +2 -0
  52. package/packages/universal-modal/src/styles/modal.css +368 -0
  53. package/packages/universal-modal/tsconfig.json +24 -0
  54. package/packages/universal-modal/tsup.config.ts +12 -0
  55. package/packages/universal-modal-angular/AUTHORS +5 -0
  56. package/packages/universal-modal-angular/CHANGELOG.md +9 -0
  57. package/packages/universal-modal-angular/LICENSE +21 -0
  58. package/packages/universal-modal-angular/NOTICE +6 -0
  59. package/packages/universal-modal-angular/README.md +12 -0
  60. package/packages/universal-modal-angular/package.json +85 -0
  61. package/packages/universal-modal-angular/src/index.ts +10 -0
  62. package/packages/universal-modal-angular/src/universal-modal.component.ts +275 -0
  63. package/packages/universal-modal-angular/tsconfig.json +20 -0
  64. package/packages/universal-modal-angular/tsup.config.ts +10 -0
  65. package/packages/universal-modal-react/AUTHORS +5 -0
  66. package/packages/universal-modal-react/CHANGELOG.md +9 -0
  67. package/packages/universal-modal-react/LICENSE +21 -0
  68. package/packages/universal-modal-react/NOTICE +6 -0
  69. package/packages/universal-modal-react/README.md +12 -0
  70. package/packages/universal-modal-react/package.json +87 -0
  71. package/packages/universal-modal-react/src/UniversalModal.tsx +340 -0
  72. package/packages/universal-modal-react/src/index.ts +10 -0
  73. package/packages/universal-modal-react/tsconfig.json +19 -0
  74. package/packages/universal-modal-react/tsup.config.ts +13 -0
  75. package/packages/universal-modal-vanilla/AUTHORS +5 -0
  76. package/packages/universal-modal-vanilla/CHANGELOG.md +9 -0
  77. package/packages/universal-modal-vanilla/LICENSE +21 -0
  78. package/packages/universal-modal-vanilla/NOTICE +6 -0
  79. package/packages/universal-modal-vanilla/README.md +12 -0
  80. package/packages/universal-modal-vanilla/package.json +79 -0
  81. package/packages/universal-modal-vanilla/src/create-modal.ts +125 -0
  82. package/packages/universal-modal-vanilla/src/index.ts +14 -0
  83. package/packages/universal-modal-vanilla/tsconfig.json +18 -0
  84. package/packages/universal-modal-vanilla/tsup.config.ts +10 -0
  85. package/packages/universal-modal-vue/AUTHORS +5 -0
  86. package/packages/universal-modal-vue/CHANGELOG.md +9 -0
  87. package/packages/universal-modal-vue/LICENSE +21 -0
  88. package/packages/universal-modal-vue/NOTICE +6 -0
  89. package/packages/universal-modal-vue/README.md +12 -0
  90. package/packages/universal-modal-vue/package.json +83 -0
  91. package/packages/universal-modal-vue/src/UniversalModal.ts +342 -0
  92. package/packages/universal-modal-vue/src/index.ts +10 -0
  93. package/packages/universal-modal-vue/tsconfig.json +18 -0
  94. package/packages/universal-modal-vue/tsup.config.ts +10 -0
  95. package/packages/universal-modal-web-component/AUTHORS +5 -0
  96. package/packages/universal-modal-web-component/CHANGELOG.md +9 -0
  97. package/packages/universal-modal-web-component/LICENSE +21 -0
  98. package/packages/universal-modal-web-component/NOTICE +6 -0
  99. package/packages/universal-modal-web-component/README.md +12 -0
  100. package/packages/universal-modal-web-component/package.json +83 -0
  101. package/packages/universal-modal-web-component/src/index.ts +11 -0
  102. package/packages/universal-modal-web-component/src/universal-modal-element.ts +424 -0
  103. package/packages/universal-modal-web-component/tsconfig.json +18 -0
  104. package/packages/universal-modal-web-component/tsup.config.ts +10 -0
  105. package/scripts/copy-styles.mjs +11 -0
  106. package/scripts/serve-examples.mjs +48 -0
@@ -0,0 +1,99 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Universal Modal — Angular samples</title>
7
+ <link rel="stylesheet" href="../../packages/universal-modal/dist/styles.css" />
8
+ <link rel="stylesheet" href="./styles.css" />
9
+ </head>
10
+ <body data-um-theme="light">
11
+ <main class="demo-page">
12
+ <header class="demo-header">
13
+ <p class="demo-kicker">Angular</p>
14
+ <h1>Modal samples</h1>
15
+ <p>
16
+ Interactive demos mirror the Angular standalone component in
17
+ <code>app.component.ts</code> / <code>app.component.html</code>. Copy those files
18
+ into an Angular 16+ app and import
19
+ <code>@poluru-labs/universal-modal-angular</code>.
20
+ </p>
21
+ <nav class="demo-nav" aria-label="Adapters">
22
+ <a href="../index.html">All</a>
23
+ <a href="../vanilla/index.html">Vanilla</a>
24
+ <a href="../web-component/index.html">Web Component</a>
25
+ <a href="../react/index.html">React</a>
26
+ <a href="../vue/index.html">Vue</a>
27
+ <a href="./index.html" aria-current="page">Angular</a>
28
+ </nav>
29
+ </header>
30
+
31
+ <section class="demo-grid">
32
+ <article class="demo-card">
33
+ <h2>1. Archive confirm</h2>
34
+ <p>Alertdialog with async confirm handler.</p>
35
+ <button type="button" class="demo-btn" id="open-archive">Archive workspace</button>
36
+ </article>
37
+ <article class="demo-card">
38
+ <h2>2. Danger delete</h2>
39
+ <p>Custom red confirm theme.</p>
40
+ <button type="button" class="demo-btn" id="open-danger">Delete webhook</button>
41
+ </article>
42
+ <article class="demo-card">
43
+ <h2>3. Right drawer</h2>
44
+ <p>Large form panel.</p>
45
+ <button type="button" class="demo-btn" id="open-drawer">Edit workspace</button>
46
+ </article>
47
+ <article class="demo-card">
48
+ <h2>4. Top notice</h2>
49
+ <p>No footer actions.</p>
50
+ <button type="button" class="demo-btn" id="open-top">Show notice</button>
51
+ </article>
52
+ <article class="demo-card">
53
+ <h2>5. Fullscreen</h2>
54
+ <p>Checklist / walkthrough layout.</p>
55
+ <button type="button" class="demo-btn" id="open-fullscreen">Open checklist</button>
56
+ </article>
57
+ <article class="demo-card">
58
+ <h2>6. Persistent</h2>
59
+ <p>Blocks ESC and backdrop.</p>
60
+ <button type="button" class="demo-btn" id="open-persistent">Accept DPA</button>
61
+ </article>
62
+ <article class="demo-card">
63
+ <h2>7. Brand theme</h2>
64
+ <p>Per-dialog brand override.</p>
65
+ <button type="button" class="demo-btn" id="open-brand">Open branded</button>
66
+ </article>
67
+ </section>
68
+
69
+ <section class="code-note">
70
+ <h2>Angular source in this folder</h2>
71
+ <p class="demo-muted">
72
+ Use <code>app.component.ts</code> + <code>app.component.html</code> inside a real
73
+ Angular application. The live buttons below are powered by the vanilla adapter so
74
+ the gallery stays runnable without the Angular CLI.
75
+ </p>
76
+ <pre>import { UniversalModalComponent } from '@poluru-labs/universal-modal-angular';
77
+ import '@poluru-labs/universal-modal/styles.css';
78
+
79
+ @Component({
80
+ standalone: true,
81
+ imports: [UniversalModalComponent],
82
+ templateUrl: './app.component.html',
83
+ })
84
+ export class AppComponent {
85
+ sample: string | null = null;
86
+ }</pre>
87
+ </section>
88
+ </main>
89
+
90
+ <script type="importmap">
91
+ {
92
+ "imports": {
93
+ "@poluru-labs/universal-modal": "../../packages/universal-modal/dist/index.js"
94
+ }
95
+ }
96
+ </script>
97
+ <script type="module" src="./main.js"></script>
98
+ </body>
99
+ </html>
@@ -0,0 +1,121 @@
1
+ import { createModal } from '../../packages/universal-modal-vanilla/dist/index.js';
2
+
3
+ function openSample(factory) {
4
+ const modal = factory();
5
+ modal.on('afterClose', () => modal.destroy());
6
+ void modal.open();
7
+ }
8
+
9
+ document.getElementById('open-archive')?.addEventListener('click', (event) => {
10
+ openSample(() =>
11
+ createModal({
12
+ title: 'Archive workspace?',
13
+ description: 'Members keep read-only access.',
14
+ role: 'alertdialog',
15
+ confirmText: 'Archive',
16
+ body: '<label class="demo-field">Workspace <input value="Poluru Labs" /></label>',
17
+ returnFocus: event.currentTarget,
18
+ onConfirm: async () => {
19
+ await new Promise((r) => setTimeout(r, 700));
20
+ },
21
+ }),
22
+ );
23
+ });
24
+
25
+ document.getElementById('open-danger')?.addEventListener('click', (event) => {
26
+ openSample(() =>
27
+ createModal({
28
+ title: 'Delete webhook?',
29
+ body: '<p>Deliveries will stop immediately.</p>',
30
+ role: 'alertdialog',
31
+ confirmText: 'Delete',
32
+ returnFocus: event.currentTarget,
33
+ theme: {
34
+ confirmBg: '#b91c1c',
35
+ confirmFg: '#ffffff',
36
+ confirmBorder: '#b91c1c',
37
+ },
38
+ }),
39
+ );
40
+ });
41
+
42
+ document.getElementById('open-drawer')?.addEventListener('click', (event) => {
43
+ openSample(() =>
44
+ createModal({
45
+ title: 'Workspace settings',
46
+ size: 'lg',
47
+ placement: 'right',
48
+ confirmText: 'Save',
49
+ returnFocus: event.currentTarget,
50
+ body: `
51
+ <div class="demo-stack">
52
+ <label class="demo-field">Name <input value="Poluru Labs" /></label>
53
+ <label class="demo-field">Slug <input value="poluru-labs" /></label>
54
+ </div>
55
+ `,
56
+ }),
57
+ );
58
+ });
59
+
60
+ document.getElementById('open-top')?.addEventListener('click', (event) => {
61
+ openSample(() =>
62
+ createModal({
63
+ title: 'Deploy in progress',
64
+ placement: 'top',
65
+ hideActions: true,
66
+ body: '<p>You can keep browsing while the pipeline finishes.</p>',
67
+ returnFocus: event.currentTarget,
68
+ }),
69
+ );
70
+ });
71
+
72
+ document.getElementById('open-fullscreen')?.addEventListener('click', (event) => {
73
+ openSample(() =>
74
+ createModal({
75
+ title: 'Launch checklist',
76
+ size: 'fullscreen',
77
+ confirmText: 'Mark complete',
78
+ returnFocus: event.currentTarget,
79
+ body: `
80
+ <div class="demo-stack">
81
+ <p>✓ Domain verified</p>
82
+ <p>✓ Billing method added</p>
83
+ <p>✓ First project created</p>
84
+ </div>
85
+ `,
86
+ }),
87
+ );
88
+ });
89
+
90
+ document.getElementById('open-persistent')?.addEventListener('click', (event) => {
91
+ openSample(() =>
92
+ createModal({
93
+ title: 'Data processing agreement',
94
+ role: 'alertdialog',
95
+ persistent: true,
96
+ showCloseIcon: false,
97
+ confirmText: 'Accept',
98
+ cancelText: 'Decline',
99
+ body: '<p>Outside dismiss is disabled until you choose an action.</p>',
100
+ returnFocus: event.currentTarget,
101
+ }),
102
+ );
103
+ });
104
+
105
+ document.getElementById('open-brand')?.addEventListener('click', (event) => {
106
+ openSample(() =>
107
+ createModal({
108
+ title: 'Branded dialog',
109
+ description: 'theme input maps to CSS variables.',
110
+ confirmText: 'Apply',
111
+ body: '<p>Use this for one-off brand treatments.</p>',
112
+ returnFocus: event.currentTarget,
113
+ theme: {
114
+ brand: '#0d47a1',
115
+ brandForeground: '#ffffff',
116
+ confirmBg: '#0d47a1',
117
+ confirmFg: '#ffffff',
118
+ },
119
+ }),
120
+ );
121
+ });
@@ -0,0 +1,25 @@
1
+ @import url('../shared/gallery.css');
2
+
3
+ .code-note {
4
+ margin-top: 2rem;
5
+ padding: 1.2rem 1.3rem;
6
+ border-radius: 16px;
7
+ border: 1px solid var(--demo-border);
8
+ background: var(--demo-card);
9
+ }
10
+
11
+ .code-note h2 {
12
+ margin: 0 0 0.5rem;
13
+ font-size: 1.1rem;
14
+ }
15
+
16
+ .code-note pre {
17
+ margin: 0.75rem 0 0;
18
+ overflow: auto;
19
+ padding: 0.9rem 1rem;
20
+ border-radius: 12px;
21
+ background: #0f172a;
22
+ color: #e2e8f0;
23
+ font-size: 0.82rem;
24
+ line-height: 1.45;
25
+ }
@@ -0,0 +1,53 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Universal Modal — Examples</title>
7
+ <link rel="stylesheet" href="./shared/gallery.css" />
8
+ </head>
9
+ <body>
10
+ <main class="demo-page">
11
+ <header class="demo-header">
12
+ <p class="demo-kicker">@poluru-labs/universal-modal</p>
13
+ <h1>Example gallery</h1>
14
+ <p>
15
+ Browse runnable samples for each adapter. Each page includes multiple modal
16
+ variants — confirm, form, drawer, fullscreen, persistent, themed, and nested.
17
+ </p>
18
+ </header>
19
+
20
+ <section class="demo-grid">
21
+ <article class="demo-card">
22
+ <h2>Vanilla JS</h2>
23
+ <p>Imperative <code>createModal</code> / <code>confirmModal</code> samples.</p>
24
+ <a class="demo-btn" href="./vanilla/index.html">Open vanilla demos</a>
25
+ </article>
26
+
27
+ <article class="demo-card">
28
+ <h2>Web Components</h2>
29
+ <p>Declarative <code>&lt;universal-modal&gt;</code> with slots and nesting.</p>
30
+ <a class="demo-btn" href="./web-component/index.html">Open WC demos</a>
31
+ </article>
32
+
33
+ <article class="demo-card">
34
+ <h2>React</h2>
35
+ <p>Controlled modals with async confirm, drawers, and custom themes.</p>
36
+ <a class="demo-btn" href="./react/index.html">Open React demos</a>
37
+ </article>
38
+
39
+ <article class="demo-card">
40
+ <h2>Vue 3</h2>
41
+ <p><code>v-model:open</code> samples including alertdialog and placement.</p>
42
+ <a class="demo-btn" href="./vue/index.html">Open Vue demos</a>
43
+ </article>
44
+
45
+ <article class="demo-card">
46
+ <h2>Angular</h2>
47
+ <p>Standalone component patterns (source gallery + usage notes).</p>
48
+ <a class="demo-btn" href="./angular/index.html">Open Angular demos</a>
49
+ </article>
50
+ </section>
51
+ </main>
52
+ </body>
53
+ </html>
@@ -0,0 +1,242 @@
1
+ import { useState } from 'react';
2
+ import { UniversalModal, type ModalTheme } from '@poluru-labs/universal-modal-react';
3
+ import '@poluru-labs/universal-modal/styles.css';
4
+ import './styles.css';
5
+
6
+ type Sample =
7
+ | 'invite'
8
+ | 'danger'
9
+ | 'drawer'
10
+ | 'top'
11
+ | 'fullscreen'
12
+ | 'persistent'
13
+ | 'brand'
14
+ | null;
15
+
16
+ const brandTheme: ModalTheme = {
17
+ brand: '#0d47a1',
18
+ brandForeground: '#ffffff',
19
+ };
20
+
21
+ const dangerTheme: ModalTheme = {
22
+ confirmBg: '#b91c1c',
23
+ confirmFg: '#ffffff',
24
+ confirmBorder: '#b91c1c',
25
+ };
26
+
27
+ const mintTheme: ModalTheme = {
28
+ brand: '#0f766e',
29
+ brandForeground: '#ecfdf5',
30
+ confirmBg: '#0f766e',
31
+ confirmFg: '#ecfdf5',
32
+ confirmBorder: '#0f766e',
33
+ cancelBg: '#f0fdfa',
34
+ cancelFg: '#115e59',
35
+ cancelBorder: '#99f6e4',
36
+ };
37
+
38
+ export function App() {
39
+ const [sample, setSample] = useState<Sample>(null);
40
+ const [email, setEmail] = useState('');
41
+
42
+ const close = () => setSample(null);
43
+
44
+ return (
45
+ <main className="demo-page">
46
+ <header className="demo-header">
47
+ <p className="demo-kicker">React</p>
48
+ <h1>Modal samples</h1>
49
+ <p>
50
+ Controlled <code>UniversalModal</code> demos with different sizes, placements,
51
+ and themes.
52
+ </p>
53
+ <nav className="demo-nav" aria-label="Adapters">
54
+ <a href="../index.html">All</a>
55
+ <a href="../vanilla/index.html">Vanilla</a>
56
+ <a href="../web-component/index.html">Web Component</a>
57
+ <a href="./index.html" aria-current="page">
58
+ React
59
+ </a>
60
+ <a href="../vue/index.html">Vue</a>
61
+ <a href="../angular/index.html">Angular</a>
62
+ </nav>
63
+ </header>
64
+
65
+ <section className="demo-grid">
66
+ <article className="demo-card">
67
+ <h2>1. Invite form</h2>
68
+ <p>Centered dialog with async confirm.</p>
69
+ <button type="button" onClick={() => setSample('invite')}>
70
+ Invite teammate
71
+ </button>
72
+ </article>
73
+
74
+ <article className="demo-card">
75
+ <h2>2. Danger confirm</h2>
76
+ <p>Alertdialog with red confirm styling.</p>
77
+ <button type="button" onClick={() => setSample('danger')}>
78
+ Revoke token
79
+ </button>
80
+ </article>
81
+
82
+ <article className="demo-card">
83
+ <h2>3. Right drawer</h2>
84
+ <p>Large panel for multi-field editing.</p>
85
+ <button type="button" onClick={() => setSample('drawer')}>
86
+ Edit billing
87
+ </button>
88
+ </article>
89
+
90
+ <article className="demo-card">
91
+ <h2>4. Top notice</h2>
92
+ <p>Top placement, close icon only.</p>
93
+ <button type="button" onClick={() => setSample('top')}>
94
+ Show notice
95
+ </button>
96
+ </article>
97
+
98
+ <article className="demo-card">
99
+ <h2>5. Fullscreen</h2>
100
+ <p>Onboarding-style fullscreen modal.</p>
101
+ <button type="button" onClick={() => setSample('fullscreen')}>
102
+ Start tour
103
+ </button>
104
+ </article>
105
+
106
+ <article className="demo-card">
107
+ <h2>6. Persistent</h2>
108
+ <p>Blocks outside dismiss until confirmed.</p>
109
+ <button type="button" onClick={() => setSample('persistent')}>
110
+ Review policy
111
+ </button>
112
+ </article>
113
+
114
+ <article className="demo-card">
115
+ <h2>7. Custom brand</h2>
116
+ <p>Per-modal mint theme tokens.</p>
117
+ <button type="button" onClick={() => setSample('brand')}>
118
+ Open branded modal
119
+ </button>
120
+ </article>
121
+ </section>
122
+
123
+ <UniversalModal
124
+ open={sample === 'invite'}
125
+ onOpenChange={(open) => !open && close()}
126
+ title="Invite teammate"
127
+ description="They will receive an email with access instructions."
128
+ confirmText="Send invite"
129
+ theme={brandTheme}
130
+ onConfirm={async () => {
131
+ await new Promise((r) => setTimeout(r, 700));
132
+ console.log('invited', email);
133
+ }}
134
+ >
135
+ <label className="demo-field">
136
+ Email
137
+ <input
138
+ type="email"
139
+ value={email}
140
+ onChange={(e) => setEmail(e.target.value)}
141
+ placeholder="name@company.com"
142
+ />
143
+ </label>
144
+ </UniversalModal>
145
+
146
+ <UniversalModal
147
+ open={sample === 'danger'}
148
+ onOpenChange={(open) => !open && close()}
149
+ title="Revoke API token?"
150
+ description="Integrations using this token will stop working."
151
+ role="alertdialog"
152
+ confirmText="Revoke"
153
+ theme={dangerTheme}
154
+ onConfirm={async () => {
155
+ await new Promise((r) => setTimeout(r, 600));
156
+ }}
157
+ >
158
+ <p className="demo-muted">
159
+ Token prefix: <code>pl_live_••••9f2a</code>
160
+ </p>
161
+ </UniversalModal>
162
+
163
+ <UniversalModal
164
+ open={sample === 'drawer'}
165
+ onOpenChange={(open) => !open && close()}
166
+ title="Billing details"
167
+ size="lg"
168
+ placement="right"
169
+ confirmText="Save"
170
+ onConfirm={async () => {
171
+ await new Promise((r) => setTimeout(r, 500));
172
+ }}
173
+ >
174
+ <div className="demo-stack">
175
+ <label className="demo-field">
176
+ Company
177
+ <input defaultValue="Poluru Labs" />
178
+ </label>
179
+ <label className="demo-field">
180
+ Tax ID
181
+ <input placeholder="Optional" />
182
+ </label>
183
+ <label className="demo-field">
184
+ Billing email
185
+ <input type="email" defaultValue="billing@polurus.com" />
186
+ </label>
187
+ </div>
188
+ </UniversalModal>
189
+
190
+ <UniversalModal
191
+ open={sample === 'top'}
192
+ onOpenChange={(open) => !open && close()}
193
+ title="Scheduled maintenance"
194
+ placement="top"
195
+ hideActions
196
+ showCloseIcon
197
+ >
198
+ <p>Read-only mode from 01:00–01:30 UTC this Sunday.</p>
199
+ </UniversalModal>
200
+
201
+ <UniversalModal
202
+ open={sample === 'fullscreen'}
203
+ onOpenChange={(open) => !open && close()}
204
+ title="Product tour"
205
+ description="Fullscreen sample for walkthroughs."
206
+ size="fullscreen"
207
+ confirmText="Finish"
208
+ cancelText="Skip"
209
+ >
210
+ <div className="demo-stack">
211
+ <p>1. Open a modal from any framework adapter.</p>
212
+ <p>2. Customize brand colors with CSS variables or the theme prop.</p>
213
+ <p>3. Stack nested dialogs when you need confirmation.</p>
214
+ </div>
215
+ </UniversalModal>
216
+
217
+ <UniversalModal
218
+ open={sample === 'persistent'}
219
+ onOpenChange={(open) => !open && close()}
220
+ title="Security policy update"
221
+ role="alertdialog"
222
+ persistent
223
+ showCloseIcon={false}
224
+ confirmText="Acknowledge"
225
+ cancelText="Later"
226
+ >
227
+ <p>Escape and backdrop click are disabled until you choose an action.</p>
228
+ </UniversalModal>
229
+
230
+ <UniversalModal
231
+ open={sample === 'brand'}
232
+ onOpenChange={(open) => !open && close()}
233
+ title="Mint brand theme"
234
+ description="Per-instance theme override."
235
+ theme={mintTheme}
236
+ confirmText="Apply"
237
+ >
238
+ <p>Useful when one dialog needs a different brand treatment.</p>
239
+ </UniversalModal>
240
+ </main>
241
+ );
242
+ }
@@ -0,0 +1,26 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Universal Modal — React samples</title>
7
+ <link rel="stylesheet" href="../../packages/universal-modal/dist/styles.css" />
8
+ <link rel="stylesheet" href="./styles.css" />
9
+ </head>
10
+ <body data-um-theme="light">
11
+ <div id="root"></div>
12
+ <script type="importmap">
13
+ {
14
+ "imports": {
15
+ "react": "https://esm.sh/react@19.0.0",
16
+ "react/jsx-runtime": "https://esm.sh/react@19.0.0/jsx-runtime",
17
+ "react-dom": "https://esm.sh/react-dom@19.0.0",
18
+ "react-dom/client": "https://esm.sh/react-dom@19.0.0/client",
19
+ "@poluru-labs/universal-modal": "../../packages/universal-modal/dist/index.js",
20
+ "@poluru-labs/universal-modal-react": "../../packages/universal-modal-react/dist/index.js"
21
+ }
22
+ }
23
+ </script>
24
+ <script type="module" src="./main.js"></script>
25
+ </body>
26
+ </html>