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