@webqit/webflo 0.20.28 → 0.20.30

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.
@@ -0,0 +1,1500 @@
1
+ // ---------------- ToastElement
2
+
3
+ export class ToastElement extends HTMLElement {
4
+
5
+ #childToast = null;
6
+
7
+ #addNested() {
8
+ if (this.#childToast) {
9
+ this.shadowRoot.appendChild(this.#childToast);
10
+ if (this.matches(':popover-open')) {
11
+ this.#childToast.showPopover();
12
+ }
13
+ }
14
+ }
15
+
16
+ _processVisibility(slot) {
17
+ if (this.hasAttribute('oncontent')) {
18
+ if (slot.assignedNodes().find((n) => n.nodeName !== '#text' || n.textContent.trim())) {
19
+ this.showPopover();
20
+ } else this.hidePopover();
21
+ }
22
+ }
23
+
24
+ connectedCallback() {
25
+ if (!this.popover) {
26
+ this.popover = 'auto';
27
+ }
28
+ }
29
+
30
+ render({ content, context }, childToast = null, recursion = 1) {
31
+ if (context && recursion > 0) {
32
+ const directChildToast = document.createElement(this.tagName);
33
+
34
+ directChildToast.setAttribute('popover', this.getAttribute('popover') || 'auto');
35
+ if (this.classList.contains('_top')) {
36
+ directChildToast.classList.add('_top');
37
+ }
38
+ directChildToast.render({ content }, childToast, 0);
39
+
40
+ this.render(context, directChildToast, recursion + 1);
41
+ return;
42
+ }
43
+
44
+ this.#childToast?.remove();
45
+ this.#childToast = childToast;
46
+ // In case "this" is already connected
47
+ this.#addNested();
48
+
49
+ const childStartDelay = parseFloat(childToast?.style.getPropertyValue('--start-delay') || '0');
50
+ this.style.setProperty('--start-delay', (childStartDelay + 0.1) + 's');
51
+
52
+ // Render now
53
+ this.type = content.type;
54
+ this.innerHTML = content.message;
55
+ }
56
+
57
+ set type(value) {
58
+ if ([undefined, null].includes(value)) {
59
+ this.removeAttribute('type');
60
+ } else this.setAttribute('type', value);
61
+ }
62
+
63
+ get type() { return this.getAttribute('type'); }
64
+
65
+ get contentHTML() { return ''; }
66
+
67
+ get css() { return ''; }
68
+
69
+ constructor() {
70
+ super();
71
+ this.attachShadow({ mode: 'open' });
72
+
73
+ this.addEventListener('toggle', (e) => {
74
+ if (e.newState === 'open') {
75
+ this.#childToast?.showPopover();
76
+ } else if (e.newState === 'closed') {
77
+ this.#childToast?.hidePopover();
78
+ if (this.getAttribute('oncontent') === 'always') {
79
+ this.innerHTML = '';
80
+ }
81
+ }
82
+ });
83
+
84
+ this.shadowRoot.innerHTML = `
85
+ <div class="container">
86
+
87
+ <svg class="icon _info" xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M444-296h72v-228h-72v228Zm36-312q20 0 33-13t13-33q0-20-13-33t-33-13q-20 0-33 13t-13 33q0 20 13 33t33 13Zm0 528q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>
88
+ <svg class="icon _success" xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M390-298 246-442l72-72 72 72 252-252 72 72-324 324Zm90 218q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>
89
+ <svg class="icon _error" xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M480-292q20 0 33-13t13-33q0-20-13-33t-33-13q-20 0-33 13t-13 33q0 20 13 33t33 13Zm-36-156h72v-240h-72v240Zm36 368q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>
90
+ <svg class="icon _warning" xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M480-292q20 0 33-13t13-33q0-20-13-33t-33-13q-20 0-33 13t-13 33q0 20 13 33t33 13Zm-36-156h72v-240h-72v240Zm36 368q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>
91
+ <div class="_content" part="content">
92
+ <slot
93
+ onslotchange="this.getRootNode().host?._processVisibility(this);"
94
+ >${this.contentHTML}</slot>
95
+ </div>
96
+ <button class="close-button" part="close-button" onclick="this.getRootNode().host.hidePopover();">
97
+ <svg xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M256-176 176-256l224-224-224-224 80-80 224 224 224-224 80 80-224 224 224 224-80 80-224-224-224 224Z"/></svg>
98
+ </button>
99
+
100
+ </div>
101
+ <style>
102
+ * {
103
+ box-sizing: border-box;
104
+ }
105
+
106
+ @keyframes flash {
107
+ from {
108
+ background-color: transparent;
109
+ }
110
+
111
+ 50% {
112
+ background-color: rgba(125, 125, 125, 0.2);
113
+ }
114
+
115
+ to {
116
+ background-color: transparent;
117
+ }
118
+ }
119
+
120
+ :host {
121
+ --color-default: var(--toast-color-default, whitesmoke);
122
+ --color-info: var(--toast-color-info, skyblue);
123
+ --color-success: var(--toast-color-success, lightgreen);
124
+ --color-error: var(--toast-color-error, coral);
125
+ --color-warning: var(--toast-color-warning, coral);
126
+
127
+ --wq-radius: var(--toast-radius, 1rem);
128
+ --background: var(--toast-background, rgb(30, 30, 30));
129
+ --shadow: var(--toast-shadow, rgb(30, 30, 30));
130
+
131
+ --dir: 1;
132
+ --translation: calc(var(--toast-translation, 50px) * var(--dir));
133
+ --exit-factor: var(--toast-exit-factor, -1);
134
+
135
+ --entry-transform: translateY(var(--translation));
136
+ --exit-transform: translateY(calc(var(--translation) * var(--exit-factor)));
137
+ }
138
+
139
+ :host {
140
+ border: none;
141
+ background: none;
142
+
143
+ margin-bottom: 0;
144
+ padding: 1rem;
145
+
146
+ /* Transition */
147
+ transition:
148
+ opacity 0.2s,
149
+ transform 0.2s,
150
+ bottom 0.1s,
151
+ top 0.1s,
152
+ overlay 0.2s allow-discrete,
153
+ display 0.2s allow-discrete;
154
+
155
+ /* Exit state */
156
+ transform: var(--exit-transform);
157
+ transition-delay: var(--start-delay, 0s);
158
+ opacity: 0;
159
+ }
160
+
161
+ :host(._top) {
162
+ margin-bottom: auto;
163
+ margin-top: 0;
164
+ --dir: -1;
165
+ }
166
+
167
+ /* ----------- */
168
+
169
+ .container {
170
+ position: relative;
171
+
172
+ display: flex;
173
+ align-items: start;
174
+ gap: 0.6rem;
175
+
176
+ padding-block: 0.8rem;
177
+ padding-inline: 1.2rem;
178
+ border-radius: var(--wq-radius);
179
+
180
+ color: var(--color-default);
181
+ background-color: var(--background);
182
+ box-shadow: var(--shadow);
183
+
184
+ anchor-name: --container;
185
+ }
186
+
187
+ /* ----------- */
188
+
189
+ :host(:popover-open) {
190
+ display: block;
191
+ opacity: 1;
192
+ transform: none;
193
+ }
194
+
195
+ @starting-style {
196
+ :host(:popover-open) {
197
+ opacity: 0;
198
+ transform: var(--entry-transform);
199
+ }
200
+ }
201
+
202
+ /* ----------- */
203
+
204
+ :host(:not([popover="manual"]):popover-open)::backdrop {
205
+ animation: flash 0.3s ease-in;
206
+ animation-iteration-count: 3;
207
+ }
208
+
209
+ :host([popover="manual"])::backdrop {
210
+ /* Transition */
211
+ transition:
212
+ display 0.2s allow-discrete,
213
+ overlay 0.2s allow-discrete,
214
+ backdrop-filter 0.2s;
215
+ }
216
+
217
+ :host([popover="manual"]:popover-open)::backdrop {
218
+ backdrop-filter: blur(3px);
219
+ }
220
+
221
+ @starting-style {
222
+ :host([popover="manual"]:popover-open)::backdrop {
223
+ backdrop-filter: none;
224
+ }
225
+ }
226
+
227
+ :host([popover="manual"]:popover-open)::before {
228
+ position: fixed;
229
+ inset: 0;
230
+ display: block;
231
+ content: "";
232
+ z-index: -1;
233
+ }
234
+
235
+ .icon {
236
+ display: none;
237
+ opacity: 0.6;
238
+ }
239
+
240
+ :host([type="info"]) .icon._info,
241
+ :host([type="success"]) .icon._success,
242
+ :host([type="error"]) .icon._error,
243
+ :host([type="warning"]) .icon._warning {
244
+ display: block;
245
+ }
246
+
247
+ :host([type="info"]) .container {
248
+ color: var(--color-info);
249
+ }
250
+
251
+ :host([type="success"]) .container {
252
+ color: var(--color-success);
253
+ }
254
+
255
+ :host([type="error"]) .container {
256
+ color: var(--color-error);
257
+ }
258
+
259
+ :host([type="warning"]) .container {
260
+ color: var(--color-warning);
261
+ }
262
+
263
+ .close-button {
264
+ padding-inline: 0;
265
+ display: flex;
266
+ align-items: center;
267
+ justify-content: center;
268
+ appearance: none;
269
+ color: gray;
270
+ cursor: pointer;
271
+ border: none;
272
+ background: none;
273
+ transform: translateX(0.1rem);
274
+ }
275
+
276
+ :host(:not([popover="manual"])) .close-button {
277
+ display: none;
278
+ }
279
+
280
+ .close-button:hover {
281
+ opacity: 0.8;
282
+ }
283
+
284
+ /* ----------- */
285
+
286
+ :host(:not(._top)) wq-toast {
287
+ position-anchor: --container;
288
+ bottom: calc(anchor(bottom) - 0.5rem);
289
+ }
290
+
291
+ :host(:not(._top)) wq-toast:hover,
292
+ :host(:not(._top)) .container:hover ~ wq-toast {
293
+ bottom: calc(anchor(top) - 0.75rem);
294
+ transition-delay: 0;
295
+ }
296
+
297
+ :host(._top) wq-toast {
298
+ position-anchor: --container;
299
+ top: calc(anchor(top) - 0.5rem);
300
+ }
301
+
302
+ :host(._top) wq-toast:hover,
303
+ :host(._top) .container:hover ~ wq-toast {
304
+ top: calc(anchor(bottom) - 0.75rem);
305
+ transition-delay: 0;
306
+ }
307
+
308
+ ${this.css}
309
+ </style>`;
310
+
311
+ this.#addNested();
312
+ }
313
+ }
314
+
315
+ // ---------------- ModalElement
316
+
317
+ export class ModalMinmaxEvent extends Event {
318
+
319
+ #ratio;
320
+ get ratio() { return this.#ratio; }
321
+
322
+ constructor(ratio) {
323
+ super('minmax');
324
+ this.#ratio = ratio;
325
+ }
326
+ }
327
+
328
+ export class ModalElement extends HTMLElement {
329
+
330
+ updateScrollViewDimensions() {
331
+ const viewElement = this.shadowRoot.querySelector('.view');
332
+ const beaderBoxElement = this.shadowRoot.querySelector('.header-box');
333
+ const beaderBarElement = this.shadowRoot.querySelector('.header-bar');
334
+ const footerBarElement = this.shadowRoot.querySelector('.footer-bar');
335
+ requestAnimationFrame(() => {
336
+ viewElement.style.setProperty('--header-box-height', beaderBoxElement.offsetHeight + 'px');
337
+ viewElement.style.setProperty('--header-bar-height', beaderBarElement.offsetHeight + 'px');
338
+ viewElement.style.setProperty('--footer-bar-height', footerBarElement.offsetHeight + 'px');
339
+ if (this.classList.contains('_container')) return;
340
+ viewElement.style.setProperty('--view-width', viewElement.clientWidth/* instead of offsetHeight; safari reasons */ + 'px');
341
+ viewElement.style.setProperty('--view-height', viewElement.clientHeight/* instead of offsetHeight; safari reasons */ + 'px');
342
+ });
343
+ }
344
+
345
+ connectedCallback() {
346
+ if (!this.popover) {
347
+ this.popover = 'manual';
348
+ }
349
+ this.bindMinmaxWorker();
350
+
351
+ if (this.hasAttribute('open')) {
352
+ this.showPopover();
353
+ }
354
+
355
+ if (this.matches(':popover-open')) {
356
+ this.updateScrollViewDimensions();
357
+ }
358
+ }
359
+
360
+ disconnectedCallback() {
361
+ this.#unbindMinmaxWorker?.();
362
+ this.#unbindMinmaxWorker = null;
363
+ }
364
+
365
+ #unbindMinmaxWorker = null;
366
+
367
+ bindMinmaxWorker() {
368
+ const swipeDismiss = this.classList.contains('_swipe-dismiss');
369
+ const minmaxEvents = this.classList.contains('_minmax');
370
+
371
+ if (!swipeDismiss && !minmaxEvents) return;
372
+
373
+ const viewElement = this.shadowRoot.querySelector('.view');
374
+ const sentinelElement = this.shadowRoot.querySelector('.sentinel');
375
+ const spacingElement = viewElement.querySelector('.spacing');
376
+
377
+ const options = {
378
+ root: viewElement,
379
+ threshold: [0, 1]
380
+ };
381
+
382
+ const observer = new IntersectionObserver((entries) => {
383
+ for (const entry of entries) {
384
+ // Minmax events
385
+ if (entry.target === spacingElement) {
386
+ const event = new ModalMinmaxEvent(1 - entry.intersectionRatio);
387
+ this.dispatchEvent(event);
388
+
389
+ let onminmax;
390
+ if (onminmax = this.getAttribute('onminmax')?.trim()) {
391
+ Function('event', onminmax).call(this, event);
392
+ }
393
+ }
394
+
395
+ // For auto-closing
396
+ if (entry.target === sentinelElement && entry.isIntersecting) {
397
+ this.hidePopover();
398
+ setTimeout(() => spacingElement.scrollIntoView(), 300);
399
+ }
400
+ }
401
+ }, options);
402
+
403
+ if (minmaxEvents) observer.observe(spacingElement);
404
+ if (swipeDismiss) observer.observe(sentinelElement);
405
+ this.#unbindMinmaxWorker = () => observer.disconnect();
406
+ }
407
+
408
+ #onminmaxHandler = null;
409
+
410
+ set onminmax(handler) {
411
+ if (this.#onminmaxHandler) {
412
+ this.removeEventListener('onminmax', this.#onminmaxHandler);
413
+ }
414
+ if (typeof handler === 'function') {
415
+ this.addEventListener('minmax', this.#onminmaxHandler);
416
+ } else if (handler !== null && handler !== undefined) {
417
+ throw new Error('onminmax must be null or a function');
418
+ }
419
+ this.#onminmaxHandler = handler;
420
+ }
421
+
422
+ get onminmax() { return this.#onminmaxHandler; }
423
+
424
+ set type(value) {
425
+ if ([undefined, null].includes(value)) {
426
+ this.removeAttribute('type');
427
+ } else this.setAttribute('type', value);
428
+ }
429
+
430
+ get type() { return this.getAttribute('type'); }
431
+
432
+ get headerBoxHTML() { return ''; }
433
+
434
+ get headerHTML() { return ''; }
435
+
436
+ get mainHTML() { return ''; }
437
+
438
+ get contentHTML() { return ''; }
439
+
440
+ get footerHTML() { return ''; }
441
+
442
+ get css() { return ''; }
443
+
444
+ constructor() {
445
+ super();
446
+ this.attachShadow({ mode: 'open' });
447
+
448
+ this.addEventListener('toggle', (e) => {
449
+ if (e.newState !== 'open') return;
450
+ this.updateScrollViewDimensions();
451
+ });
452
+
453
+ window.addEventListener('resize', () => {
454
+ this.updateScrollViewDimensions();
455
+ });
456
+
457
+ this.shadowRoot.innerHTML = `
458
+ <div class="spacing"></div>
459
+ <div class="view" part="view">
460
+
461
+ <div class="sentinel"></div>
462
+ <div class="spacing"></div>
463
+
464
+ <div class="container" part="container">
465
+ <header part="header">
466
+ <div class="header-box" part="header-box">
467
+ <slot
468
+ name="header-box"
469
+ onslotchange="this.classList.toggle('has-slotted', !!this.assignedElements().length); this.closest('.view').style.setProperty('--header-box-height', this.closest('.header-box').offsetHeight + 'px');"
470
+ >${this.headerBoxHTML}</slot>
471
+ </div>
472
+
473
+ <div class="header-bar" part="header-bar">
474
+ <div class="header-left">
475
+ <svg class="icon _info" xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M444-296h72v-228h-72v228Zm36-312q20 0 33-13t13-33q0-20-13-33t-33-13q-20 0-33 13t-13 33q0 20 13 33t33 13Zm0 528q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>
476
+ <svg class="icon _success" xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M390-298 246-442l72-72 72 72 252-252 72 72-324 324Zm90 218q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>
477
+ <svg class="icon _error" xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M480-292q20 0 33-13t13-33q0-20-13-33t-33-13q-20 0-33 13t-13 33q0 20 13 33t33 13Zm-36-156h72v-240h-72v240Zm36 368q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>
478
+ <svg class="icon _warning" xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M480-292q20 0 33-13t13-33q0-20-13-33t-33-13q-20 0-33 13t-13 33q0 20 13 33t33 13Zm-36-156h72v-240h-72v240Zm36 368q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Z"/></svg>
479
+ <div class="_content" style="flex-grow: 1">
480
+ <slot
481
+ name="header"
482
+ onslotchange="this.closest('.view').style.setProperty('--header-bar-height', this.closest('.header-bar').offsetHeight + 'px');"
483
+ >${this.headerHTML}</slot>
484
+ </div>
485
+ </div>
486
+ <button class="close-button" part="close-button" onclick="this.getRootNode().host.hidePopover();">
487
+ <svg xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M256-176 176-256l224-224-224-224 80-80 224 224 224-224 80 80-224 224 224 224-80 80-224-224-224 224Z"/></svg>
488
+ </button>
489
+ </div>
490
+
491
+ <div class="scrollport-anchor">
492
+ <div class="scrollport">
493
+ <div class="scrollbar-track">
494
+ <div class="scrollbar-thumb"></div>
495
+ </div>
496
+ </div>
497
+ </div>
498
+ </header>
499
+
500
+ ${this.mainHTML || `<div class="main" part="main">${this.contentHTML || `<slot></slot>`
501
+ }</div>`}
502
+
503
+ <footer part="footer">
504
+ <div class="scrollport-anchor">
505
+ <div class="scrollport">
506
+ <div class="scrollbar-track">
507
+ <div class="scrollbar-thumb"></div>
508
+ </div>
509
+ </div>
510
+ </div>
511
+
512
+ <div class="footer-bar" part="footer-bar">
513
+ <slot
514
+ name="footer"
515
+ onslotchange="this.classList.toggle('has-slotted', !!this.assignedElements().length); this.closest('.view').style.setProperty('--footer-bar-height', this.closest('.footer-bar').offsetHeight + 'px');"
516
+ >${this.footerHTML}</slot>
517
+ </div>
518
+ </footer>
519
+
520
+ </div>
521
+ </div>
522
+ <span class="spacing-b"></span>
523
+
524
+ <style>
525
+ * {
526
+ box-sizing: border-box;
527
+ }
528
+
529
+ @keyframes untransform {
530
+ to { transform: none; }
531
+ }
532
+
533
+ @keyframes transform-n {
534
+ to { transform: var(--transform); }
535
+ }
536
+
537
+ @keyframes appear {
538
+ from { opacity: 0; }
539
+ to { opacity: 1; }
540
+ }
541
+
542
+ @keyframes disappear {
543
+ from { opacity: 1; }
544
+ to { opacity: 0; }
545
+ }
546
+
547
+ @keyframes header-chrome {
548
+ from { background: var(--header-open-background); }
549
+ to { background: var(--header-background); }
550
+ }
551
+
552
+ @keyframes move-scrollbar-thumb {
553
+ from { transform: var(--scrollbar-thumb-start); }
554
+ to { transform: var(--scrollbar-thumb-length); }
555
+ }
556
+
557
+ @keyframes radius0 {
558
+ to { --wq-radius: 0; }
559
+ }
560
+
561
+ :host {
562
+ --wq-radius: var(--modal-radius, 1rem);
563
+ --aero-blur: var(--modal-aero-blur, 10px);
564
+ --background: var(--modal-background, rgba(80, 80, 80, 1));
565
+
566
+ --color-default: var(--modal-color-default, whitesmoke);
567
+ --color-info: var(--modal-color-info, whitesmoke);
568
+ --color-success: var(--modal-color-success, whitesmoke);
569
+ --color-error: var(--modal-color-error, whitesmoke);
570
+ --color-warning: var(--modal-color-warning, whitesmoke);
571
+
572
+ --header-color-default: var(--modal-header-color-default, whitesmoke);
573
+ --header-color-info: var(--modal-header-color-info, skyblue);
574
+ --header-color-success: var(--modal-header-color-success, lightgreen);
575
+ --header-color-error: var(--modal-header-color-error, coral);
576
+ --header-background: var(--modal-header-background, var(--background));
577
+ --header-open-background: var(--modal-header-open-background, var(--header-background));
578
+
579
+ --footer-color-default: var(--modal-footer-color-default, whitesmoke);
580
+ --footer-color-info: var(--modal-footer-color-info, skyblue);
581
+ --footer-color-success: var(--modal-footer-color-success, lightgreen);
582
+ --footer-color-error: var(--modal-footer-color-error, coral);
583
+ --footer-background: var(--modal-footer-background, var(--background));
584
+
585
+ --expanse-length: var(--modal-expanse-length, 0px);
586
+ --minmax-length: var(--modal-minmax-length, 0px);
587
+ --swipe-dismiss-length: var(--modal-swipe-dismiss-length, 0px);
588
+
589
+ --scrollbar-thumb-color: var(--modal-scrollbar-thumb-color, black);
590
+ --scrollbar-thumb-width: var(--modal-scrollbar-thumb-width, 4px);
591
+ --scrollbar-thumb-height: var(--modal-scrollbar-thumb-height, 30px);
592
+
593
+ --translation: calc(var(--modal-translation, 50px) * var(--dir));
594
+ --exit-factor: var(--modal-exit-factor, -1);
595
+ }
596
+
597
+ /* -------- internal, dynamic props (root) -------- */
598
+
599
+ :host {
600
+ --dir: 1;
601
+ --entry-transform: translateY(var(--translation));
602
+ --exit-transform: translateY(calc(var(--translation) * var(--exit-factor)));
603
+ }
604
+
605
+ /* transform reversal */
606
+
607
+ :host(:is(._top, ._left)) { --dir: -1; }
608
+
609
+ /* horizontal axis */
610
+
611
+ :host(:is(._left, ._right, ._horz)) {
612
+ --entry-transform: translateX(var(--translation));
613
+ --exit-transform: translateX(calc(var(--translation) * var(--exit-factor)));
614
+ }
615
+
616
+ :host(._edge-tight) { --exit-factor: var(--modal-exit-factor, 1); }
617
+
618
+ /* -------- internal, dynamic props (view) -------- */
619
+
620
+ .view {
621
+ --header-box-height: 0px;
622
+ --header-bar-height: 1.6rem;
623
+ --footer-bar-height: 0px;
624
+
625
+ --view-inner-height: calc(var(--view-height) - var(--header-bar-height) - var(--footer-bar-height));
626
+ --total-minmax-length: calc(var(--minmax-length) + var(--swipe-dismiss-length));
627
+
628
+ --y-scroll-effect-exclude: var(--total-minmax-length);
629
+ --scrollbar-appear-range: calc(var(--total-minmax-length) - 25px) var(--total-minmax-length);
630
+ --scrollbar-progress-range: calc(var(--total-minmax-length) + var(--header-box-height)) 100%;
631
+
632
+ --scroll-snap-start: start;
633
+ --scroll-snap-end: end;
634
+
635
+ --radius-top-left: var(--wq-radius);
636
+ --radius-top-right: var(--wq-radius);
637
+ --radius-bottom-left: var(--wq-radius);
638
+ --radius-bottom-right: var(--wq-radius);
639
+ }
640
+
641
+ :host(._container) .view {
642
+ --view-height: calc(100cqh - var(--expanse-length));
643
+ --view-width: 100cqw;
644
+ }
645
+
646
+ /* transform reversal */
647
+
648
+ :host(:is(._top:not(._horz), ._left._horz)) .view {
649
+ --scroll-snap-start: end;
650
+ --scroll-snap-end: start;
651
+
652
+ --y-scroll-effect-exclude: 0px;
653
+ --scrollbar-appear-range: -25px 0;
654
+ }
655
+
656
+ :host(._top:not(._horz)) .view {
657
+ --scrollbar-progress-range: var(--header-box-height) calc(100% - var(--total-minmax-length));
658
+ }
659
+
660
+ :host(._left._horz) .view {
661
+ --scrollbar-progress-range: 0 calc(100% - var(--total-minmax-length));
662
+ }
663
+
664
+ /* curves */
665
+
666
+ :host(._top._edge-tight) .view {
667
+ --radius-top-left: 0px;
668
+ --radius-top-right: 0px;
669
+ }
670
+
671
+ :host(._bottom._edge-tight) .view {
672
+ --radius-bottom-left: 0px;
673
+ --radius-bottom-right: 0px;
674
+ }
675
+
676
+ :host(._left._edge-tight) .view {
677
+ --radius-top-left: 0px;
678
+ --radius-bottom-left: 0px;
679
+ }
680
+
681
+ :host(._right._edge-tight) .view {
682
+ --radius-top-right: 0px;
683
+ --radius-bottom-right: 0px;
684
+ }
685
+
686
+ /* --------- actual styling -------- */
687
+
688
+ :host {
689
+ background: none;
690
+ border: none;
691
+ padding: 0;
692
+
693
+ max-height: 100vh;
694
+ max-width: 100vw;
695
+
696
+ /* Transition */
697
+ transition:
698
+ opacity 0.2s,
699
+ transform 0.2s,
700
+ overlay 0.2s allow-discrete,
701
+ display 0.2s allow-discrete;
702
+ transition-timing-function: ease-out;
703
+
704
+ /* Exit state */
705
+ transform: var(--exit-transform);
706
+ opacity: 0;
707
+ }
708
+
709
+ :host(:not(._horz, ._left, ._right, ._top, ._bottom)) {
710
+ max-width: 800px;
711
+ }
712
+
713
+ /* edge alignment */
714
+
715
+ :host(._top) { margin-top: 0; }
716
+ :host(._bottom) { margin-bottom: 0; }
717
+ :host(._left) { margin-left: 0; }
718
+ :host(._right) { margin-right: 0; }
719
+
720
+ /* flex orientation */
721
+
722
+ :host(:popover-open),
723
+ .view {
724
+ display: flex;
725
+ flex-direction: column;
726
+ align-items: stretch;
727
+ }
728
+
729
+ :host(._horz),
730
+ :host(._horz) .view {
731
+ flex-direction: row;
732
+ }
733
+
734
+ :host(:is(._top:not(._horz), ._left._horz)) .view,
735
+ :host(:is(._top:not(._horz), ._left._horz)) .view .container {
736
+ order: -1;
737
+ }
738
+
739
+ :host(:is(._top:not(._horz), ._left._horz)) .view .sentinel {
740
+ order: 1000;
741
+ }
742
+
743
+ /* spacing */
744
+
745
+ :host>.spacing,
746
+ .view>.spacing {
747
+ position: relative;
748
+ display: block;
749
+ flex-shrink: 0;
750
+ }
751
+
752
+ :host(:not(._horz))>.spacing { height: var(--expanse-length); }
753
+ :host(:not(._horz, ._top, ._bottom))>:is(.spacing, .spacing-b) {
754
+ height: calc(var(--expanse-length) / 2);
755
+ flex-shrink: 0;
756
+ }
757
+
758
+ :host(._horz)>.spacing { width: var(--expanse-length); }
759
+ :host(._horz:not(._left, ._right))>:is(.spacing, .spacing-b) {
760
+ width: calc(var(--expanse-length) / 2);
761
+ flex-shrink: 0;
762
+ }
763
+
764
+ :host(:not(._horz)) .view>.spacing { height: var(--minmax-length); }
765
+ :host(._horz) .view>.spacing { width: var(--minmax-length); }
766
+
767
+ :host(:not(._top, ._horz)) .view>.spacing { margin-top: var(--swipe-dismiss-length); }
768
+ :host(._top:not(._horz)) .view>.spacing { margin-bottom: var(--swipe-dismiss-length); }
769
+
770
+ :host(._horz:not(._left)) .view>.spacing { margin-left: var(--swipe-dismiss-length); }
771
+ :host(._horz._left) .view>.spacing { margin-right: var(--swipe-dismiss-length); }
772
+
773
+ /* ----------- */
774
+
775
+ .view {
776
+ position: relative;
777
+ flex-grow: 1;
778
+
779
+ pointer-events: none;
780
+
781
+ overflow-y: auto;
782
+ scrollbar-width: none;
783
+
784
+ border-top-left-radius: var(--radius-top-left);
785
+ border-top-right-radius: var(--radius-top-right);
786
+ border-bottom-left-radius: var(--radius-bottom-left);
787
+ border-bottom-right-radius: var(--radius-bottom-right);
788
+
789
+ scroll-timeline-name: --view-scroll;
790
+
791
+ animation-timing-function: linear;
792
+ animation-fill-mode: forwards;
793
+ animation-name: untransform;
794
+ animation-timeline: --view-scroll;
795
+
796
+ animation-range: 0 var(--total-minmax-length);
797
+ }
798
+
799
+ :host(:not(._horz, ._top, ._bottom, ._edge-tight._alt-edge-tight)) .view {
800
+ transform: translateY(calc(var(--total-minmax-length) / -2));
801
+ }
802
+
803
+ :host(._horz:not(._left, ._right, ._edge-tight._alt-edge-tight)) .view {
804
+ transform: translateX(calc(var(--total-minmax-length) / -2));
805
+ }
806
+
807
+ :host(._edge-tight._alt-edge-tight) .view {
808
+ animation-timing-function: linear;
809
+ animation-fill-mode: forwards;
810
+ animation-name: radius0;
811
+ animation-timeline: --view-scroll;
812
+
813
+ animation-range: calc(var(--total-minmax-length) - var(--wq-radius)) var(--total-minmax-length);
814
+ }
815
+
816
+ :host(._horz) .view {
817
+ overflow-y: hidden;
818
+ overflow-x: auto;
819
+
820
+ scroll-timeline-axis: inline;
821
+ }
822
+
823
+ .view::-webkit-scrollbar { display: none; }
824
+
825
+ /* ----------- */
826
+
827
+ .container {
828
+ position: relative;
829
+ flex-grow: 1;
830
+
831
+ min-height: 100%;
832
+ min-width: 100%;
833
+
834
+ pointer-events: auto;
835
+
836
+ display: flex;
837
+ flex-direction: column;
838
+ }
839
+
840
+ :host(._swipe-dismiss) .container {
841
+ animation-timing-function: linear;
842
+ animation-fill-mode: both;
843
+ animation-name: appear;
844
+ animation-timeline: --view-scroll;
845
+ animation-range: 0 var(--swipe-dismiss-length);
846
+ }
847
+
848
+ :host(._swipe-dismiss:is(._top:not(._horz), ._left._horz)) .container {
849
+ animation-name: disappear;
850
+ animation-range: calc(100% - var(--swipe-dismiss-length)) 100%;
851
+ }
852
+
853
+ /* ------------ */
854
+
855
+ header {
856
+ position: sticky;
857
+ top: calc(var(--header-box-height) * -1);
858
+ z-index: 1;
859
+
860
+ display: flex;
861
+ flex-direction: column;
862
+
863
+ color: var(--header-color-default);
864
+ background: var(--header-background);
865
+
866
+ border-top-left-radius: var(--radius-top-left);
867
+ border-top-right-radius: var(--radius-top-right);
868
+ }
869
+
870
+ :host(:not(._horz)) header {
871
+ animation-timing-function: linear;
872
+ animation-fill-mode: both;
873
+ animation-name: header-chrome;
874
+ animation-timeline: --view-scroll;
875
+ animation-range: var(--y-scroll-effect-exclude) calc(var(--y-scroll-effect-exclude) + var(--header-box-height));
876
+ }
877
+
878
+ :host(._aero) :is(header, .main, footer) {
879
+ backdrop-filter: blur(var(--aero-blur));
880
+ }
881
+
882
+ .header-box {
883
+ position: relative;
884
+
885
+ display: flex;
886
+ align-items: center;
887
+ justify-content: center;
888
+
889
+ --transform: translateY(35%);
890
+
891
+ animation-timing-function: linear;
892
+ animation-fill-mode: forwards;
893
+ animation-name: disappear, transform-n;
894
+ animation-timeline: --view-scroll;
895
+ animation-range: var(--y-scroll-effect-exclude) calc(var(--y-scroll-effect-exclude) + (var(--header-box-height) / 2));
896
+ }
897
+
898
+ :host(._horz) .header-box {
899
+ display: none;
900
+ }
901
+
902
+ .header-bar {
903
+ position: relative;
904
+ z-index: 1;
905
+
906
+ display: flex;
907
+ align-items: start;
908
+ justify-content: space-between;
909
+ }
910
+
911
+ .header-bar {
912
+ gap: 0.6rem;
913
+ padding-block: 0.8rem;
914
+ padding-inline: 1.2rem;
915
+ }
916
+
917
+ .header-left {
918
+ display: flex;
919
+ align-items: start;
920
+ gap: 0.6rem;
921
+
922
+ opacity: 0;
923
+
924
+ animation-timing-function: linear;
925
+ animation-fill-mode: forwards;
926
+ animation-name: appear;
927
+ animation-timeline: --view-scroll;
928
+ animation-range: calc(var(--y-scroll-effect-exclude) + (var(--header-box-height) / 2)) calc(var(--y-scroll-effect-exclude) + var(--header-box-height));
929
+ }
930
+
931
+ :host(._horz) .header-left,
932
+ header:not(:has(slot[name="header-box"]:is(.has-slotted, :not(:empty)))) .header-left {
933
+ opacity: 1;
934
+ }
935
+
936
+ :host([type="info"]) header {
937
+ color: var(--header-color-info);
938
+ }
939
+
940
+ :host([type="success"]) header {
941
+ color: var(--header-color-success);
942
+ }
943
+
944
+ :host([type="error"]) header {
945
+ color: var(--header-color-error);
946
+ }
947
+
948
+ /* ----------- */
949
+
950
+ footer {
951
+ position: sticky;
952
+ bottom: 0;
953
+ z-index: 1;
954
+
955
+ border-bottom-left-radius: var(--radius-bottom-left);
956
+ border-bottom-right-radius: var(--radius-bottom-right);
957
+
958
+ color: var(--footer-color-default);
959
+ background: var(--footer-background);
960
+ }
961
+
962
+ :host([type="info"]) footer {
963
+ color: var(--footer-color-info);
964
+ }
965
+
966
+ :host([type="success"]) footer {
967
+ color: var(--footer-color-success);
968
+ }
969
+
970
+ :host([type="error"]) footer {
971
+ color: var(--footer-color-error);
972
+ }
973
+
974
+ /* ------------ */
975
+
976
+ footer .footer-bar {
977
+ position: sticky;
978
+ left: 0;
979
+ right: 0;
980
+ }
981
+
982
+ /* ----------- */
983
+
984
+ :host(:popover-open) .view {
985
+ scroll-snap-type: y mandatory;
986
+ }
987
+
988
+ :host(._horz:popover-open) .view {
989
+ scroll-snap-type: x mandatory;
990
+ }
991
+
992
+ .view>.spacing {
993
+ scroll-snap-align: var(--scroll-snap-start);
994
+ }
995
+
996
+ .main {
997
+ flex-grow: 1;
998
+ scroll-margin-top: var(--header-bar-height);
999
+ scroll-margin-bottom: var(--footer-bar-height);
1000
+ scroll-snap-align: var(--scroll-snap-start);
1001
+ }
1002
+
1003
+ :host(:is(._top, ._left._horz)) .main {
1004
+ scroll-snap-align: none;
1005
+ }
1006
+
1007
+ :host(:is(._top, ._left._horz)) .container {
1008
+ scroll-snap-align: var(--scroll-snap-start);
1009
+ }
1010
+
1011
+ header {
1012
+ scroll-snap-align: start;
1013
+ }
1014
+
1015
+ .header-bar {
1016
+ scroll-snap-align: start;
1017
+ }
1018
+
1019
+ /* ----------- */
1020
+
1021
+ .scrollport-anchor {
1022
+ position: relative;
1023
+ height: 0;
1024
+ }
1025
+
1026
+ :host(:not(._top:not(._horz))) footer .scrollport-anchor,
1027
+ :host(._top:not(._horz)) header .scrollport-anchor {
1028
+ display: none;
1029
+ }
1030
+
1031
+ .scrollport {
1032
+ position: sticky;
1033
+ top: var(--header-bar-height);
1034
+ left: 0;
1035
+ right: 0;
1036
+
1037
+ container-type: size;
1038
+ height: var(--view-inner-height);
1039
+ width: var(--view-width);
1040
+
1041
+ pointer-events: none;
1042
+ }
1043
+
1044
+ footer .scrollport {
1045
+ top: auto;
1046
+ position: absolute;
1047
+ bottom: 0;
1048
+ }
1049
+
1050
+ :host(._scrollbars._top:not(._horz)) .scrollport {
1051
+ height: calc(var(--view-inner-height) - var(--header-box-height));
1052
+ }
1053
+
1054
+ :host(._scrollbars._left._horz) .scrollport {
1055
+ width: calc(var(--view-width) - var(--minmax-length));
1056
+ }
1057
+
1058
+ :host(._scrollbars) .scrollbar-track {
1059
+ position: absolute;
1060
+ display: block;
1061
+ overflow: hidden;
1062
+
1063
+ height: 100%;
1064
+ top: 0;
1065
+ right: 0;
1066
+ padding: 6px;
1067
+
1068
+ opacity: 0;
1069
+
1070
+ animation: appear linear;
1071
+ animation-timeline: --view-scroll;
1072
+ animation-range: var(--scrollbar-appear-range);
1073
+ animation-fill-mode: forwards;
1074
+ }
1075
+
1076
+ :host(._scrollbars._horz) .scrollbar-track {
1077
+ height: unset;
1078
+ width: 100%;
1079
+ top: auto;
1080
+ bottom: 0;
1081
+
1082
+ container-type: inline-size;
1083
+ }
1084
+
1085
+ :host(._scrollbars) .scrollbar-thumb {
1086
+ width: var(--scrollbar-thumb-width);
1087
+ height: var(--scrollbar-thumb-height);
1088
+ background: var(--scrollbar-thumb-color);
1089
+ border-radius: 10px;
1090
+
1091
+ --scrollbar-thumb-start: translateY(0);
1092
+ --scrollbar-thumb-length: translateY(calc(100cqh - 100% - 12px));
1093
+
1094
+ animation: move-scrollbar-thumb linear both;
1095
+ animation-timeline: --view-scroll;
1096
+ animation-range: var(--scrollbar-progress-range);
1097
+ }
1098
+
1099
+ :host(._scrollbars._horz) .scrollbar-thumb {
1100
+ height: var(--scrollbar-thumb-width);
1101
+ width: var(--scrollbar-thumb-height);
1102
+ --scrollbar-thumb-start: translateX(0);
1103
+ --scrollbar-thumb-length: translateX(calc(100cqw - 100%));
1104
+ }
1105
+
1106
+ /* ----------- */
1107
+
1108
+ :host(:popover-open) {
1109
+ display: flex;
1110
+ opacity: 1;
1111
+ transform: none;
1112
+ }
1113
+
1114
+ @starting-style {
1115
+ :host(:popover-open) {
1116
+ opacity: 0;
1117
+ transform: var(--entry-transform);
1118
+ }
1119
+ }
1120
+
1121
+ /* ----------- */
1122
+
1123
+ :host::backdrop {
1124
+ /* Transition */
1125
+ transition:
1126
+ display 0.2s allow-discrete,
1127
+ overlay 0.2s allow-discrete,
1128
+ backdrop-filter 0.2s;
1129
+ }
1130
+
1131
+ :host(:popover-open)::backdrop {
1132
+ backdrop-filter: blur(3px);
1133
+ }
1134
+
1135
+ :host(:not([popover="manual"]):popover-open)::backdrop {
1136
+ backdrop-filter: blur(0px);
1137
+ }
1138
+
1139
+ @starting-style {
1140
+ :host(:popover-open)::backdrop {
1141
+ backdrop-filter: none;
1142
+ }
1143
+ }
1144
+
1145
+ :host(:popover-open)::before {
1146
+ position: fixed;
1147
+ inset: 0;
1148
+ display: block;
1149
+ content: "";
1150
+ z-index: -1;
1151
+ }
1152
+
1153
+ .icon {
1154
+ display: none;
1155
+ opacity: 0.6;
1156
+ }
1157
+
1158
+ :host([type="info"]) .icon._info,
1159
+ :host([type="success"]) .icon._success,
1160
+ :host([type="error"]) .icon._error,
1161
+ :host([type="warning"]) .icon._warning {
1162
+ display: block;
1163
+ }
1164
+
1165
+ :host([type="info"]) .container {
1166
+ color: var(--color-info);
1167
+ }
1168
+
1169
+ :host([type="success"]) .container {
1170
+ color: var(--color-success);
1171
+ }
1172
+
1173
+ :host([type="error"]) .container {
1174
+ color: var(--color-error);
1175
+ }
1176
+
1177
+ :host([type="warning"]) .container {
1178
+ color: var(--color-warning);
1179
+ }
1180
+
1181
+ .main {
1182
+ color: var(--color-default);
1183
+ background-color: var(--background);
1184
+ }
1185
+
1186
+ .view:not(:has(footer slot:is(.has-slotted, :not(:empty)))) .main {
1187
+ border-bottom-left-radius: var(--radius-bottom-left);
1188
+ border-bottom-right-radius: var(--radius-bottom-right);
1189
+ }
1190
+
1191
+ .close-button {
1192
+ padding-inline: 0;
1193
+ display: flex;
1194
+ align-items: center;
1195
+ justify-content: center;
1196
+ appearance: none;
1197
+ color: gray;
1198
+ cursor: pointer;
1199
+ border: none;
1200
+ background: none;
1201
+ }
1202
+
1203
+ :host(:not([popover="manual"])) {
1204
+ pointer-events: none;
1205
+ }
1206
+
1207
+ :host(:not([popover="manual"])) .close-button {
1208
+ display: none;
1209
+ }
1210
+
1211
+ .close-button:hover {
1212
+ opacity: 0.8;
1213
+ }
1214
+
1215
+ :host(._horz) :is(.header-left, .close-button) {
1216
+ position: sticky;
1217
+ left: 1.2rem;
1218
+ right: 1.2rem;
1219
+ }
1220
+
1221
+ ${this.css}
1222
+ </style>
1223
+ `;
1224
+ }
1225
+ }
1226
+
1227
+ // ---------------- DialogElement
1228
+
1229
+ export class DialogResponseEvent extends Event {
1230
+
1231
+ #data;
1232
+ get data() { return this.#data; }
1233
+
1234
+ constructor(data) {
1235
+ super('response');
1236
+ this.#data = data;
1237
+ }
1238
+ }
1239
+
1240
+ export class DialogElement extends ModalElement {
1241
+
1242
+ constructor() {
1243
+ super();
1244
+ this.addEventListener('toggle', (e) => {
1245
+ if (e.newState === 'open' && !this.querySelector('[autofocus]')) {
1246
+ this.shadowRoot.querySelector('[autofocus]')?.focus();
1247
+ }
1248
+ });
1249
+ }
1250
+
1251
+ hidePopover() { this.respondWith(null); }
1252
+
1253
+ respondWith(response) {
1254
+ const event = new DialogResponseEvent(response);
1255
+ this.dispatchEvent(event);
1256
+ super.hidePopover();
1257
+
1258
+ let onresponse;
1259
+ if (onresponse = this.getAttribute('onresponse')?.trim()) {
1260
+ Function('event', onresponse).call(this, event);
1261
+ }
1262
+ }
1263
+
1264
+ respondWithData() {
1265
+ const data = this.querySelector('form')
1266
+ || this.shadowRoot.querySelector('form');
1267
+ this.respondWith(data);
1268
+ }
1269
+
1270
+ render(data = {}) {
1271
+ this.type = data.type;
1272
+
1273
+ const html = [data.message];
1274
+ if (data.actions?.[0]) {
1275
+ html.push(`<span slot="action-0">${data.actions[0]}</span>`);
1276
+ }
1277
+ if (data.actions?.[1]) {
1278
+ html.push(`<span slot="action-1">${data.actions[1]}</span>`);
1279
+ }
1280
+
1281
+ this.innerHTML = html.join('\n');
1282
+ }
1283
+
1284
+ #onresponseHandler = null;
1285
+
1286
+ set onresponse(handler) {
1287
+ if (this.#onresponseHandler) {
1288
+ this.removeEventListener('response', this.#onresponseHandler);
1289
+ }
1290
+ if (typeof handler === 'function') {
1291
+ this.addEventListener('response', this.#onresponseHandler);
1292
+ } else if (handler !== null && handler !== undefined) {
1293
+ throw new Error('onresponse must be null or a function');
1294
+ }
1295
+ this.#onresponseHandler = handler;
1296
+ }
1297
+
1298
+ get onresponse() { return this.#onresponseHandler; }
1299
+
1300
+ get actionTexts() { return ['Cancel', 'Submit']; }
1301
+
1302
+ get footerHTML() {
1303
+ return `
1304
+ <button
1305
+ part="action-0"
1306
+ class="action _secondary"
1307
+ onclick="this.getRootNode().host.hidePopover()">
1308
+ <svg xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M256-176 176-256l224-224-224-224 80-80 224 224 224-224 80 80-224 224 224 224-80 80-224-224-224 224Z"/></svg>
1309
+ <slot name="action-0" onslotchange="this.classList.toggle('has-slotted', !!this.assignedElements().length);">${this.actionTexts[0]}</slot>
1310
+ </button>
1311
+
1312
+ <button
1313
+ part="action-1"
1314
+ class="action _primary"
1315
+ onclick="this.getRootNode().host.respondWithData()">
1316
+ <svg xmlns="http://www.w3.org/2000/svg" height="1.4em" width="1.4em" viewBox="0 -960 960 960" fill="currentColor"><path d="M369-222 128-463l84-84 157 157 379-379 84 84-463 463Z"/></svg>
1317
+ <slot name="action-1" onslotchange="this.classList.toggle('has-slotted', !!this.assignedElements().length);">${this.actionTexts[1]}</slot>
1318
+ </button>
1319
+ `;
1320
+ }
1321
+
1322
+ get css() {
1323
+ return super.css + `
1324
+ :host {
1325
+ --primary-button-color: var(--dialog-primary-button-color, black);
1326
+ --primary-button-background: var(--dialog-primary-button-background, white);
1327
+ --secondary-button-color: var(--dialog-secondary-button-color, white);
1328
+ --secondary-button-background: var(--dialog-secondary-button-background, black);
1329
+ --button-radius: var(--dialog-button-radius, 10px);
1330
+ }
1331
+
1332
+ .main {
1333
+ display: flex;
1334
+ flex-direction: column;
1335
+ gap: 1rem;
1336
+ padding: 1rem;
1337
+ }
1338
+
1339
+ .footer-bar {
1340
+ display: flex;
1341
+ align-items: center;
1342
+ gap: 0.5rem;
1343
+ padding: 1rem;
1344
+ }
1345
+
1346
+ button.action {
1347
+ whitespace: nowrap;
1348
+ display: flex;
1349
+ align-items: center;
1350
+ justify-content: center;
1351
+ gap: 0.5rem;
1352
+ border: none;
1353
+ border-radius: var(--button-radius);
1354
+ padding: 0.5rem 1rem;
1355
+ cursor: pointer;
1356
+ transition: all 0.2s;
1357
+ font-weight: bold;
1358
+ flex-grow: 1;
1359
+ }
1360
+
1361
+ button.action:hover {
1362
+ opacity: 0.8;
1363
+ }
1364
+
1365
+ button.action:is(:focus, :active, .active) {
1366
+ outline: none;
1367
+ box-shadow: none;
1368
+ opacity: 0.5;
1369
+ }
1370
+
1371
+ button.action._primary {
1372
+ background-color: var(--primary-button-background);
1373
+ color: var(--primary-button-color);
1374
+ }
1375
+
1376
+ button.action._secondary {
1377
+ background-color: var(--secondary-button-background);
1378
+ color: var(--secondary-button-color);
1379
+ }
1380
+
1381
+ button.action:not(:has(slot:is(.has-slotted, :not(:empty)))) {
1382
+ display: none;
1383
+ }
1384
+ `;
1385
+ }
1386
+ }
1387
+
1388
+ // ---------------- PromptElement
1389
+
1390
+ export class PromptElement extends DialogElement {
1391
+
1392
+ static get observedAttributes() {
1393
+ return ['value', 'placeholder'].concat(super.observedAttributes || []);
1394
+ }
1395
+
1396
+ attributeChangedCallback(name, old, _new) {
1397
+ super.attributeChangedCallback?.(...arguments);
1398
+ const input = this.shadowRoot.querySelector('input');
1399
+ if (name === 'value') { input.value = _new; }
1400
+ if (name === 'placeholder') { input.placeholder = _new; }
1401
+ }
1402
+
1403
+ set placeholder(value) {
1404
+ if ([undefined, null].includes(value)) {
1405
+ this.removeAttribute('placeholder');
1406
+ } else this.setAttribute('placeholder', value);
1407
+ }
1408
+
1409
+ get placeholder() { return this.getAttribute('placeholder'); }
1410
+
1411
+ set value(value) {
1412
+ if ([undefined, null].includes(value)) {
1413
+ this.removeAttribute('value');
1414
+ } else this.setAttribute('value', value);
1415
+ }
1416
+
1417
+ get value() { return this.getAttribute('value'); }
1418
+
1419
+ respondWithData() {
1420
+ const data = this.shadowRoot.querySelector('input').value;
1421
+ this.respondWith(data);
1422
+ }
1423
+
1424
+ render(data = {}) {
1425
+ this.value = data.value;
1426
+ this.placeholder = data.placeholder;
1427
+ super.render(data);
1428
+ }
1429
+
1430
+ get mainHTML() {
1431
+ return `
1432
+ <form class="main" part="main" onsubmit="this.getRootNode().host.respondWithData(); event.preventDefault();">
1433
+ <slot></slot>
1434
+ <slot name="input">
1435
+ <input part="input" type="text" autocomplete="off" autofocus placeholder="Enter response">
1436
+ </slot>
1437
+ </form>
1438
+ `;
1439
+ }
1440
+
1441
+ get actionTexts() { return ['Cancel', 'Submit']; }
1442
+
1443
+ get css() {
1444
+ return super.css + `
1445
+ :host {
1446
+ --input-color: var(--prompt-input-color, inherit);
1447
+ --input-background: var(--prompt-input-background, rgba(255, 255, 255, 0.2));
1448
+ --input-radius: var(--prompt-input-radius, 10px);
1449
+ }
1450
+
1451
+ input {
1452
+ width: 100%;
1453
+ border: none;
1454
+ border-radius: var(--input-radius);
1455
+ padding: 0.6rem 1rem;
1456
+ color: var(--input-color);
1457
+ background-color: var(--input-background);
1458
+ }
1459
+
1460
+ input::placeholder {
1461
+ color: currentColor;
1462
+ }
1463
+ `;
1464
+ }
1465
+ }
1466
+
1467
+ // ---------------- ConfirmElement
1468
+
1469
+ export class ConfirmElement extends DialogElement {
1470
+ get actionTexts() { return ['No', 'Yes']; }
1471
+
1472
+ respondWith(response) { super.respondWith(!!response); }
1473
+
1474
+ respondWithData() { super.respondWith(true); }
1475
+ }
1476
+
1477
+ // ---------------- AlertElement
1478
+
1479
+ export class AlertElement extends DialogElement {
1480
+ get actionTexts() { return ['', 'Got it']; }
1481
+ }
1482
+
1483
+ // ---------------- define
1484
+
1485
+ export function defineElements() {
1486
+ try {
1487
+ CSS.registerProperty({
1488
+ name: '--wq-radius',
1489
+ syntax: '<length-percentage>',
1490
+ inherits: true,
1491
+ initialValue: '0'
1492
+ });
1493
+ } catch (e) { }
1494
+ customElements.define('wq-toast', ToastElement);
1495
+ customElements.define('wq-modal', ModalElement);
1496
+ customElements.define('wq-dialog', DialogElement);
1497
+ customElements.define('wq-prompt', PromptElement);
1498
+ customElements.define('wq-confirm', ConfirmElement);
1499
+ customElements.define('wq-alert', AlertElement);
1500
+ }