@t007/dialog 0.0.22 → 0.0.24
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/README.md +57 -12
- package/dist/index.css +67 -48
- package/dist/index.d.ts +34 -12
- package/dist/index.global.js +333 -79
- package/dist/index.js +66 -75
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
## Overview
|
|
31
31
|
|
|
32
|
-
**@t007/dialog** is a
|
|
32
|
+
**@t007/dialog** is a lightweight UI library that completely overrides the "ugly", thread-blocking native browser dialogs with beautiful, non-blocking, Promise-based alternatives.
|
|
33
33
|
|
|
34
34
|
### Why @t007/dialog?
|
|
35
35
|
|
|
@@ -62,7 +62,6 @@ Integrates with the `t007.FM` (Form Manager) to capture, validate, and return us
|
|
|
62
62
|
- **Promise-Based API**: Use `async/await` for incredibly clean control flow.
|
|
63
63
|
- **Form Validation**: Native form validation built directly into the prompt modal.
|
|
64
64
|
- **Keyboard Navigation**: Native `Esc` key cancellation and auto-focusing capabilities.
|
|
65
|
-
- **Tree-Shakeable**: Import only the specific dialogs you need.
|
|
66
65
|
- **Highly Customizable**: Clean DOM structure with distinct CSS classes for easy overriding.
|
|
67
66
|
|
|
68
67
|
---
|
|
@@ -131,7 +130,7 @@ async function triggerPrompt() {
|
|
|
131
130
|
|
|
132
131
|
### CDN / Browser (Global)
|
|
133
132
|
|
|
134
|
-
If you are not using a bundler, the IIFE build automatically injects the dialogs into the global `t007` object and provides convenient capitalized window fallbacks (`window.Alert`, `window.Confirm`, `window.Prompt`).
|
|
133
|
+
If you are not using a bundler, the IIFE build automatically injects the dialogs into the global `t007` object and provides convenient capitalized window fallbacks (`window.Alert`, `window.Confirm`, `window.Prompt`), reassign if desired.
|
|
135
134
|
|
|
136
135
|
```html
|
|
137
136
|
<!DOCTYPE html>
|
|
@@ -146,7 +145,7 @@ If you are not using a bundler, the IIFE build automatically injects the dialogs
|
|
|
146
145
|
<script>
|
|
147
146
|
// The library automatically maps to window.Confirm!
|
|
148
147
|
document.getElementById('deleteBtn').addEventListener('click', async () => {
|
|
149
|
-
const proceed = await Confirm("Proceed with formatting?"); // or use `t007.confirm()`
|
|
148
|
+
const proceed = await Confirm("Proceed with formatting?"); // or use `t007.confirm()`, `t007.dialog` contains the whole API
|
|
150
149
|
if(proceed) doFormat();
|
|
151
150
|
});
|
|
152
151
|
</script>
|
|
@@ -163,7 +162,11 @@ Displays a simple message and a confirmation button.
|
|
|
163
162
|
|
|
164
163
|
- **`message`** *(String)*: The text to display.
|
|
165
164
|
- **`options`** *(Object)*: Optional configuration.
|
|
165
|
+
- `options.id` *(String)*: Unique identifier for the dialog for programmatic dismissal.
|
|
166
166
|
- `options.confirmText` *(String)*: Custom text for the button (Default: `"OK"`).
|
|
167
|
+
- `options.closedBy` *(String)*: Browser behavior for handling dialog closure.
|
|
168
|
+
- `options.rootElement` *(HTMLElement)*: Render dialog inside this element.
|
|
169
|
+
- `options.scoped` *(Boolean)*: Whether to restrict interactions to `rootElement` if provided. Defaults to `true`.
|
|
167
170
|
- **Returns**: `Promise<true>`
|
|
168
171
|
|
|
169
172
|
### `confirm(question, options)`
|
|
@@ -172,8 +175,10 @@ Displays a question with confirm and cancel buttons.
|
|
|
172
175
|
|
|
173
176
|
- **`question`** *(String)*: The question to ask the user.
|
|
174
177
|
- **`options`** *(Object)*: Optional configuration.
|
|
178
|
+
- `options.id` *(String)*: Unique identifier for the dialog for programmatic dismissal.
|
|
175
179
|
- `options.confirmText` *(String)*: Custom text for the confirm button (Default: `"OK"`).
|
|
176
180
|
- `options.cancelText` *(String)*: Custom text for the cancel button (Default: `"Cancel"`).
|
|
181
|
+
- *Last 3 in `alert()` apply here as well...*
|
|
177
182
|
- **Returns**: `Promise<boolean>` (`true` if confirmed, `false` if cancelled).
|
|
178
183
|
|
|
179
184
|
### `prompt(question, defaultValue, options)`
|
|
@@ -182,19 +187,40 @@ Displays an input field to collect data from the user. Note: This automatically
|
|
|
182
187
|
|
|
183
188
|
- **`question`** *(String)*: The prompt instructions.
|
|
184
189
|
- **`defaultValue`** *(String)*: The initial value placed inside the input.
|
|
185
|
-
- **`options`** *(Object)*: Optional configuration passed directly to the input field generation.
|
|
190
|
+
- **`options`** *(Object)*: Optional @t007/input configuration passed directly to the input field generation.
|
|
191
|
+
- `options.id` *(String)*: Unique identifier for the dialog for programmatic dismissal, also used as the input's ID.
|
|
186
192
|
- `options.confirmText` *(String)*: Custom text for the submit button.
|
|
187
193
|
- `options.cancelText` *(String)*: Custom text for the cancel button.
|
|
188
194
|
- *Accepts standard HTML input attributes (type, required, placeholder, etc.)*
|
|
195
|
+
- *Last 3 in `alert()` apply here as well...*
|
|
189
196
|
- **Returns**: `Promise<String | null>` (Returns the string value, or `null` if cancelled).
|
|
190
197
|
|
|
198
|
+
### `dismiss(id, response)`
|
|
199
|
+
Programatically dismiss a dialog by id or all dialogs when no id is provided.
|
|
200
|
+
|
|
201
|
+
- **`id`** *(String)*: Optional unique identifier of the dialog to dismiss. If not provided, all dialogs will be dismissed.
|
|
202
|
+
- **`response`** *(Any)*: Optional value to resolve the dialog's promise with. If not provided, defaults to `true` for confirm and alert dialogs, and `null` for prompt dialogs.
|
|
203
|
+
|
|
204
|
+
### `isActive(id)`
|
|
205
|
+
Check if a dialog is currently active. Optionally check for a specific dialog by id.
|
|
206
|
+
- **`id`** *(String)*: Optional unique identifier of the dialog to check. If not provided, checks if any dialog is active.
|
|
207
|
+
- **Returns**: `boolean` (`true` if the specified dialog or any dialog is active, otherwise `false`).
|
|
208
|
+
|
|
209
|
+
#### Backdrop behavior
|
|
210
|
+
|
|
211
|
+
- Default mode uses native `<dialog>.showModal()` and native `::backdrop`.
|
|
212
|
+
- Scoped mode (`rootElement`) uses `<dialog>.show()` with a pseudo backdrop.
|
|
213
|
+
-
|
|
214
|
+
|
|
215
|
+
*NOTE: Scoped mode only restricts interactions within the specified root and top layer, while default mode is truly modal and blocks all interactions until dismissed. Choose based on your application's needs, you might need to gate some business logic depending on the mode.*
|
|
216
|
+
|
|
191
217
|
-----
|
|
192
218
|
|
|
193
219
|
## Customization
|
|
194
220
|
|
|
195
221
|
The dialogs are built with semantic, easily targetable CSS classes. You can easily override these in your own stylesheet to match your application's theme.
|
|
196
222
|
|
|
197
|
-
### CSS Selectors
|
|
223
|
+
### CSS Selectors & Variables
|
|
198
224
|
|
|
199
225
|
- `.t007-dialog`: The main `<dialog>` container.
|
|
200
226
|
- `.t007-dialog-top-section`: The wrapper for the text content.
|
|
@@ -204,17 +230,36 @@ The dialogs are built with semantic, easily targetable CSS classes. You can easi
|
|
|
204
230
|
- `.t007-dialog-cancel-button`: The secondary/cancel button.
|
|
205
231
|
- `.t007-input-form`: The form wrapper used exclusively in the `prompt` dialog.
|
|
206
232
|
|
|
207
|
-
|
|
233
|
+
#### Override Starter (Chrome like)
|
|
208
234
|
|
|
209
235
|
```css
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
color:
|
|
214
|
-
|
|
236
|
+
:root {
|
|
237
|
+
--app-theme-color: white;
|
|
238
|
+
--app-brand-color: red;
|
|
239
|
+
--app-brand-accent-color: orangered;
|
|
240
|
+
--t007-dialog-backdrop-background: rgba(0, 0, 0, 0.6);
|
|
241
|
+
--t007-dialog-backdrop-filter: none;
|
|
242
|
+
--t007-cancel-button-color: dodgerblue;
|
|
243
|
+
/* ...check source code for all variables... */
|
|
244
|
+
}
|
|
245
|
+
.t007-dialog {
|
|
246
|
+
margin-top: 0;
|
|
247
|
+
/* advised override variables */
|
|
248
|
+
--t007-dialog-unit: 0.9rem;
|
|
249
|
+
--t007-dialog-message-color: var(--app-theme-color);
|
|
250
|
+
--t007-dialog-confirm-button-color: var(--app-brand-accent-color);
|
|
251
|
+
--t007-dialog-cancel-button-color: var(--app-brand-color);
|
|
215
252
|
}
|
|
216
253
|
```
|
|
217
254
|
|
|
255
|
+
#### Specificity Note
|
|
256
|
+
|
|
257
|
+
- Start with `:root` for shared dialog tokens.
|
|
258
|
+
- If a token does not apply, override directly on `.t007-dialog`.
|
|
259
|
+
- For sizing/layout variables, override `.t007-dialog` (unit, width, icon size, margins).
|
|
260
|
+
- For strong app themes (e.g. `html[data-theme="dark"]`), use equal/stronger selectors like `html[data-theme="dark"] .t007-dialog`.
|
|
261
|
+
- Check source code for more details.
|
|
262
|
+
|
|
218
263
|
-----
|
|
219
264
|
|
|
220
265
|
## Author
|
package/dist/index.css
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
/* src/css/index.css */
|
|
2
2
|
:where(:root) {
|
|
3
3
|
--t007-dialog-font-family: inherit;
|
|
4
|
-
--t007-dialog-unit: 1rem;
|
|
5
4
|
--t007-dialog-background: rgb(20, 20, 20);
|
|
6
|
-
--t007-dialog-gap: 0.8rem;
|
|
7
|
-
--t007-dialog-width: 26.5rem;
|
|
8
5
|
--t007-dialog-height: fit-content;
|
|
9
6
|
--t007-dialog-max-width: 90%;
|
|
10
|
-
--t007-dialog-
|
|
11
|
-
--t007-dialog-max-content-height: 12rem;
|
|
12
|
-
--t007-dialog-padding: 1rem;
|
|
13
|
-
--t007-dialog-content-padding-block: 1.5rem;
|
|
14
|
-
--t007-dialog-border-radius: 1rem;
|
|
15
|
-
--t007-dialog-border: 0.05rem solid rgba(255, 255, 255, 0.25);
|
|
7
|
+
--t007-dialog-border: 0.8px solid rgba(255, 255, 255, 0.25);
|
|
16
8
|
--t007-dialog-box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5), -2px -2px 6px rgba(0, 0, 0, 0.5);
|
|
17
9
|
--t007-dialog-backdrop-background: rgba(0, 0, 0, 0.2);
|
|
18
|
-
--t007-dialog-backdrop-
|
|
10
|
+
--t007-dialog-backdrop-filter: blur(6px);
|
|
19
11
|
--t007-dialog-message-color: whitesmoke;
|
|
20
|
-
--t007-dialog-message-font-size: 0.9rem;
|
|
21
|
-
--t007-dialog-button-font-size: 0.8rem;
|
|
22
12
|
--t007-dialog-button-text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5);
|
|
23
|
-
--t007-dialog-button-min-width: 4.7rem;
|
|
24
13
|
--t007-dialog-button-width: unset;
|
|
25
14
|
--t007-dialog-button-max-width: unset;
|
|
26
|
-
--t007-dialog-button-min-height: 2.3rem;
|
|
27
15
|
--t007-dialog-button-height: unset;
|
|
28
16
|
--t007-dialog-button-max-height: unset;
|
|
29
|
-
--t007-
|
|
30
|
-
--t007-dialog-button-padding-block: 0.5rem;
|
|
31
|
-
--t007-dialog-button-border-radius: 1.25rem;
|
|
32
|
-
--t007-dialog-button-gap: 0.65rem;
|
|
33
|
-
--t007-button-outline-width: 0.15rem;
|
|
17
|
+
--t007-button-outline-width: 2px;
|
|
34
18
|
--t007-button-outline-style: solid;
|
|
35
|
-
--t007-dialog-
|
|
36
|
-
--t007-dialog-
|
|
37
|
-
--t007-dialog-
|
|
19
|
+
--t007-dialog-entry-duration: 150ms;
|
|
20
|
+
--t007-dialog-starting-transform: translateY(-100%);
|
|
21
|
+
--t007-dialog-starting-opacity: 0;
|
|
38
22
|
}
|
|
39
23
|
:where(:root, .t007-dialog) {
|
|
24
|
+
--t007-dialog-unit: 1rem;
|
|
25
|
+
--t007-dialog-gap: calc(var(--t007-dialog-unit) * 0.8);
|
|
26
|
+
--t007-dialog-width: calc(var(--t007-dialog-unit) * 26.5);
|
|
27
|
+
--t007-dialog-max-height: min(calc(var(--t007-dialog-unit) * 30), 90%);
|
|
28
|
+
--t007-dialog-max-content-height: calc(var(--t007-dialog-unit) * 12);
|
|
29
|
+
--t007-dialog-padding: var(--t007-dialog-unit);
|
|
30
|
+
--t007-dialog-content-padding-block: calc(var(--t007-dialog-unit) * 1.5);
|
|
31
|
+
--t007-dialog-border-radius: var(--t007-dialog-unit);
|
|
32
|
+
--t007-dialog-message-font-size: calc(var(--t007-dialog-unit) * 0.9);
|
|
33
|
+
--t007-dialog-button-font-size: calc(var(--t007-dialog-unit) * 0.85);
|
|
34
|
+
--t007-dialog-button-min-width: calc(var(--t007-dialog-unit) * 4.5);
|
|
35
|
+
--t007-dialog-button-min-height: calc(var(--t007-dialog-unit) * 2.3);
|
|
36
|
+
--t007-dialog-button-padding-inline: var(--t007-dialog-unit);
|
|
37
|
+
--t007-dialog-button-padding-block: calc(var(--t007-dialog-unit) * 0.5);
|
|
38
|
+
--t007-dialog-button-border-radius: calc(var(--t007-dialog-unit) * 1.25);
|
|
39
|
+
--t007-dialog-button-gap: calc(var(--t007-dialog-unit) * 0.65);
|
|
40
40
|
--t007-confirm-button-color: grey;
|
|
41
41
|
--t007-confirm-button-background: var(--t007-cancel-button-color);
|
|
42
42
|
--t007-cancel-button-color: greenyellow;
|
|
43
43
|
--t007-cancel-button-background: var(--t007-confirm-button-color);
|
|
44
|
-
--t007-confirm-button-outline-color: var(--t007-
|
|
44
|
+
--t007-confirm-button-outline-color: var(--t007-cancel-button-color);
|
|
45
45
|
--t007-cancel-button-outline-color: var(--t007-cancel-button-color);
|
|
46
46
|
}
|
|
47
47
|
:where(.t007-dialog) .t007-input-field {
|
|
@@ -62,11 +62,11 @@
|
|
|
62
62
|
cursor: not-allowed;
|
|
63
63
|
}
|
|
64
64
|
.t007-dialog *:focus {
|
|
65
|
-
outline-style:
|
|
66
|
-
outline-offset:
|
|
65
|
+
outline-style: solid;
|
|
66
|
+
outline-offset: 2px;
|
|
67
67
|
outline-width: 0;
|
|
68
68
|
}
|
|
69
|
-
.t007-dialog *:focus-visible {
|
|
69
|
+
.t007-dialog *:is(:focus-visible, :where(.focus-outline:focus)) {
|
|
70
70
|
outline-width: 0.15rem;
|
|
71
71
|
transition: none !important;
|
|
72
72
|
}
|
|
@@ -83,14 +83,19 @@
|
|
|
83
83
|
cursor: pointer;
|
|
84
84
|
filter: brightness(1.15);
|
|
85
85
|
}
|
|
86
|
-
:is(html, body):has(.t007-dialog
|
|
86
|
+
:is(html, body):has(.t007-dialog) {
|
|
87
87
|
overflow: hidden;
|
|
88
88
|
}
|
|
89
|
-
body:has(.t007-dialog
|
|
89
|
+
body:has(.t007-dialog) {
|
|
90
90
|
overflow-y: visible;
|
|
91
91
|
}
|
|
92
|
+
*:where(:has(> .t007-dialog.t007-dialog-scoped)) {
|
|
93
|
+
position: relative;
|
|
94
|
+
overflow: hidden !important;
|
|
95
|
+
}
|
|
92
96
|
.t007-dialog,
|
|
93
|
-
.t007-dialog::backdrop
|
|
97
|
+
.t007-dialog::backdrop,
|
|
98
|
+
.t007-dialog-backdrop {
|
|
94
99
|
position: fixed;
|
|
95
100
|
inset: 0;
|
|
96
101
|
margin: auto;
|
|
@@ -100,9 +105,18 @@ body:has(.t007-dialog[open]) {
|
|
|
100
105
|
transform,
|
|
101
106
|
opacity,
|
|
102
107
|
backdrop-filter;
|
|
103
|
-
transition-duration: var(--t007-dialog-
|
|
108
|
+
transition-duration: var(--t007-dialog-entry-duration);
|
|
104
109
|
transition-behavior: allow-discrete;
|
|
105
110
|
}
|
|
111
|
+
.t007-dialog.t007-dialog-scoped,
|
|
112
|
+
.t007-dialog-backdrop {
|
|
113
|
+
position: absolute;
|
|
114
|
+
}
|
|
115
|
+
.t007-dialog,
|
|
116
|
+
.t007-dialog form {
|
|
117
|
+
flex-direction: column;
|
|
118
|
+
gap: var(--t007-dialog-gap);
|
|
119
|
+
}
|
|
106
120
|
.t007-dialog {
|
|
107
121
|
padding: 0;
|
|
108
122
|
margin: auto;
|
|
@@ -114,44 +128,46 @@ body:has(.t007-dialog[open]) {
|
|
|
114
128
|
border: var(--t007-dialog-border);
|
|
115
129
|
border-radius: var(--t007-dialog-border-radius);
|
|
116
130
|
box-shadow: var(--t007-dialog-box-shadow);
|
|
117
|
-
opacity: var(--t007-dialog-
|
|
118
|
-
transform: var(--t007-dialog-
|
|
131
|
+
opacity: var(--t007-dialog-starting-opacity);
|
|
132
|
+
transform: var(--t007-dialog-starting-transform);
|
|
133
|
+
}
|
|
134
|
+
.t007-dialog.t007-dialog-scoped {
|
|
135
|
+
z-index: 2147483647;
|
|
119
136
|
}
|
|
120
137
|
.t007-dialog[open] {
|
|
138
|
+
display: flex;
|
|
121
139
|
opacity: 1;
|
|
122
140
|
transform: none;
|
|
123
141
|
}
|
|
124
142
|
@starting-style {
|
|
125
143
|
.t007-dialog[open] {
|
|
126
|
-
opacity: var(--t007-dialog-
|
|
127
|
-
transform: var(--t007-dialog-
|
|
144
|
+
opacity: var(--t007-dialog-starting-opacity);
|
|
145
|
+
transform: var(--t007-dialog-starting-transform);
|
|
128
146
|
}
|
|
129
147
|
}
|
|
130
|
-
.t007-dialog,
|
|
131
|
-
.t007-dialog
|
|
132
|
-
flex-direction: column;
|
|
133
|
-
gap: var(--t007-dialog-gap);
|
|
134
|
-
}
|
|
135
|
-
.t007-dialog[open],
|
|
136
|
-
.t007-dialog form {
|
|
137
|
-
display: flex;
|
|
138
|
-
}
|
|
139
|
-
.t007-dialog::backdrop {
|
|
148
|
+
.t007-dialog::backdrop,
|
|
149
|
+
.t007-dialog-backdrop {
|
|
140
150
|
background-color: transparent;
|
|
141
151
|
}
|
|
142
|
-
.t007-dialog
|
|
152
|
+
.t007-dialog-backdrop {
|
|
153
|
+
z-index: 2147483646;
|
|
154
|
+
}
|
|
155
|
+
.t007-dialog[open]::backdrop,
|
|
156
|
+
.t007-dialog.t007-dialog-scoped[open] + .t007-dialog-backdrop {
|
|
143
157
|
display: block;
|
|
144
158
|
background-color: var(--t007-dialog-backdrop-background);
|
|
145
|
-
-webkit-backdrop-filter: var(--t007-dialog-backdrop-
|
|
146
|
-
backdrop-filter: var(--t007-dialog-backdrop-
|
|
159
|
+
-webkit-backdrop-filter: var(--t007-dialog-backdrop-filter);
|
|
160
|
+
backdrop-filter: var(--t007-dialog-backdrop-filter);
|
|
147
161
|
}
|
|
148
162
|
@starting-style {
|
|
149
|
-
.t007-dialog[open]::backdrop
|
|
163
|
+
.t007-dialog[open]::backdrop,
|
|
164
|
+
.t007-dialog.t007-dialog-scoped[open] + .t007-dialog-backdrop {
|
|
150
165
|
background-color: transparent;
|
|
151
166
|
}
|
|
152
167
|
}
|
|
153
168
|
.t007-dialog > * {
|
|
154
169
|
padding-inline: var(--t007-dialog-padding);
|
|
170
|
+
font-weight: 500;
|
|
155
171
|
}
|
|
156
172
|
.t007-dialog-top-section {
|
|
157
173
|
margin-top: var(--t007-dialog-padding);
|
|
@@ -168,6 +184,9 @@ body:has(.t007-dialog[open]) {
|
|
|
168
184
|
.t007-dialog:has(.field) .t007-dialog-top-section {
|
|
169
185
|
padding-block-end: 0;
|
|
170
186
|
}
|
|
187
|
+
.t007-dialog form {
|
|
188
|
+
display: flex;
|
|
189
|
+
}
|
|
171
190
|
.t007-dialog-question {
|
|
172
191
|
color: var(--t007-dialog-message-color);
|
|
173
192
|
font-size: var(--t007-dialog-message-font-size);
|
package/dist/index.d.ts
CHANGED
|
@@ -2,40 +2,60 @@ import "@t007/utils";
|
|
|
2
2
|
import type { FieldOptions } from "@t007/input";
|
|
3
3
|
|
|
4
4
|
/** Shared options for alert, confirm, and prompt dialogs. */
|
|
5
|
-
export interface DialogOptions
|
|
6
|
-
/**
|
|
5
|
+
export interface DialogOptions {
|
|
6
|
+
/** Unique identifier for the dialog for programmatic dismissal. Auto-generated by default. */
|
|
7
|
+
id?: string;
|
|
8
|
+
/** Text for the confirmation button. Defaults to "OK". */
|
|
7
9
|
confirmText?: string;
|
|
8
|
-
/** Text for the cancel button. */
|
|
10
|
+
/** Text for the cancel button where applicable. Defaults to "Cancel". */
|
|
9
11
|
cancelText?: string;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
+
/** Browser behavior for handling dialog closure. Defaults to "any". */
|
|
13
|
+
closedBy?: HTMLDialogElement["closedBy"];
|
|
14
|
+
/** Render dialog inside a specific root element instead of document body. */
|
|
15
|
+
rootElement?: HTMLElement;
|
|
16
|
+
/** Whether to restrict interactions to `rootElement` if provided. Defaults to `true`. */
|
|
17
|
+
scoped?: boolean;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
// BUNDLE EXPORTS & GLOBAL DECLARATIONS
|
|
15
21
|
|
|
22
|
+
/** Check if a dialog is currently active. Optionally check for a specific dialog by id.
|
|
23
|
+
* @param id Optional unique identifier of the dialog to check.
|
|
24
|
+
* @return `true` if the specified dialog (or any dialog when no id is provided) is active, otherwise `false`.
|
|
25
|
+
*/
|
|
26
|
+
export function isActive(id?: string): boolean;
|
|
16
27
|
/** Show an alert dialog and resolve when the user confirms.
|
|
17
28
|
* @param message Message shown in the dialog body.
|
|
18
29
|
* @param options Optional dialog configuration.
|
|
19
30
|
* @returns Promise that resolves to the user choice.
|
|
20
31
|
*/
|
|
21
|
-
export function
|
|
32
|
+
export function alert(message: string, options?: DialogOptions): Promise<boolean>;
|
|
22
33
|
/** Show a confirm dialog and resolve with the user choice.
|
|
23
34
|
* @param question Question shown in the dialog body.
|
|
24
35
|
* @param options Optional dialog configuration.
|
|
25
36
|
* @returns Promise that resolves to true when confirmed.
|
|
26
37
|
*/
|
|
27
|
-
export function
|
|
38
|
+
export function confirm(question: string, options?: DialogOptions): Promise<boolean>;
|
|
28
39
|
/** Show a prompt dialog and resolve with the entered value.
|
|
29
40
|
* @param question Question shown in the dialog body.
|
|
30
41
|
* @param defaultValue Initial value for the prompt input.
|
|
31
42
|
* @param options Optional dialog configuration.
|
|
32
43
|
* @returns Promise that resolves to the entered string or null.
|
|
33
44
|
*/
|
|
34
|
-
export function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
export function prompt(question: string, defaultValue?: string, options?: DialogOptions & FieldOptions): Promise<string | null>;
|
|
46
|
+
/** Dismiss a dialog by id or all dialogs when no id is provided.
|
|
47
|
+
* @param id Optional unique identifier of the dialog to dismiss. If not provided, all dialogs will be dismissed.
|
|
48
|
+
* @param response Optional value to resolve the dialog's promise with. If not provided, defaults to `true` for confirm and alert dialogs, and `null` for prompt dialogs.
|
|
49
|
+
*/
|
|
50
|
+
export function dismiss(id?: string, response?: any): void;
|
|
51
|
+
|
|
52
|
+
interface Dialog {
|
|
53
|
+
isActive(id?: string): boolean;
|
|
54
|
+
alert(message: string, options?: DialogOptions): Promise<boolean>;
|
|
55
|
+
confirm(question: string, options?: DialogOptions): Promise<boolean>;
|
|
56
|
+
prompt(question: string, defaultValue?: string, options?: DialogOptions & FieldOptions): Promise<string | null>;
|
|
57
|
+
dismiss(id?: string, response?: any): void;
|
|
58
|
+
}
|
|
39
59
|
|
|
40
60
|
declare global {
|
|
41
61
|
interface T007Namespace {
|
|
@@ -45,6 +65,8 @@ declare global {
|
|
|
45
65
|
confirm: typeof Confirm;
|
|
46
66
|
/** Browser prompt helper. */
|
|
47
67
|
prompt: typeof Prompt;
|
|
68
|
+
/** Dialog management object. */
|
|
69
|
+
dialog: Dialog;
|
|
48
70
|
}
|
|
49
71
|
interface Window {
|
|
50
72
|
Alert?: T007Namespace["alert"];
|
package/dist/index.global.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
-
//
|
|
3
|
+
// ../../../sia-reactor/dist/chunk-IBAPWB27.js
|
|
4
|
+
function clamp(min = 0, val, max = Infinity) {
|
|
5
|
+
return Math.min(Math.max(val, min), max);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// ../../../sia-reactor/dist/chunk-RI45W4O6.js
|
|
4
9
|
function createEl(tag, props, dataset, styles, el = tag ? document?.createElement(tag) : null) {
|
|
5
10
|
return assignEl(el, props, dataset, styles), el;
|
|
6
11
|
}
|
|
@@ -16,8 +21,12 @@
|
|
|
16
21
|
for (const k of Object.keys(styles)) if (styles[k] !== void 0) el.style[k] = styles[k];
|
|
17
22
|
}
|
|
18
23
|
}
|
|
24
|
+
function getActiveEl(root) {
|
|
25
|
+
const activeEl = (root ?? document).activeElement;
|
|
26
|
+
return !activeEl ? null : activeEl.shadowRoot ? getActiveEl(activeEl.shadowRoot) : activeEl;
|
|
27
|
+
}
|
|
19
28
|
|
|
20
|
-
//
|
|
29
|
+
// ../../../sia-reactor/dist/chunk-3OT72G7R.js
|
|
21
30
|
function onAllMethods(owner, callback, skipOwn = true, nested = false) {
|
|
22
31
|
let proto = owner;
|
|
23
32
|
while (proto && proto !== Object.prototype) {
|
|
@@ -35,7 +44,24 @@
|
|
|
35
44
|
});
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
//
|
|
47
|
+
// ../../../sia-reactor/dist/chunk-EZ4VRGYI.js
|
|
48
|
+
var CTX = {
|
|
49
|
+
/** Flag indicating whether the application is running in development mode. */
|
|
50
|
+
isDevEnv: "undefined" !== typeof process ? process.env.NODE_ENV !== "production" : true,
|
|
51
|
+
/** Flag indicating whether a cascade is currently ongoing so reactors can allow all writes. */
|
|
52
|
+
isCascading: false,
|
|
53
|
+
/** Active `Autotracker` instance, override for automatic dependency collection on `Reactor` traps. */
|
|
54
|
+
autotracker: null
|
|
55
|
+
};
|
|
56
|
+
var RTR_BATCH = "undefined" !== typeof window ? ("undefined" !== typeof queueMicrotask ? queueMicrotask : setTimeout).bind(window) : "undefined" !== typeof process && process.nextTick ? process.nextTick : setTimeout;
|
|
57
|
+
var RTR_LOG = console.log.bind(console, "[S.I.A Reactor]");
|
|
58
|
+
var NIL = Object.freeze({});
|
|
59
|
+
var NOOP = () => {
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ../utils/dist/chunk-N5KX6IW4.js
|
|
63
|
+
var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
|
|
64
|
+
var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
|
|
39
65
|
var VIRTUAL_RESOURCE = /* @__PURE__ */ Symbol.for("T007_VIRTUAL_RESOURCE");
|
|
40
66
|
function loadResource(req, type = "style", { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
|
|
41
67
|
w.t007 ??= {}, w.t007._resourceCache ??= {};
|
|
@@ -64,12 +90,21 @@
|
|
|
64
90
|
});
|
|
65
91
|
return w.t007._resourceCache[src];
|
|
66
92
|
}
|
|
93
|
+
function isDef(val) {
|
|
94
|
+
return "undefined" !== typeof val;
|
|
95
|
+
}
|
|
67
96
|
function isSym(val) {
|
|
68
97
|
return "symbol" === typeof val;
|
|
69
98
|
}
|
|
70
99
|
function isStr(val) {
|
|
71
100
|
return "string" === typeof val;
|
|
72
101
|
}
|
|
102
|
+
function uid(prefix = "") {
|
|
103
|
+
return prefix + Date.now().toString(36) + "_" + performance.now().toString(36).replace(".", "") + "_" + Math.random().toString(36).slice(2);
|
|
104
|
+
}
|
|
105
|
+
function parseCSSTime(time) {
|
|
106
|
+
return time?.endsWith?.("ms") ? parseFloat(time) : parseFloat(time) * 1e3;
|
|
107
|
+
}
|
|
73
108
|
function isSameURL(src1, src2) {
|
|
74
109
|
if (!isStr(src1) || !isStr(src2) || !src1 || !src2) return false;
|
|
75
110
|
try {
|
|
@@ -80,8 +115,7 @@
|
|
|
80
115
|
}
|
|
81
116
|
}
|
|
82
117
|
if ("undefined" !== typeof window) {
|
|
83
|
-
window.t007 ??= {};
|
|
84
|
-
t007.VIRTUAL_RESOURCE = VIRTUAL_RESOURCE;
|
|
118
|
+
(window.t007 ??= {}).VIRTUAL_RESOURCE = VIRTUAL_RESOURCE;
|
|
85
119
|
window.T007_TOAST_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest`;
|
|
86
120
|
window.T007_INPUT_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest`;
|
|
87
121
|
window.T007_DIALOG_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest`;
|
|
@@ -90,138 +124,358 @@
|
|
|
90
124
|
window.T007_DIALOG_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest/dist/index.min.css`;
|
|
91
125
|
}
|
|
92
126
|
|
|
93
|
-
//
|
|
127
|
+
// ../utils/dist/chunk-NLR4ANGT.js
|
|
128
|
+
function initOutsideClick(el, { enabled = false, onOutsideClick = NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowInputs = false, root = window, scoped = true, capture = true } = NIL) {
|
|
129
|
+
const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
130
|
+
if (!enabled || existing) return existing ? existing : void 0;
|
|
131
|
+
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
132
|
+
const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = el.getBoundingClientRect()) => {
|
|
133
|
+
if (stack.at(-1) !== el || p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom) return false;
|
|
134
|
+
return (!scoped ? true : root.contains(t)) && onOutsideClick(e);
|
|
135
|
+
}, handleClick = ((e) => outOnClick && !(allowInputs && isInteractive(e.target)) && onScopedOut(e, e.target)), handleEscape = ((e) => outOnEscape && e.key === "Escape" && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey && stack.at(-1) === el && onOutsideClick(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
|
|
136
|
+
root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
|
|
137
|
+
root.addEventListener("keydown", handleEscape, capture), el.addEventListener("focusout", handleFocusOut, capture);
|
|
138
|
+
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
139
|
+
const destroy = () => {
|
|
140
|
+
root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
|
|
141
|
+
root.removeEventListener("keydown", handleEscape, capture);
|
|
142
|
+
el.removeEventListener("focusout", handleFocusOut, capture);
|
|
143
|
+
t007._outsiders.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
144
|
+
};
|
|
145
|
+
return t007._outsiders.set(el, destroy), destroy;
|
|
146
|
+
}
|
|
147
|
+
var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
|
|
148
|
+
function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = NIL) {
|
|
149
|
+
const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
150
|
+
if (!enabled || existing) return existing ? existing : void 0;
|
|
151
|
+
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
152
|
+
const stack = stacks.get(root) ?? [], focused = document.querySelector(":focus"), initial = el.querySelector(initialSelector), first = createEl("span", { tabIndex: 0 }, { focusGuard: "start" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), last = createEl("span", { tabIndex: 0 }, { focusGuard: "end" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), getFocusable = (c = el) => [...c.querySelectorAll(INTERACTIVE_SELECTOR)], resetFocus = (i = 0, els = getFocusable()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false, rt = root) => {
|
|
153
|
+
if (!scoped) return resetFocus(pre ? -1 : 0);
|
|
154
|
+
if (rt.hasAttribute("tabindex")) return rt.focus();
|
|
155
|
+
const items = getFocusable();
|
|
156
|
+
if (!items.length) return resetFocus(0, null);
|
|
157
|
+
const ceiling = document.fullscreenElement || document.querySelector("dialog:modal") || document.body;
|
|
158
|
+
let p = rt.parentElement || ceiling, all = getFocusable(p);
|
|
159
|
+
while (p !== ceiling && (!all.length || rt.contains(all[0]) && rt.contains(all.at(-1)))) all = getFocusable(p = p.parentElement || ceiling);
|
|
160
|
+
for (let target, len = all.length, i = all.indexOf(items[pre ? 0 : items.length - 1]) + (pre ? -1 : 1); pre ? i >= 0 : i < len; pre ? i-- : i++) if (!rt.contains(target = all[i])) return target.focus();
|
|
161
|
+
(pre ? first : last).blur();
|
|
162
|
+
}, handleFocusIn = () => {
|
|
163
|
+
if (document.querySelector("dialog:modal") && !el.matches("dialog:modal")) return;
|
|
164
|
+
stack.at(-1) === el && getActiveEl(el.ownerDocument) !== root && !el.contains(getActiveEl(el.ownerDocument)) && resetFocus();
|
|
165
|
+
}, handleInitialBlur = () => initial.classList.remove(ringClassName);
|
|
166
|
+
first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
|
|
167
|
+
last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
|
|
168
|
+
root.addEventListener("focusin", handleFocusIn, capture);
|
|
169
|
+
if (!el.querySelector(":focus")) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
|
|
170
|
+
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
171
|
+
const destroy = () => {
|
|
172
|
+
focused?.isConnected && focused.focus(), first.remove(), last.remove();
|
|
173
|
+
root.removeEventListener("focusin", handleFocusIn, capture);
|
|
174
|
+
initial?.removeEventListener("blur", handleInitialBlur, capture);
|
|
175
|
+
t007._ftrappers.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
176
|
+
};
|
|
177
|
+
return t007._ftrappers.set(el, destroy), destroy;
|
|
178
|
+
}
|
|
179
|
+
var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
|
|
180
|
+
var getTargetIndex = ({ key, currIndex, length, gridX, gridY, vGridY, loop, ctrlKey = false, rtl }) => {
|
|
181
|
+
const rowStart = currIndex - currIndex % gridX, rowEnd = Math.min(rowStart + gridX - 1, length - 1), colStart = currIndex % gridX, colEnd = Math.min(colStart + gridX * (gridY - 1), length - 1), canX = gridX > 1, canY = gridY > 1, horizontalMove = rtl ? { ArrowRight: canX ? -1 : 0, ArrowLeft: canX ? 1 : 0 } : { ArrowRight: canX ? 1 : 0, ArrowLeft: canX ? -1 : 0 }, move = { ...horizontalMove, ArrowDown: canY ? gridX : 0, ArrowUp: canY ? -gridX : 0, Home: ctrlKey ? 0 : rowStart, End: ctrlKey ? length - 1 : rowEnd, PageDown: (vGridY - 1) * gridX, PageUp: -(vGridY - 1) * gridX }[key] ?? 0;
|
|
182
|
+
let targetIndex = key === "Home" || key === "End" ? move : currIndex + move;
|
|
183
|
+
if (key === "ArrowDown") {
|
|
184
|
+
if (!loop && targetIndex >= length) targetIndex -= move;
|
|
185
|
+
} else if (key === "ArrowUp") {
|
|
186
|
+
if (!loop && targetIndex < 0) targetIndex += Math.abs(move);
|
|
187
|
+
} else if (key === "PageDown") {
|
|
188
|
+
if (!loop && targetIndex >= length) targetIndex = colEnd;
|
|
189
|
+
} else if (key === "PageUp") {
|
|
190
|
+
if (!loop && targetIndex < 0) targetIndex = colStart;
|
|
191
|
+
}
|
|
192
|
+
return loop ? (targetIndex + length) % length : clamp(0, targetIndex, length - 1);
|
|
193
|
+
};
|
|
194
|
+
var getCommonAncestor = (first, second) => {
|
|
195
|
+
if (!first) return null;
|
|
196
|
+
if (!second) return first.parentElement;
|
|
197
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
198
|
+
let current = first;
|
|
199
|
+
while (current) ancestors.add(current), current = current.parentElement;
|
|
200
|
+
current = second;
|
|
201
|
+
while (current) {
|
|
202
|
+
if (ancestors.has(current)) return current;
|
|
203
|
+
current = current.parentElement;
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
};
|
|
207
|
+
var getGrid = (all, x = true, y = true, vY = true) => {
|
|
208
|
+
const len = all.length, grid = {};
|
|
209
|
+
if (!len) return grid;
|
|
210
|
+
let cols = all.findIndex((el) => el.offsetTop !== all[0].offsetTop);
|
|
211
|
+
cols = cols > 0 ? cols : len;
|
|
212
|
+
if (x) grid.x = cols;
|
|
213
|
+
let rows = Math.ceil(len / cols);
|
|
214
|
+
if (y) grid.y = rows;
|
|
215
|
+
if (vY) {
|
|
216
|
+
const itemHeight = all[0].offsetHeight ?? 0, containerHeight = getCommonAncestor(all[0], all[1])?.clientHeight ?? 0;
|
|
217
|
+
rows = clamp(1, Math.floor(containerHeight / itemHeight), rows) || rows;
|
|
218
|
+
grid.vY = rows;
|
|
219
|
+
}
|
|
220
|
+
return grid;
|
|
221
|
+
};
|
|
222
|
+
var H_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
|
|
223
|
+
var V_NAV_KEYS = ["ArrowUp", "ArrowDown", "PageDown", "PageUp"];
|
|
224
|
+
var NAV_KEYS = [...H_NAV_KEYS, ...V_NAV_KEYS];
|
|
225
|
+
var DEFAULT_CONFIG = {
|
|
226
|
+
enabled: null,
|
|
227
|
+
selector: "[data-arrow-item]",
|
|
228
|
+
focusOnHover: true,
|
|
229
|
+
loop: true,
|
|
230
|
+
virtual: false,
|
|
231
|
+
typeahead: false,
|
|
232
|
+
rovingTab: null,
|
|
233
|
+
defaultTabbableIndex: null,
|
|
234
|
+
baseTabIndex: "0",
|
|
235
|
+
resetMs: 500,
|
|
236
|
+
rtl: null,
|
|
237
|
+
grid: {},
|
|
238
|
+
activeClass: "focus-outlined",
|
|
239
|
+
inputSelector: "input,textarea,[contenteditable='true']",
|
|
240
|
+
focusOptions: { preventScroll: false },
|
|
241
|
+
scrollIntoView: { block: "nearest", inline: "nearest" },
|
|
242
|
+
onSelect: NOOP,
|
|
243
|
+
onFocusOut: NOOP
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// ../utils/dist/hooks/vanilla.js
|
|
247
|
+
function initArrowNavigation(container, config = {}) {
|
|
248
|
+
const existing = (t007._arrownavs ??= /* @__PURE__ */ new WeakMap()).get(container);
|
|
249
|
+
if (!config.enabled || existing) return existing ? existing : void 0;
|
|
250
|
+
const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut, rovingTab } = { ...DEFAULT_CONFIG, ...config };
|
|
251
|
+
let gridX = grid.x || 1, gridY = grid.y || 1, vGridY = grid.vY || 1, activeIndex = -1, buffer = "", timeout = null, items = [];
|
|
252
|
+
const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? getComputedStyle(container).direction === "rtl", shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.hasAttribute("disabled") || el.hasAttribute("aria-disabled"), getItems = () => items = Array.from(container.querySelectorAll(selector));
|
|
253
|
+
const getAbleIndex = (targetIndex, e = { key: "ArrowRight", ctrlKey: false }) => {
|
|
254
|
+
if (shouldSnub() || !items.length) return null;
|
|
255
|
+
let index = targetIndex;
|
|
256
|
+
let attempts = 0;
|
|
257
|
+
while (attempts < items.length) {
|
|
258
|
+
if (!isItemDisabled(items[index])) return index;
|
|
259
|
+
index = getTargetIndex({ key: e.key, currIndex: index, gridX, gridY, vGridY, length: items.length, loop, ctrlKey: e.ctrlKey, rtl });
|
|
260
|
+
attempts++;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
};
|
|
264
|
+
const goToIndex = (targetIndex, e = { key: "ArrowRight" }) => {
|
|
265
|
+
if (shouldSnub()) return;
|
|
266
|
+
const idx = getAbleIndex(targetIndex, e);
|
|
267
|
+
if (idx === null) return;
|
|
268
|
+
resetActiveIndex(idx);
|
|
269
|
+
onSelect?.(items[idx], e), updateDOM();
|
|
270
|
+
if (virtual) {
|
|
271
|
+
items[idx]?.scrollIntoView(scrollIntoView);
|
|
272
|
+
container.setAttribute("aria-activedescendant", items[idx].id || "");
|
|
273
|
+
} else items[idx]?.focus(focusOptions);
|
|
274
|
+
};
|
|
275
|
+
const updateDOM = () => {
|
|
276
|
+
if (shouldSnub() || !virtual && !roving || !items.length) return;
|
|
277
|
+
const hasDefaultTabbable = defaultTabbableIndex !== null && defaultTabbableIndex !== void 0, tabbableIndex = hasDefaultTabbable && !isItemDisabled(items[defaultTabbableIndex]) ? defaultTabbableIndex : getAbleIndex(0);
|
|
278
|
+
for (let i = 0, len = items.length; i < len; i++) {
|
|
279
|
+
const el = items[i], isActive2 = i === activeIndex;
|
|
280
|
+
if (roving) el.setAttribute("tabindex", i === activeIndex || activeIndex === -1 && i === tabbableIndex ? baseTabIndex : "-1");
|
|
281
|
+
else if (virtual && activeIndex > 0) el.setAttribute("tabindex", i > activeIndex ? baseTabIndex : "-1");
|
|
282
|
+
else el.setAttribute("tabindex", baseTabIndex);
|
|
283
|
+
if (virtual) el.setAttribute("aria-selected", String(isActive2)), el.classList.toggle(activeClass, isActive2);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
const resetActiveIndex = (index = -1) => (activeIndex = index, updateDOM());
|
|
287
|
+
const typeAhead = (key) => {
|
|
288
|
+
if (shouldSnub() || !typeahead) return;
|
|
289
|
+
buffer += key.toLowerCase();
|
|
290
|
+
if (timeout) clearTimeout(timeout);
|
|
291
|
+
timeout = setTimeout(() => buffer = "", resetMs);
|
|
292
|
+
const start = activeIndex >= 0 ? activeIndex + 1 : 0;
|
|
293
|
+
for (let i = 0; i < items.length; i++) {
|
|
294
|
+
const idx = (start + i) % items.length, label = (items[idx].getAttribute("data-label") || items[idx].innerText || "").trim().toLowerCase();
|
|
295
|
+
if (label.startsWith(buffer)) return goToIndex(idx);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
const simulateKey = (e) => {
|
|
299
|
+
const t = e.target;
|
|
300
|
+
if (shouldSnub() || getActiveEl(t?.ownerDocument)?.matches("option")) return;
|
|
301
|
+
const { key } = e;
|
|
302
|
+
if (!items.length) return;
|
|
303
|
+
if (virtual && (key === " " || key === "Enter")) return items[activeIndex]?.click();
|
|
304
|
+
if (t?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
|
|
305
|
+
if (typeahead && key.length === 1 && /^[a-z0-9]$/i.test(key)) return typeAhead(key);
|
|
306
|
+
if (!NAV_KEYS.includes(key)) return;
|
|
307
|
+
if (!(e.currentTarget?.matches(DEFAULT_CONFIG.inputSelector) && gridX <= 1 && H_NAV_KEYS.includes(key))) e.preventDefault?.(), e.stopPropagation?.();
|
|
308
|
+
const currIndex = virtual ? activeIndex : items.indexOf(getActiveEl(t?.ownerDocument)), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: items.length, loop, rtl, key, ctrlKey: e.ctrlKey });
|
|
309
|
+
goToIndex(targetIndex, e);
|
|
310
|
+
};
|
|
311
|
+
getItems(), updateDOM();
|
|
312
|
+
const interactiveEls = !virtual ? [container] : [container.querySelector(inputSelector)];
|
|
313
|
+
for (const el of interactiveEls) el?.addEventListener("keydown", simulateKey);
|
|
314
|
+
const handleFocusOut = (e) => {
|
|
315
|
+
if (!container.contains(e.relatedTarget)) return resetActiveIndex(), updateDOM(), onFocusOut?.(e);
|
|
316
|
+
const among = items.includes(e.relatedTarget);
|
|
317
|
+
if (!among && (defaultTabbableIndex ?? -1) >= 0) return resetActiveIndex(), updateDOM();
|
|
318
|
+
if (virtual) resetActiveIndex(), updateDOM();
|
|
319
|
+
};
|
|
320
|
+
container.addEventListener("focusout", handleFocusOut);
|
|
321
|
+
const handleHover = (e) => {
|
|
322
|
+
if (!enabled || !focusOnHover) return;
|
|
323
|
+
const el = e.currentTarget, idx = items.indexOf(el);
|
|
324
|
+
if (idx !== -1) goToIndex(idx);
|
|
325
|
+
};
|
|
326
|
+
for (const el of items) el.addEventListener("mouseenter", handleHover);
|
|
327
|
+
const mutationObserver = new MutationObserver(() => {
|
|
328
|
+
const oldEl = items[activeIndex];
|
|
329
|
+
getItems();
|
|
330
|
+
const newEl = items[activeIndex];
|
|
331
|
+
updateDOM();
|
|
332
|
+
if (oldEl && newEl && oldEl === newEl) return;
|
|
333
|
+
resetActiveIndex();
|
|
334
|
+
updateDOM();
|
|
335
|
+
});
|
|
336
|
+
mutationObserver.observe(container, { childList: true, subtree: true });
|
|
337
|
+
const setGrid = (g) => {
|
|
338
|
+
if (g.x !== void 0) gridX = g.x;
|
|
339
|
+
if (g.y !== void 0) gridY = g.y;
|
|
340
|
+
if (g.vY !== void 0) vGridY = g.vY;
|
|
341
|
+
};
|
|
342
|
+
const calcGrid = () => setGrid(getGrid(items, !grid.x, !grid.y, !grid.vY));
|
|
343
|
+
setGrid(grid), calcGrid();
|
|
344
|
+
const ancestor = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
|
|
345
|
+
const resizeObserver = new ResizeObserver(calcGrid);
|
|
346
|
+
if (ancestor) resizeObserver.observe(ancestor);
|
|
347
|
+
const destroy = () => {
|
|
348
|
+
for (const el of interactiveEls) el?.removeEventListener("keydown", simulateKey);
|
|
349
|
+
container.removeEventListener("focusout", handleFocusOut);
|
|
350
|
+
for (const el of items) el.removeEventListener("mouseenter", handleHover);
|
|
351
|
+
mutationObserver.disconnect(), resizeObserver.disconnect();
|
|
352
|
+
if (timeout) clearTimeout(timeout);
|
|
353
|
+
};
|
|
354
|
+
const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, destroy };
|
|
355
|
+
return t007._arrownavs.set(container, handle), handle;
|
|
356
|
+
}
|
|
357
|
+
var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
|
|
358
|
+
|
|
359
|
+
// src/js/index.js
|
|
94
360
|
var T007_Dialog = class {
|
|
95
|
-
|
|
96
|
-
confirmBtn;
|
|
97
|
-
cancelBtn;
|
|
98
|
-
constructor(resolve) {
|
|
361
|
+
constructor(resolve, { id, closedBy: by, rootElement: root = document.body, scoped = true }) {
|
|
99
362
|
bindAllMethods(this);
|
|
100
363
|
this.resolve = resolve;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
closedBy: "any"
|
|
105
|
-
})
|
|
106
|
-
);
|
|
364
|
+
t007.dialogs.set(this.id = id ?? uid("t007_dialog_"), this);
|
|
365
|
+
this.rootElement = root?.isConnected ? root : document.body, this.rootScoped = this.rootElement !== document.body, this.scoped = this.rootScoped && scoped;
|
|
366
|
+
this.rootElement.append(this.dialog = createEl("dialog", { closedBy: by || (this.rootScoped ? "none" : "any"), className: `t007-dialog${this.rootScoped ? " t007-dialog-scoped" : ""}`, id: this.id }), this.rootScoped ? this.backdrop = createEl("div", { className: "t007-dialog-backdrop", id: `${this.id}_backdrop` }) : null);
|
|
107
367
|
this.dialog.addEventListener("cancel", this.cancel);
|
|
368
|
+
initArrowNavigation(this.dialog, { enabled: true, rovingTab: false });
|
|
369
|
+
this.rootScoped && !by && initOutsideClick(this.dialog, { enabled: true, onOutsideClick: this.cancel, root: this.rootElement, scoped: this.scoped });
|
|
108
370
|
}
|
|
109
371
|
show() {
|
|
110
|
-
this.dialog.showModal();
|
|
111
|
-
this.
|
|
372
|
+
this.dialog[this.rootScoped ? "show" : "showModal"]();
|
|
373
|
+
initFocusTrap(this.dialog, { enabled: true, root: this.rootElement, scoped: this.scoped });
|
|
112
374
|
}
|
|
113
375
|
remove() {
|
|
114
|
-
this.dialog.
|
|
115
|
-
this.dialog.
|
|
376
|
+
removeArrowNavigation(this.dialog), removeFocusTrap(this.dialog), removeOutsideClick(this.dialog);
|
|
377
|
+
this.dialog.open && this.dialog.close(), t007.dialogs.delete(this.id);
|
|
378
|
+
setTimeout(() => (this.dialog.remove(), this.backdrop?.remove()), parseCSSTime(getComputedStyle(this.dialog).transitionDuration || "150ms"));
|
|
116
379
|
}
|
|
117
|
-
confirm() {
|
|
118
|
-
this.remove();
|
|
119
|
-
this.resolve(true);
|
|
380
|
+
confirm(res = true) {
|
|
381
|
+
this.remove(), this.resolve(res instanceof Event ? true : res);
|
|
120
382
|
}
|
|
121
|
-
cancel() {
|
|
122
|
-
this.remove();
|
|
123
|
-
this.resolve(false);
|
|
383
|
+
cancel(res = false) {
|
|
384
|
+
this.remove(), this.resolve(res instanceof Event ? false : res);
|
|
124
385
|
}
|
|
125
386
|
};
|
|
126
387
|
var T007_Alert_Dialog = class extends T007_Dialog {
|
|
127
|
-
constructor({ message, resolve, options }) {
|
|
128
|
-
super(resolve);
|
|
129
|
-
this.render(message, options);
|
|
388
|
+
constructor({ message, resolve, options = {} }) {
|
|
389
|
+
super(resolve, options), this.render(message, options);
|
|
130
390
|
}
|
|
131
|
-
render(message, options
|
|
391
|
+
render(message, options) {
|
|
132
392
|
this.dialog.innerHTML = `
|
|
133
|
-
<div class="t007-dialog-top-section">
|
|
134
|
-
<
|
|
393
|
+
<div class="t007-dialog-top-section" tabindex="-1">
|
|
394
|
+
<div class="t007-dialog-question">${message}</div>
|
|
135
395
|
</div>
|
|
136
396
|
<div class="t007-dialog-bottom-section">
|
|
137
|
-
<button class="t007-dialog-confirm-button"
|
|
397
|
+
<button type="button" data-autofocus data-arrow-item class="t007-dialog-confirm-button">${options.confirmText || "OK"}</button>
|
|
138
398
|
</div>
|
|
139
399
|
`;
|
|
140
|
-
this.
|
|
141
|
-
this.confirmBtn.addEventListener("click", this.confirm);
|
|
400
|
+
this.dialog.querySelector(".t007-dialog-confirm-button").addEventListener("click", this.confirm);
|
|
142
401
|
this.show();
|
|
143
402
|
}
|
|
144
403
|
};
|
|
145
404
|
var T007_Confirm_Dialog = class extends T007_Dialog {
|
|
146
|
-
constructor({ question, resolve, options }) {
|
|
147
|
-
super(resolve);
|
|
148
|
-
this.render(question, options);
|
|
405
|
+
constructor({ question, resolve, options = {} }) {
|
|
406
|
+
super(resolve, options), this.render(question, options);
|
|
149
407
|
}
|
|
150
|
-
render(question, options
|
|
408
|
+
render(question, options) {
|
|
151
409
|
this.dialog.innerHTML = `
|
|
152
|
-
<div class="t007-dialog-top-section">
|
|
153
|
-
<
|
|
410
|
+
<div class="t007-dialog-top-section" tabindex="-1">
|
|
411
|
+
<div class="t007-dialog-question">${question}</div>
|
|
154
412
|
</div>
|
|
155
413
|
<div class="t007-dialog-bottom-section">
|
|
156
|
-
<button class="t007-dialog-confirm-button"
|
|
157
|
-
<button class="t007-dialog-cancel-button"
|
|
414
|
+
<button type="button" data-autofocus data-arrow-item class="t007-dialog-confirm-button">${options.confirmText || "OK"}</button>
|
|
415
|
+
<button type="button" data-arrow-item class="t007-dialog-cancel-button">${options.cancelText || "Cancel"}</button>
|
|
158
416
|
</div>
|
|
159
417
|
`;
|
|
160
|
-
this.
|
|
161
|
-
this.
|
|
162
|
-
this.confirmBtn.addEventListener("click", this.confirm);
|
|
163
|
-
this.cancelBtn.addEventListener("click", this.cancel);
|
|
418
|
+
this.dialog.querySelector(".t007-dialog-confirm-button").addEventListener("click", this.confirm);
|
|
419
|
+
this.dialog.querySelector(".t007-dialog-cancel-button").addEventListener("click", this.cancel);
|
|
164
420
|
this.show();
|
|
165
421
|
}
|
|
166
422
|
};
|
|
167
423
|
var T007_Prompt_Dialog = class extends T007_Dialog {
|
|
168
|
-
constructor({ question, defaultValue, resolve, options }) {
|
|
169
|
-
super(resolve);
|
|
170
|
-
this.render(question, defaultValue, options);
|
|
424
|
+
constructor({ question, defaultValue, resolve, options = {} }) {
|
|
425
|
+
super(resolve, options), this.render(question, defaultValue, options);
|
|
171
426
|
}
|
|
172
|
-
async render(question, defaultValue, options
|
|
427
|
+
async render(question, defaultValue, options) {
|
|
428
|
+
options = { ...options, value: defaultValue };
|
|
173
429
|
await loadResource(window.T007_INPUT_JS_SRC, "script");
|
|
174
|
-
options.value = defaultValue;
|
|
175
430
|
this.dialog.innerHTML = `
|
|
176
|
-
<form class="t007-input-form" novalidate>
|
|
177
|
-
<div class="t007-dialog-top-section">
|
|
178
|
-
<
|
|
431
|
+
<form class="t007-input-form" ${t007.field ? "novalidate" : ""}>
|
|
432
|
+
<div class="t007-dialog-top-section" tabindex="-1">
|
|
433
|
+
<div class="t007-dialog-question">${question}</div>
|
|
179
434
|
</div>
|
|
180
435
|
<div class="t007-dialog-bottom-section">
|
|
181
|
-
<button class="t007-dialog-confirm-button"
|
|
182
|
-
<button class="t007-dialog-cancel-button"
|
|
436
|
+
<button type="submit" data-arrow-item class="t007-dialog-confirm-button">${options.confirmText || "OK"}</button>
|
|
437
|
+
<button type="button" data-arrow-item class="t007-dialog-cancel-button">${options.cancelText || "Cancel"}</button>
|
|
183
438
|
</div>
|
|
184
439
|
</form>
|
|
185
440
|
`;
|
|
186
|
-
(this.form = this.dialog.querySelector("form")).lastElementChild.insertAdjacentElement("beforebegin", t007.
|
|
187
|
-
this.form.onSubmit = this.confirm;
|
|
188
|
-
this.
|
|
189
|
-
|
|
190
|
-
this.cancelBtn.addEventListener("click", this.cancel);
|
|
191
|
-
t007.FM?.handleFormValidation?.(this.form);
|
|
441
|
+
(this.form = this.dialog.querySelector("form")).lastElementChild.insertAdjacentElement("beforebegin", t007.field?.(options) || createEl("input", options));
|
|
442
|
+
t007.field ? this.form.onSubmit = this.confirm : this.form.addEventListener("submit", (e) => (e.preventDefault(), this.confirm()));
|
|
443
|
+
this.dialog.querySelector(".t007-dialog-cancel-button").addEventListener("click", this.cancel);
|
|
444
|
+
t007.handleFormValidation?.(this.form);
|
|
192
445
|
this.show();
|
|
193
446
|
}
|
|
194
447
|
show() {
|
|
195
|
-
this.
|
|
196
|
-
this.form.elements[0]?.focus();
|
|
197
|
-
this.form.elements[0]?.select?.();
|
|
448
|
+
super.show(), this.form.elements[0]?.select?.();
|
|
198
449
|
}
|
|
199
450
|
confirm() {
|
|
200
|
-
this.
|
|
201
|
-
this.resolve(this.form.elements[0]?.value);
|
|
451
|
+
super.confirm(this.form.elements[0]?.value);
|
|
202
452
|
}
|
|
203
453
|
cancel() {
|
|
204
|
-
|
|
205
|
-
this.resolve(null);
|
|
454
|
+
super.cancel(null);
|
|
206
455
|
}
|
|
207
456
|
};
|
|
457
|
+
function isActive(id) {
|
|
458
|
+
return isDef(id) ? t007.dialogs.has(id) : t007.dialogs.size > 0;
|
|
459
|
+
}
|
|
208
460
|
function alert(message, options) {
|
|
209
|
-
return new Promise((resolve) => new T007_Alert_Dialog({
|
|
461
|
+
return new Promise((resolve) => new T007_Alert_Dialog({ resolve, message, options }));
|
|
210
462
|
}
|
|
211
463
|
function confirm(question, options) {
|
|
212
|
-
return new Promise((resolve) => new T007_Confirm_Dialog({
|
|
464
|
+
return new Promise((resolve) => new T007_Confirm_Dialog({ resolve, question, options }));
|
|
213
465
|
}
|
|
214
466
|
function prompt(question, defaultValue, options) {
|
|
215
|
-
return new Promise((resolve) => new T007_Prompt_Dialog({ question, defaultValue,
|
|
467
|
+
return new Promise((resolve) => new T007_Prompt_Dialog({ resolve, question, defaultValue, options }));
|
|
468
|
+
}
|
|
469
|
+
function dismiss(id, response) {
|
|
470
|
+
if (isDef(id)) return t007.dialogs.get(id)?.cancel(response);
|
|
471
|
+
for (const dialog of t007.dialogs.values()) dialog.cancel(response);
|
|
216
472
|
}
|
|
217
473
|
if ("undefined" !== typeof window) {
|
|
218
|
-
t007.alert = alert;
|
|
219
|
-
t007.
|
|
220
|
-
t007.
|
|
474
|
+
t007.alert = alert, t007.confirm = confirm, t007.prompt = prompt;
|
|
475
|
+
t007.dialog = { isActive, alert, confirm, prompt, dismiss };
|
|
476
|
+
t007.dialogs = /* @__PURE__ */ new Map();
|
|
221
477
|
loadResource(T007_DIALOG_CSS_SRC), loadResource(window.T007_INPUT_JS_SRC, "script");
|
|
222
|
-
window.Alert ??= t007.alert;
|
|
223
|
-
window.Confirm ??= t007.confirm;
|
|
224
|
-
window.Prompt ??= t007.prompt;
|
|
478
|
+
window.Alert ??= t007.alert, window.Confirm ??= t007.confirm, window.Prompt ??= t007.prompt;
|
|
225
479
|
console.log("%cT007 Dialogs attached to window!", "color: darkturquoise");
|
|
226
480
|
}
|
|
227
481
|
})();
|
package/dist/index.js
CHANGED
|
@@ -1,140 +1,131 @@
|
|
|
1
|
-
// src/index.js
|
|
2
|
-
import {
|
|
1
|
+
// src/js/index.js
|
|
2
|
+
import { initArrowNavigation, initFocusTrap, initOutsideClick, removeArrowNavigation, removeFocusTrap, removeOutsideClick } from "@t007/utils/hooks/vanilla";
|
|
3
|
+
import { bindAllMethods, createEl, isDef, loadResource, uid, parseCSSTime } from "@t007/utils";
|
|
3
4
|
var T007_Dialog = class {
|
|
4
|
-
|
|
5
|
-
confirmBtn;
|
|
6
|
-
cancelBtn;
|
|
7
|
-
constructor(resolve) {
|
|
5
|
+
constructor(resolve, { id, closedBy: by, rootElement: root = document.body, scoped = true }) {
|
|
8
6
|
bindAllMethods(this);
|
|
9
7
|
this.resolve = resolve;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
closedBy: "any"
|
|
14
|
-
})
|
|
15
|
-
);
|
|
8
|
+
t007.dialogs.set(this.id = id ?? uid("t007_dialog_"), this);
|
|
9
|
+
this.rootElement = root?.isConnected ? root : document.body, this.rootScoped = this.rootElement !== document.body, this.scoped = this.rootScoped && scoped;
|
|
10
|
+
this.rootElement.append(this.dialog = createEl("dialog", { closedBy: by || (this.rootScoped ? "none" : "any"), className: `t007-dialog${this.rootScoped ? " t007-dialog-scoped" : ""}`, id: this.id }), this.rootScoped ? this.backdrop = createEl("div", { className: "t007-dialog-backdrop", id: `${this.id}_backdrop` }) : null);
|
|
16
11
|
this.dialog.addEventListener("cancel", this.cancel);
|
|
12
|
+
initArrowNavigation(this.dialog, { enabled: true, rovingTab: false });
|
|
13
|
+
this.rootScoped && !by && initOutsideClick(this.dialog, { enabled: true, onOutsideClick: this.cancel, root: this.rootElement, scoped: this.scoped });
|
|
17
14
|
}
|
|
18
15
|
show() {
|
|
19
|
-
this.dialog.showModal();
|
|
20
|
-
this.
|
|
16
|
+
this.dialog[this.rootScoped ? "show" : "showModal"]();
|
|
17
|
+
initFocusTrap(this.dialog, { enabled: true, root: this.rootElement, scoped: this.scoped });
|
|
21
18
|
}
|
|
22
19
|
remove() {
|
|
23
|
-
this.dialog.
|
|
24
|
-
this.dialog.
|
|
20
|
+
removeArrowNavigation(this.dialog), removeFocusTrap(this.dialog), removeOutsideClick(this.dialog);
|
|
21
|
+
this.dialog.open && this.dialog.close(), t007.dialogs.delete(this.id);
|
|
22
|
+
setTimeout(() => (this.dialog.remove(), this.backdrop?.remove()), parseCSSTime(getComputedStyle(this.dialog).transitionDuration || "150ms"));
|
|
25
23
|
}
|
|
26
|
-
confirm() {
|
|
27
|
-
this.remove();
|
|
28
|
-
this.resolve(true);
|
|
24
|
+
confirm(res = true) {
|
|
25
|
+
this.remove(), this.resolve(res instanceof Event ? true : res);
|
|
29
26
|
}
|
|
30
|
-
cancel() {
|
|
31
|
-
this.remove();
|
|
32
|
-
this.resolve(false);
|
|
27
|
+
cancel(res = false) {
|
|
28
|
+
this.remove(), this.resolve(res instanceof Event ? false : res);
|
|
33
29
|
}
|
|
34
30
|
};
|
|
35
31
|
var T007_Alert_Dialog = class extends T007_Dialog {
|
|
36
|
-
constructor({ message, resolve, options }) {
|
|
37
|
-
super(resolve);
|
|
38
|
-
this.render(message, options);
|
|
32
|
+
constructor({ message, resolve, options = {} }) {
|
|
33
|
+
super(resolve, options), this.render(message, options);
|
|
39
34
|
}
|
|
40
|
-
render(message, options
|
|
35
|
+
render(message, options) {
|
|
41
36
|
this.dialog.innerHTML = `
|
|
42
|
-
<div class="t007-dialog-top-section">
|
|
43
|
-
<
|
|
37
|
+
<div class="t007-dialog-top-section" tabindex="-1">
|
|
38
|
+
<div class="t007-dialog-question">${message}</div>
|
|
44
39
|
</div>
|
|
45
40
|
<div class="t007-dialog-bottom-section">
|
|
46
|
-
<button class="t007-dialog-confirm-button"
|
|
41
|
+
<button type="button" data-autofocus data-arrow-item class="t007-dialog-confirm-button">${options.confirmText || "OK"}</button>
|
|
47
42
|
</div>
|
|
48
43
|
`;
|
|
49
|
-
this.
|
|
50
|
-
this.confirmBtn.addEventListener("click", this.confirm);
|
|
44
|
+
this.dialog.querySelector(".t007-dialog-confirm-button").addEventListener("click", this.confirm);
|
|
51
45
|
this.show();
|
|
52
46
|
}
|
|
53
47
|
};
|
|
54
48
|
var T007_Confirm_Dialog = class extends T007_Dialog {
|
|
55
|
-
constructor({ question, resolve, options }) {
|
|
56
|
-
super(resolve);
|
|
57
|
-
this.render(question, options);
|
|
49
|
+
constructor({ question, resolve, options = {} }) {
|
|
50
|
+
super(resolve, options), this.render(question, options);
|
|
58
51
|
}
|
|
59
|
-
render(question, options
|
|
52
|
+
render(question, options) {
|
|
60
53
|
this.dialog.innerHTML = `
|
|
61
|
-
<div class="t007-dialog-top-section">
|
|
62
|
-
<
|
|
54
|
+
<div class="t007-dialog-top-section" tabindex="-1">
|
|
55
|
+
<div class="t007-dialog-question">${question}</div>
|
|
63
56
|
</div>
|
|
64
57
|
<div class="t007-dialog-bottom-section">
|
|
65
|
-
<button class="t007-dialog-confirm-button"
|
|
66
|
-
<button class="t007-dialog-cancel-button"
|
|
58
|
+
<button type="button" data-autofocus data-arrow-item class="t007-dialog-confirm-button">${options.confirmText || "OK"}</button>
|
|
59
|
+
<button type="button" data-arrow-item class="t007-dialog-cancel-button">${options.cancelText || "Cancel"}</button>
|
|
67
60
|
</div>
|
|
68
61
|
`;
|
|
69
|
-
this.
|
|
70
|
-
this.
|
|
71
|
-
this.confirmBtn.addEventListener("click", this.confirm);
|
|
72
|
-
this.cancelBtn.addEventListener("click", this.cancel);
|
|
62
|
+
this.dialog.querySelector(".t007-dialog-confirm-button").addEventListener("click", this.confirm);
|
|
63
|
+
this.dialog.querySelector(".t007-dialog-cancel-button").addEventListener("click", this.cancel);
|
|
73
64
|
this.show();
|
|
74
65
|
}
|
|
75
66
|
};
|
|
76
67
|
var T007_Prompt_Dialog = class extends T007_Dialog {
|
|
77
|
-
constructor({ question, defaultValue, resolve, options }) {
|
|
78
|
-
super(resolve);
|
|
79
|
-
this.render(question, defaultValue, options);
|
|
68
|
+
constructor({ question, defaultValue, resolve, options = {} }) {
|
|
69
|
+
super(resolve, options), this.render(question, defaultValue, options);
|
|
80
70
|
}
|
|
81
|
-
async render(question, defaultValue, options
|
|
71
|
+
async render(question, defaultValue, options) {
|
|
72
|
+
options = { ...options, value: defaultValue };
|
|
82
73
|
await loadResource(window.T007_INPUT_JS_SRC, "script");
|
|
83
|
-
options.value = defaultValue;
|
|
84
74
|
this.dialog.innerHTML = `
|
|
85
|
-
<form class="t007-input-form" novalidate>
|
|
86
|
-
<div class="t007-dialog-top-section">
|
|
87
|
-
<
|
|
75
|
+
<form class="t007-input-form" ${t007.field ? "novalidate" : ""}>
|
|
76
|
+
<div class="t007-dialog-top-section" tabindex="-1">
|
|
77
|
+
<div class="t007-dialog-question">${question}</div>
|
|
88
78
|
</div>
|
|
89
79
|
<div class="t007-dialog-bottom-section">
|
|
90
|
-
<button class="t007-dialog-confirm-button"
|
|
91
|
-
<button class="t007-dialog-cancel-button"
|
|
80
|
+
<button type="submit" data-arrow-item class="t007-dialog-confirm-button">${options.confirmText || "OK"}</button>
|
|
81
|
+
<button type="button" data-arrow-item class="t007-dialog-cancel-button">${options.cancelText || "Cancel"}</button>
|
|
92
82
|
</div>
|
|
93
83
|
</form>
|
|
94
84
|
`;
|
|
95
|
-
(this.form = this.dialog.querySelector("form")).lastElementChild.insertAdjacentElement("beforebegin", t007.
|
|
96
|
-
this.form.onSubmit = this.confirm;
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
this.cancelBtn.addEventListener("click", this.cancel);
|
|
100
|
-
t007.FM?.handleFormValidation?.(this.form);
|
|
85
|
+
(this.form = this.dialog.querySelector("form")).lastElementChild.insertAdjacentElement("beforebegin", t007.field?.(options) || createEl("input", options));
|
|
86
|
+
t007.field ? this.form.onSubmit = this.confirm : this.form.addEventListener("submit", (e) => (e.preventDefault(), this.confirm()));
|
|
87
|
+
this.dialog.querySelector(".t007-dialog-cancel-button").addEventListener("click", this.cancel);
|
|
88
|
+
t007.handleFormValidation?.(this.form);
|
|
101
89
|
this.show();
|
|
102
90
|
}
|
|
103
91
|
show() {
|
|
104
|
-
this.
|
|
105
|
-
this.form.elements[0]?.focus();
|
|
106
|
-
this.form.elements[0]?.select?.();
|
|
92
|
+
super.show(), this.form.elements[0]?.select?.();
|
|
107
93
|
}
|
|
108
94
|
confirm() {
|
|
109
|
-
this.
|
|
110
|
-
this.resolve(this.form.elements[0]?.value);
|
|
95
|
+
super.confirm(this.form.elements[0]?.value);
|
|
111
96
|
}
|
|
112
97
|
cancel() {
|
|
113
|
-
|
|
114
|
-
this.resolve(null);
|
|
98
|
+
super.cancel(null);
|
|
115
99
|
}
|
|
116
100
|
};
|
|
101
|
+
function isActive(id) {
|
|
102
|
+
return isDef(id) ? t007.dialogs.has(id) : t007.dialogs.size > 0;
|
|
103
|
+
}
|
|
117
104
|
function alert(message, options) {
|
|
118
|
-
return new Promise((resolve) => new T007_Alert_Dialog({
|
|
105
|
+
return new Promise((resolve) => new T007_Alert_Dialog({ resolve, message, options }));
|
|
119
106
|
}
|
|
120
107
|
function confirm(question, options) {
|
|
121
|
-
return new Promise((resolve) => new T007_Confirm_Dialog({
|
|
108
|
+
return new Promise((resolve) => new T007_Confirm_Dialog({ resolve, question, options }));
|
|
122
109
|
}
|
|
123
110
|
function prompt(question, defaultValue, options) {
|
|
124
|
-
return new Promise((resolve) => new T007_Prompt_Dialog({ question, defaultValue,
|
|
111
|
+
return new Promise((resolve) => new T007_Prompt_Dialog({ resolve, question, defaultValue, options }));
|
|
112
|
+
}
|
|
113
|
+
function dismiss(id, response) {
|
|
114
|
+
if (isDef(id)) return t007.dialogs.get(id)?.cancel(response);
|
|
115
|
+
for (const dialog of t007.dialogs.values()) dialog.cancel(response);
|
|
125
116
|
}
|
|
126
117
|
if ("undefined" !== typeof window) {
|
|
127
|
-
t007.alert = alert;
|
|
128
|
-
t007.
|
|
129
|
-
t007.
|
|
118
|
+
t007.alert = alert, t007.confirm = confirm, t007.prompt = prompt;
|
|
119
|
+
t007.dialog = { isActive, alert, confirm, prompt, dismiss };
|
|
120
|
+
t007.dialogs = /* @__PURE__ */ new Map();
|
|
130
121
|
loadResource(T007_DIALOG_CSS_SRC), loadResource(window.T007_INPUT_JS_SRC, "script");
|
|
131
|
-
window.Alert ??= t007.alert;
|
|
132
|
-
window.Confirm ??= t007.confirm;
|
|
133
|
-
window.Prompt ??= t007.prompt;
|
|
122
|
+
window.Alert ??= t007.alert, window.Confirm ??= t007.confirm, window.Prompt ??= t007.prompt;
|
|
134
123
|
console.log("%cT007 Dialogs attached to window!", "color: darkturquoise");
|
|
135
124
|
}
|
|
136
125
|
export {
|
|
137
126
|
alert,
|
|
138
127
|
confirm,
|
|
128
|
+
dismiss,
|
|
129
|
+
isActive,
|
|
139
130
|
prompt
|
|
140
131
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t007/dialog",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"description": "A lightweight, pure JS dialog system.",
|
|
5
5
|
"author": "Oketade Oluwatobiloba <tobioketade007@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"ecosystem",
|
|
47
47
|
"ui",
|
|
48
48
|
"vanilla-js",
|
|
49
|
+
"react",
|
|
49
50
|
"dialog",
|
|
50
51
|
"modal",
|
|
51
52
|
"popup",
|
|
@@ -55,6 +56,6 @@
|
|
|
55
56
|
"accessible"
|
|
56
57
|
],
|
|
57
58
|
"dependencies": {
|
|
58
|
-
"@t007/utils": "^0.0.
|
|
59
|
+
"@t007/utils": "^0.0.27"
|
|
59
60
|
}
|
|
60
61
|
}
|