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.
- package/AUTHORS +5 -0
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/NOTICE +6 -0
- package/README.md +239 -0
- package/SECURITY.md +21 -0
- package/docs/ACCESSIBILITY.md +43 -0
- package/docs/API.md +56 -0
- package/docs/THEMING.md +65 -0
- package/examples/angular/app.component.html +137 -0
- package/examples/angular/app.component.ts +53 -0
- package/examples/angular/index.html +99 -0
- package/examples/angular/main.js +121 -0
- package/examples/angular/styles.css +25 -0
- package/examples/index.html +53 -0
- package/examples/react/App.tsx +242 -0
- package/examples/react/index.html +26 -0
- package/examples/react/main.js +144 -0
- package/examples/react/styles.css +1 -0
- package/examples/shared/gallery.css +177 -0
- package/examples/vanilla/index.html +89 -0
- package/examples/vanilla/main.js +174 -0
- package/examples/vanilla/styles.css +1 -0
- package/examples/vue/App.vue +190 -0
- package/examples/vue/index.html +23 -0
- package/examples/vue/main.js +174 -0
- package/examples/vue/styles.css +1 -0
- package/examples/web-component/index.html +190 -0
- package/examples/web-component/main.js +58 -0
- package/examples/web-component/styles.css +11 -0
- package/package.json +36 -0
- package/packages/universal-modal/AUTHORS +5 -0
- package/packages/universal-modal/CHANGELOG.md +10 -0
- package/packages/universal-modal/LICENSE +21 -0
- package/packages/universal-modal/NOTICE +6 -0
- package/packages/universal-modal/README.md +29 -0
- package/packages/universal-modal/package.json +83 -0
- package/packages/universal-modal/scripts/copy-styles.mjs +11 -0
- package/packages/universal-modal/src/core/constants.ts +50 -0
- package/packages/universal-modal/src/core/event-emitter.ts +46 -0
- package/packages/universal-modal/src/core/focus-trap.ts +101 -0
- package/packages/universal-modal/src/core/index.ts +39 -0
- package/packages/universal-modal/src/core/modal-engine.ts +410 -0
- package/packages/universal-modal/src/core/modal-stack.ts +68 -0
- package/packages/universal-modal/src/core/scroll-lock.ts +78 -0
- package/packages/universal-modal/src/core/theme.ts +50 -0
- package/packages/universal-modal/src/core/types.ts +196 -0
- package/packages/universal-modal/src/core/utils.ts +114 -0
- package/packages/universal-modal/src/dom/index.ts +1 -0
- package/packages/universal-modal/src/dom/modal-renderer.ts +191 -0
- package/packages/universal-modal/src/index.ts +2 -0
- package/packages/universal-modal/src/styles/modal.css +368 -0
- package/packages/universal-modal/tsconfig.json +24 -0
- package/packages/universal-modal/tsup.config.ts +12 -0
- package/packages/universal-modal-angular/AUTHORS +5 -0
- package/packages/universal-modal-angular/CHANGELOG.md +9 -0
- package/packages/universal-modal-angular/LICENSE +21 -0
- package/packages/universal-modal-angular/NOTICE +6 -0
- package/packages/universal-modal-angular/README.md +12 -0
- package/packages/universal-modal-angular/package.json +85 -0
- package/packages/universal-modal-angular/src/index.ts +10 -0
- package/packages/universal-modal-angular/src/universal-modal.component.ts +275 -0
- package/packages/universal-modal-angular/tsconfig.json +20 -0
- package/packages/universal-modal-angular/tsup.config.ts +10 -0
- package/packages/universal-modal-react/AUTHORS +5 -0
- package/packages/universal-modal-react/CHANGELOG.md +9 -0
- package/packages/universal-modal-react/LICENSE +21 -0
- package/packages/universal-modal-react/NOTICE +6 -0
- package/packages/universal-modal-react/README.md +12 -0
- package/packages/universal-modal-react/package.json +87 -0
- package/packages/universal-modal-react/src/UniversalModal.tsx +340 -0
- package/packages/universal-modal-react/src/index.ts +10 -0
- package/packages/universal-modal-react/tsconfig.json +19 -0
- package/packages/universal-modal-react/tsup.config.ts +13 -0
- package/packages/universal-modal-vanilla/AUTHORS +5 -0
- package/packages/universal-modal-vanilla/CHANGELOG.md +9 -0
- package/packages/universal-modal-vanilla/LICENSE +21 -0
- package/packages/universal-modal-vanilla/NOTICE +6 -0
- package/packages/universal-modal-vanilla/README.md +12 -0
- package/packages/universal-modal-vanilla/package.json +79 -0
- package/packages/universal-modal-vanilla/src/create-modal.ts +125 -0
- package/packages/universal-modal-vanilla/src/index.ts +14 -0
- package/packages/universal-modal-vanilla/tsconfig.json +18 -0
- package/packages/universal-modal-vanilla/tsup.config.ts +10 -0
- package/packages/universal-modal-vue/AUTHORS +5 -0
- package/packages/universal-modal-vue/CHANGELOG.md +9 -0
- package/packages/universal-modal-vue/LICENSE +21 -0
- package/packages/universal-modal-vue/NOTICE +6 -0
- package/packages/universal-modal-vue/README.md +12 -0
- package/packages/universal-modal-vue/package.json +83 -0
- package/packages/universal-modal-vue/src/UniversalModal.ts +342 -0
- package/packages/universal-modal-vue/src/index.ts +10 -0
- package/packages/universal-modal-vue/tsconfig.json +18 -0
- package/packages/universal-modal-vue/tsup.config.ts +10 -0
- package/packages/universal-modal-web-component/AUTHORS +5 -0
- package/packages/universal-modal-web-component/CHANGELOG.md +9 -0
- package/packages/universal-modal-web-component/LICENSE +21 -0
- package/packages/universal-modal-web-component/NOTICE +6 -0
- package/packages/universal-modal-web-component/README.md +12 -0
- package/packages/universal-modal-web-component/package.json +83 -0
- package/packages/universal-modal-web-component/src/index.ts +11 -0
- package/packages/universal-modal-web-component/src/universal-modal-element.ts +424 -0
- package/packages/universal-modal-web-component/tsconfig.json +18 -0
- package/packages/universal-modal-web-component/tsup.config.ts +10 -0
- package/scripts/copy-styles.mjs +11 -0
- package/scripts/serve-examples.mjs +48 -0
package/AUTHORS
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in each package `CHANGELOG.md`.
|
|
4
|
+
|
|
5
|
+
## 1.0.0 — 2026-08-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Monorepo packages: core + react / vue / angular / vanilla / web-component
|
|
9
|
+
- Brand and button theming via CSS variables and `theme` option
|
|
10
|
+
- MIT license, AUTHORS, NOTICE, SECURITY policy
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Subrahmanyam Poluru
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# @poluru-labs/universal-modal
|
|
2
|
+
|
|
3
|
+
Production-grade **universal modal** monorepo — framework-agnostic core plus separately installable adapters.
|
|
4
|
+
|
|
5
|
+
**Author:** Subrahmanyam Poluru ([polurus.com](https://polurus.com)) · [mail.polurus@gmail.com](mailto:mail.polurus@gmail.com) · [LinkedIn](https://www.linkedin.com/in/polurus/)
|
|
6
|
+
**Org:** [Poluru](https://github.com/poluru-labs) · License: MIT
|
|
7
|
+
|
|
8
|
+
> npm cannot publish or install a package named `@scope/pkg/react` (the `/` is a subpath, not a package).
|
|
9
|
+
> Install the matching package name instead — same idea as `@headlessui/react`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
Pick the adapter for your stack (each pulls in the core automatically):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @poluru-labs/universal-modal-react
|
|
19
|
+
npm install @poluru-labs/universal-modal-vue
|
|
20
|
+
npm install @poluru-labs/universal-modal-angular
|
|
21
|
+
npm install @poluru-labs/universal-modal-vanilla
|
|
22
|
+
npm install @poluru-labs/universal-modal-web-component
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Core only (engine + styles):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @poluru-labs/universal-modal
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Import styles once:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import '@poluru-labs/universal-modal/styles.css';
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Brand & button colors
|
|
38
|
+
|
|
39
|
+
Override globally with CSS variables:
|
|
40
|
+
|
|
41
|
+
```css
|
|
42
|
+
:root {
|
|
43
|
+
--um-color-brand: #0d47a1;
|
|
44
|
+
--um-color-brand-fg: #ffffff;
|
|
45
|
+
--um-btn-confirm-bg: var(--um-color-brand);
|
|
46
|
+
--um-btn-confirm-fg: var(--um-color-brand-fg);
|
|
47
|
+
--um-btn-confirm-border: var(--um-color-brand);
|
|
48
|
+
--um-btn-cancel-bg: #fff;
|
|
49
|
+
--um-btn-cancel-fg: #111827;
|
|
50
|
+
--um-btn-cancel-border: #e5e7eb;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or per modal via `theme`:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
createModal({
|
|
58
|
+
title: 'Delete?',
|
|
59
|
+
theme: {
|
|
60
|
+
brand: '#0d47a1',
|
|
61
|
+
confirmBg: '#b91c1c',
|
|
62
|
+
confirmFg: '#ffffff',
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
See [docs/THEMING.md](docs/THEMING.md).
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Packages
|
|
72
|
+
|
|
73
|
+
| Package | Import |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `@poluru-labs/universal-modal` | Core engine, types, `ModalRenderer`, CSS |
|
|
76
|
+
| `@poluru-labs/universal-modal-react` | `UniversalModal` React component |
|
|
77
|
+
| `@poluru-labs/universal-modal-vue` | `UniversalModal` Vue 3 component |
|
|
78
|
+
| `@poluru-labs/universal-modal-angular` | `UniversalModalComponent` Angular standalone |
|
|
79
|
+
| `@poluru-labs/universal-modal-vanilla` | `createModal`, `confirmModal` |
|
|
80
|
+
| `@poluru-labs/universal-modal-web-component` | `<universal-modal>` custom element |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Usage
|
|
85
|
+
|
|
86
|
+
### React
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm install @poluru-labs/universal-modal-react
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { useState } from 'react';
|
|
94
|
+
import { UniversalModal } from '@poluru-labs/universal-modal-react';
|
|
95
|
+
import '@poluru-labs/universal-modal/styles.css';
|
|
96
|
+
|
|
97
|
+
export function Example() {
|
|
98
|
+
const [open, setOpen] = useState(false);
|
|
99
|
+
return (
|
|
100
|
+
<>
|
|
101
|
+
<button onClick={() => setOpen(true)}>Open</button>
|
|
102
|
+
<UniversalModal open={open} onOpenChange={setOpen} title="Hello">
|
|
103
|
+
<p>Content</p>
|
|
104
|
+
</UniversalModal>
|
|
105
|
+
</>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Vue 3
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm install @poluru-labs/universal-modal-vue
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```vue
|
|
117
|
+
<script setup lang="ts">
|
|
118
|
+
import { ref } from 'vue';
|
|
119
|
+
import { UniversalModal } from '@poluru-labs/universal-modal-vue';
|
|
120
|
+
import '@poluru-labs/universal-modal/styles.css';
|
|
121
|
+
const open = ref(false);
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<template>
|
|
125
|
+
<UniversalModal v-model:open="open" title="Hello">
|
|
126
|
+
<p>Content</p>
|
|
127
|
+
</UniversalModal>
|
|
128
|
+
</template>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Angular
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npm install @poluru-labs/universal-modal-angular
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { UniversalModalComponent } from '@poluru-labs/universal-modal-angular';
|
|
139
|
+
import '@poluru-labs/universal-modal/styles.css';
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Vanilla JS / TypeScript
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm install @poluru-labs/universal-modal-vanilla
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { createModal } from '@poluru-labs/universal-modal-vanilla';
|
|
150
|
+
import '@poluru-labs/universal-modal/styles.css';
|
|
151
|
+
|
|
152
|
+
const modal = createModal({
|
|
153
|
+
title: 'Delete?',
|
|
154
|
+
role: 'alertdialog',
|
|
155
|
+
onConfirm: async () => api.remove(),
|
|
156
|
+
});
|
|
157
|
+
await modal.open();
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Web Components
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm install @poluru-labs/universal-modal-web-component
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import '@poluru-labs/universal-modal-web-component';
|
|
168
|
+
import '@poluru-labs/universal-modal/styles.css';
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```html
|
|
172
|
+
<universal-modal id="demo" title="Settings" size="lg">
|
|
173
|
+
<p>Content</p>
|
|
174
|
+
</universal-modal>
|
|
175
|
+
<script>
|
|
176
|
+
document.getElementById('demo').open();
|
|
177
|
+
</script>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Monorepo layout
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
packages/
|
|
186
|
+
universal-modal/ # @poluru-labs/universal-modal
|
|
187
|
+
universal-modal-react/ # @poluru-labs/universal-modal-react
|
|
188
|
+
universal-modal-vue/
|
|
189
|
+
universal-modal-angular/
|
|
190
|
+
universal-modal-vanilla/
|
|
191
|
+
universal-modal-web-component/
|
|
192
|
+
examples/
|
|
193
|
+
docs/
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Develop
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npm install
|
|
202
|
+
npm run build
|
|
203
|
+
npm run examples
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- http://localhost:4173/examples/index.html — gallery hub
|
|
207
|
+
- Vanilla / Web Component / React / Vue / Angular each include multiple samples (confirm, drawer, sheet, fullscreen, persistent, themed, nested)
|
|
208
|
+
|
|
209
|
+
See `docs/API.md`, `docs/THEMING.md`, and `docs/ACCESSIBILITY.md`.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Publish checklist
|
|
214
|
+
|
|
215
|
+
Before `npm run publish:packages`:
|
|
216
|
+
|
|
217
|
+
1. You are logged in (`npm whoami`) and can publish under `@poluru-labs`
|
|
218
|
+
2. GitHub repo URLs in each `package.json` match the real remote (or update them)
|
|
219
|
+
3. `npm run build` succeeds
|
|
220
|
+
4. Each package includes `LICENSE`, `AUTHORS`, `NOTICE`, `CHANGELOG.md`, `README.md`
|
|
221
|
+
5. No secrets in the tarball (`npm pack -w @poluru-labs/universal-modal --dry-run`)
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
npm run build
|
|
225
|
+
npm run publish:packages
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Publishes core first, then each adapter.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## License & legal
|
|
233
|
+
|
|
234
|
+
- **License:** [MIT](LICENSE) © Subrahmanyam Poluru
|
|
235
|
+
- **Authors:** [AUTHORS](AUTHORS)
|
|
236
|
+
- **Notice:** [NOTICE](NOTICE)
|
|
237
|
+
- **Security:** [SECURITY.md](SECURITY.md)
|
|
238
|
+
|
|
239
|
+
Third-party runtime code: none in the published core (framework peers are optional and not bundled). DevDependencies (TypeScript, tsup, React/Vue/Angular for builds) are not shipped in the npm tarball.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
|---|---|
|
|
7
|
+
| 1.x | Yes |
|
|
8
|
+
|
|
9
|
+
## Reporting a vulnerability
|
|
10
|
+
|
|
11
|
+
Please report security issues privately to **mail.polurus@gmail.com**.
|
|
12
|
+
|
|
13
|
+
Include:
|
|
14
|
+
- Package name and version
|
|
15
|
+
- Description of the issue
|
|
16
|
+
- Steps to reproduce (if possible)
|
|
17
|
+
- Impact assessment
|
|
18
|
+
|
|
19
|
+
Do not open a public GitHub issue for security reports until a fix is available or we confirm disclosure is safe.
|
|
20
|
+
|
|
21
|
+
We aim to acknowledge reports within 7 days.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Accessibility notes
|
|
2
|
+
|
|
3
|
+
Author: **Subrahmanyam Poluru** ·
|
|
4
|
+
|
|
5
|
+
## Semantics
|
|
6
|
+
|
|
7
|
+
- Root dialog uses `role="dialog"` by default
|
|
8
|
+
- Use `role="alertdialog"` for destructive or blocking confirms
|
|
9
|
+
- Always sets `aria-modal="true"`
|
|
10
|
+
- Prefers `aria-labelledby` (title) and `aria-describedby` (description)
|
|
11
|
+
- Falls back to `aria-label` when no visible title is provided
|
|
12
|
+
|
|
13
|
+
## Keyboard
|
|
14
|
+
|
|
15
|
+
- `Tab` / `Shift+Tab` are trapped inside the **topmost** open modal
|
|
16
|
+
- `Escape` closes the topmost modal (unless `persistent` or `closeOnEsc: false`)
|
|
17
|
+
- Close, Cancel, and Confirm are real `<button>` elements
|
|
18
|
+
|
|
19
|
+
## Focus management
|
|
20
|
+
|
|
21
|
+
1. On open: focus moves to `initialFocus`, then `[autofocus]`, then the first focusable control, then the dialog itself
|
|
22
|
+
2. On close: focus restores to `returnFocus` (or the previously focused element)
|
|
23
|
+
3. Nested modals: only the top layer’s focus trap is active
|
|
24
|
+
|
|
25
|
+
## Screen readers
|
|
26
|
+
|
|
27
|
+
- Title and description IDs are stable per modal instance
|
|
28
|
+
- Loading confirm buttons expose `aria-busy="true"`
|
|
29
|
+
- Close control has `aria-label="Close dialog"`
|
|
30
|
+
- Hidden closed roots use `hidden` + `aria-hidden="true"`
|
|
31
|
+
|
|
32
|
+
## Motion
|
|
33
|
+
|
|
34
|
+
- Honors `prefers-reduced-motion: reduce` (skips enter/exit delays and CSS transitions)
|
|
35
|
+
|
|
36
|
+
## Authoring checklist
|
|
37
|
+
|
|
38
|
+
- [ ] Provide a title **or** `ariaLabel`
|
|
39
|
+
- [ ] Use `alertdialog` for confirms that interrupt the user’s flow
|
|
40
|
+
- [ ] Ensure at least one focusable control inside the dialog
|
|
41
|
+
- [ ] Pass `returnFocus` to the trigger in Vanilla / imperative usage
|
|
42
|
+
- [ ] Verify stacked modals: ESC closes only the top layer
|
|
43
|
+
- [ ] Confirm focus returns after close with VoiceOver / NVDA
|
package/docs/API.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Author: **Subrahmanyam Poluru** · mail.polurus@gmail.com · https://polurus.com
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @poluru-labs/universal-modal-react
|
|
9
|
+
npm install @poluru-labs/universal-modal-vue
|
|
10
|
+
npm install @poluru-labs/universal-modal-angular
|
|
11
|
+
npm install @poluru-labs/universal-modal-vanilla
|
|
12
|
+
npm install @poluru-labs/universal-modal-web-component
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Imports
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { ModalEngine, ModalRenderer } from '@poluru-labs/universal-modal';
|
|
19
|
+
import { createModal, confirmModal } from '@poluru-labs/universal-modal-vanilla';
|
|
20
|
+
import { UniversalModal } from '@poluru-labs/universal-modal-react';
|
|
21
|
+
import { UniversalModal } from '@poluru-labs/universal-modal-vue';
|
|
22
|
+
import { UniversalModalComponent } from '@poluru-labs/universal-modal-angular';
|
|
23
|
+
import '@poluru-labs/universal-modal-web-component';
|
|
24
|
+
import '@poluru-labs/universal-modal/styles.css';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Options (shared)
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
type ModalOptions = {
|
|
31
|
+
id?: string;
|
|
32
|
+
size?: 'sm' | 'md' | 'lg' | 'xl' | 'fullscreen';
|
|
33
|
+
placement?: 'center' | 'top' | 'bottom' | 'left' | 'right';
|
|
34
|
+
title?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
persistent?: boolean;
|
|
37
|
+
closeOnEsc?: boolean;
|
|
38
|
+
closeOnBackdrop?: boolean;
|
|
39
|
+
showCloseIcon?: boolean;
|
|
40
|
+
role?: 'dialog' | 'alertdialog';
|
|
41
|
+
confirmText?: string;
|
|
42
|
+
cancelText?: string;
|
|
43
|
+
hideActions?: boolean;
|
|
44
|
+
zIndexBase?: number;
|
|
45
|
+
onConfirm?: () => void | Promise<void>;
|
|
46
|
+
onCancel?: () => void | Promise<void>;
|
|
47
|
+
beforeOpen?: () => boolean | void | Promise<boolean | void>;
|
|
48
|
+
afterOpen?: () => void;
|
|
49
|
+
beforeClose?: (reason: CloseReason) => boolean | void | Promise<boolean | void>;
|
|
50
|
+
afterClose?: (reason: CloseReason) => void;
|
|
51
|
+
};
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Events
|
|
55
|
+
|
|
56
|
+
`beforeOpen` · `afterOpen` · `beforeClose` · `afterClose` · `confirm` · `cancel` · `openChange` · `loadingChange`
|
package/docs/THEMING.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Theming & brand colors
|
|
2
|
+
|
|
3
|
+
Default brand color: `#0d47a1`.
|
|
4
|
+
|
|
5
|
+
## 1. CSS variables (recommended)
|
|
6
|
+
|
|
7
|
+
Import styles, then override on `:root`, `[data-um-theme]`, or a parent:
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
:root {
|
|
11
|
+
--um-color-brand: #0d47a1;
|
|
12
|
+
--um-color-brand-fg: #ffffff;
|
|
13
|
+
|
|
14
|
+
--um-btn-confirm-bg: var(--um-color-brand);
|
|
15
|
+
--um-btn-confirm-fg: var(--um-color-brand-fg);
|
|
16
|
+
--um-btn-confirm-border: var(--um-color-brand);
|
|
17
|
+
|
|
18
|
+
--um-btn-cancel-bg: #ffffff;
|
|
19
|
+
--um-btn-cancel-fg: #111827;
|
|
20
|
+
--um-btn-cancel-border: #e5e7eb;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Brand tokens
|
|
25
|
+
|
|
26
|
+
| Variable | Purpose |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `--um-color-brand` | Primary brand color |
|
|
29
|
+
| `--um-color-brand-fg` | Text/icon on brand surfaces |
|
|
30
|
+
| `--um-color-accent` | Alias of brand (legacy) |
|
|
31
|
+
| `--um-color-accent-fg` | Alias of brand foreground |
|
|
32
|
+
|
|
33
|
+
### Button tokens
|
|
34
|
+
|
|
35
|
+
| Variable | Purpose |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `--um-btn-confirm-bg` | Confirm button background |
|
|
38
|
+
| `--um-btn-confirm-fg` | Confirm button text |
|
|
39
|
+
| `--um-btn-confirm-border` | Confirm button border |
|
|
40
|
+
| `--um-btn-cancel-bg` | Cancel button background |
|
|
41
|
+
| `--um-btn-cancel-fg` | Cancel button text |
|
|
42
|
+
| `--um-btn-cancel-border` | Cancel button border |
|
|
43
|
+
|
|
44
|
+
## 2. Per-modal `theme` option
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
createModal({
|
|
48
|
+
title: 'Delete?',
|
|
49
|
+
theme: {
|
|
50
|
+
brand: '#0d47a1',
|
|
51
|
+
brandForeground: '#ffffff',
|
|
52
|
+
confirmBg: '#b91c1c',
|
|
53
|
+
confirmFg: '#ffffff',
|
|
54
|
+
confirmBorder: '#b91c1c',
|
|
55
|
+
cancelBg: '#ffffff',
|
|
56
|
+
cancelFg: '#111827',
|
|
57
|
+
cancelBorder: '#e5e7eb',
|
|
58
|
+
cssVars: {
|
|
59
|
+
'um-radius': '16px',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
React / Vue / Angular accept the same `theme` prop/input.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<main class="demo-page">
|
|
2
|
+
<header class="demo-header">
|
|
3
|
+
<p class="demo-kicker">Angular</p>
|
|
4
|
+
<h1>Modal samples</h1>
|
|
5
|
+
<p>
|
|
6
|
+
Standalone <code>UniversalModalComponent</code> patterns — drop this into an Angular
|
|
7
|
+
16+ app with the package import.
|
|
8
|
+
</p>
|
|
9
|
+
</header>
|
|
10
|
+
|
|
11
|
+
<section class="demo-grid">
|
|
12
|
+
<article class="demo-card">
|
|
13
|
+
<h2>1. Archive confirm</h2>
|
|
14
|
+
<p>Alertdialog with async confirm handler.</p>
|
|
15
|
+
<button type="button" (click)="open('archive')">Archive workspace</button>
|
|
16
|
+
</article>
|
|
17
|
+
<article class="demo-card">
|
|
18
|
+
<h2>2. Danger delete</h2>
|
|
19
|
+
<p>Custom red confirm theme.</p>
|
|
20
|
+
<button type="button" (click)="open('danger')">Delete webhook</button>
|
|
21
|
+
</article>
|
|
22
|
+
<article class="demo-card">
|
|
23
|
+
<h2>3. Right drawer</h2>
|
|
24
|
+
<p>Large form panel.</p>
|
|
25
|
+
<button type="button" (click)="open('drawer')">Edit workspace</button>
|
|
26
|
+
</article>
|
|
27
|
+
<article class="demo-card">
|
|
28
|
+
<h2>4. Top notice</h2>
|
|
29
|
+
<p>No footer actions.</p>
|
|
30
|
+
<button type="button" (click)="open('top')">Show notice</button>
|
|
31
|
+
</article>
|
|
32
|
+
<article class="demo-card">
|
|
33
|
+
<h2>5. Fullscreen</h2>
|
|
34
|
+
<p>Checklist / walkthrough layout.</p>
|
|
35
|
+
<button type="button" (click)="open('fullscreen')">Open checklist</button>
|
|
36
|
+
</article>
|
|
37
|
+
<article class="demo-card">
|
|
38
|
+
<h2>6. Persistent</h2>
|
|
39
|
+
<p>Blocks ESC and backdrop.</p>
|
|
40
|
+
<button type="button" (click)="open('persistent')">Accept DPA</button>
|
|
41
|
+
</article>
|
|
42
|
+
<article class="demo-card">
|
|
43
|
+
<h2>7. Brand theme</h2>
|
|
44
|
+
<p>Per-dialog brand override.</p>
|
|
45
|
+
<button type="button" (click)="open('brand')">Open branded</button>
|
|
46
|
+
</article>
|
|
47
|
+
</section>
|
|
48
|
+
|
|
49
|
+
<universal-modal
|
|
50
|
+
[open]="sample === 'archive'"
|
|
51
|
+
title="Archive workspace?"
|
|
52
|
+
description="Members keep read-only access."
|
|
53
|
+
role="alertdialog"
|
|
54
|
+
confirmText="Archive"
|
|
55
|
+
cancelText="Cancel"
|
|
56
|
+
(openChange)="onOpenChange($event)"
|
|
57
|
+
(confirm)="onArchive()"
|
|
58
|
+
>
|
|
59
|
+
<label class="demo-field">
|
|
60
|
+
Workspace
|
|
61
|
+
<input [value]="workspace" (input)="workspace = $any($event.target).value" />
|
|
62
|
+
</label>
|
|
63
|
+
</universal-modal>
|
|
64
|
+
|
|
65
|
+
<universal-modal
|
|
66
|
+
[open]="sample === 'danger'"
|
|
67
|
+
title="Delete webhook?"
|
|
68
|
+
role="alertdialog"
|
|
69
|
+
confirmText="Delete"
|
|
70
|
+
[theme]="dangerTheme"
|
|
71
|
+
(openChange)="onOpenChange($event)"
|
|
72
|
+
>
|
|
73
|
+
<p>Deliveries will stop immediately.</p>
|
|
74
|
+
</universal-modal>
|
|
75
|
+
|
|
76
|
+
<universal-modal
|
|
77
|
+
[open]="sample === 'drawer'"
|
|
78
|
+
title="Workspace settings"
|
|
79
|
+
size="lg"
|
|
80
|
+
placement="right"
|
|
81
|
+
confirmText="Save"
|
|
82
|
+
(openChange)="onOpenChange($event)"
|
|
83
|
+
>
|
|
84
|
+
<div class="demo-stack">
|
|
85
|
+
<label class="demo-field">Name <input [value]="workspace" /></label>
|
|
86
|
+
<label class="demo-field">Slug <input value="poluru-labs" /></label>
|
|
87
|
+
</div>
|
|
88
|
+
</universal-modal>
|
|
89
|
+
|
|
90
|
+
<universal-modal
|
|
91
|
+
[open]="sample === 'top'"
|
|
92
|
+
title="Deploy in progress"
|
|
93
|
+
placement="top"
|
|
94
|
+
[hideActions]="true"
|
|
95
|
+
(openChange)="onOpenChange($event)"
|
|
96
|
+
>
|
|
97
|
+
<p>You can keep browsing while the pipeline finishes.</p>
|
|
98
|
+
</universal-modal>
|
|
99
|
+
|
|
100
|
+
<universal-modal
|
|
101
|
+
[open]="sample === 'fullscreen'"
|
|
102
|
+
title="Launch checklist"
|
|
103
|
+
size="fullscreen"
|
|
104
|
+
confirmText="Mark complete"
|
|
105
|
+
(openChange)="onOpenChange($event)"
|
|
106
|
+
>
|
|
107
|
+
<div class="demo-stack">
|
|
108
|
+
<p>✓ Domain verified</p>
|
|
109
|
+
<p>✓ Billing method added</p>
|
|
110
|
+
<p>✓ First project created</p>
|
|
111
|
+
</div>
|
|
112
|
+
</universal-modal>
|
|
113
|
+
|
|
114
|
+
<universal-modal
|
|
115
|
+
[open]="sample === 'persistent'"
|
|
116
|
+
title="Data processing agreement"
|
|
117
|
+
role="alertdialog"
|
|
118
|
+
[persistent]="true"
|
|
119
|
+
[showCloseIcon]="false"
|
|
120
|
+
confirmText="Accept"
|
|
121
|
+
cancelText="Decline"
|
|
122
|
+
(openChange)="onOpenChange($event)"
|
|
123
|
+
>
|
|
124
|
+
<p>Outside dismiss is disabled until you choose an action.</p>
|
|
125
|
+
</universal-modal>
|
|
126
|
+
|
|
127
|
+
<universal-modal
|
|
128
|
+
[open]="sample === 'brand'"
|
|
129
|
+
title="Branded dialog"
|
|
130
|
+
description="theme input maps to CSS variables."
|
|
131
|
+
[theme]="brandTheme"
|
|
132
|
+
confirmText="Apply"
|
|
133
|
+
(openChange)="onOpenChange($event)"
|
|
134
|
+
>
|
|
135
|
+
<p>Use this for one-off brand treatments.</p>
|
|
136
|
+
</universal-modal>
|
|
137
|
+
</main>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
UniversalModalComponent,
|
|
4
|
+
type ModalTheme,
|
|
5
|
+
} from '@poluru-labs/universal-modal-angular';
|
|
6
|
+
|
|
7
|
+
type Sample =
|
|
8
|
+
| 'archive'
|
|
9
|
+
| 'danger'
|
|
10
|
+
| 'drawer'
|
|
11
|
+
| 'top'
|
|
12
|
+
| 'fullscreen'
|
|
13
|
+
| 'persistent'
|
|
14
|
+
| 'brand'
|
|
15
|
+
| null;
|
|
16
|
+
|
|
17
|
+
@Component({
|
|
18
|
+
selector: 'app-root',
|
|
19
|
+
standalone: true,
|
|
20
|
+
imports: [UniversalModalComponent],
|
|
21
|
+
templateUrl: './app.component.html',
|
|
22
|
+
styleUrl: './styles.css',
|
|
23
|
+
})
|
|
24
|
+
export class AppComponent {
|
|
25
|
+
sample: Sample = null;
|
|
26
|
+
workspace = 'Poluru Labs';
|
|
27
|
+
|
|
28
|
+
readonly dangerTheme: ModalTheme = {
|
|
29
|
+
confirmBg: '#b91c1c',
|
|
30
|
+
confirmFg: '#ffffff',
|
|
31
|
+
confirmBorder: '#b91c1c',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
readonly brandTheme: ModalTheme = {
|
|
35
|
+
brand: '#0d47a1',
|
|
36
|
+
brandForeground: '#ffffff',
|
|
37
|
+
confirmBg: '#0d47a1',
|
|
38
|
+
confirmFg: '#ffffff',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
open(sample: Exclude<Sample, null>): void {
|
|
42
|
+
this.sample = sample;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
onOpenChange(open: boolean): void {
|
|
46
|
+
if (!open) this.sample = null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async onArchive(): Promise<void> {
|
|
50
|
+
await new Promise((r) => setTimeout(r, 700));
|
|
51
|
+
console.log('archived', this.workspace);
|
|
52
|
+
}
|
|
53
|
+
}
|