@webqit/webflo 0.20.32 → 0.20.34
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/package.json
CHANGED
|
@@ -35,6 +35,9 @@ export class WebfloClient extends AppRuntime {
|
|
|
35
35
|
#background;
|
|
36
36
|
get background() { return this.#background; }
|
|
37
37
|
|
|
38
|
+
#viewport;
|
|
39
|
+
get viewport() { return this.#viewport; }
|
|
40
|
+
|
|
38
41
|
get isClientSide() { return true; }
|
|
39
42
|
|
|
40
43
|
constructor(bootstrap, host) {
|
|
@@ -56,6 +59,68 @@ export class WebfloClient extends AppRuntime {
|
|
|
56
59
|
phase: 0
|
|
57
60
|
};
|
|
58
61
|
this.#background = new StarPort({ handshake: 1, autoClose: false });
|
|
62
|
+
|
|
63
|
+
// ---------------------
|
|
64
|
+
// Dynamic viewport styling
|
|
65
|
+
|
|
66
|
+
const oskToken = 'interactive-widget=resizes-content';
|
|
67
|
+
const hasOsk = (content) => content?.includes(oskToken);
|
|
68
|
+
const removeOsk = (content) => {
|
|
69
|
+
if (content?.includes('interactive-widget')) {
|
|
70
|
+
return content
|
|
71
|
+
.split(',')
|
|
72
|
+
.filter((s) => !s.includes('interactive-widget'))
|
|
73
|
+
.map((s) => s.trim())
|
|
74
|
+
.join(', ');
|
|
75
|
+
}
|
|
76
|
+
return content;
|
|
77
|
+
};
|
|
78
|
+
const addOsk = (content) => {
|
|
79
|
+
if (content?.includes('interactive-widget')) {
|
|
80
|
+
return content
|
|
81
|
+
.split(',')
|
|
82
|
+
.map((s) => s.includes('interactive-widget') ? oskToken : s.trim())
|
|
83
|
+
.join(', ');
|
|
84
|
+
}
|
|
85
|
+
return content + ', ' + oskToken;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const viewportMeta = document.querySelector('meta[name="viewport"]');
|
|
89
|
+
const viewportMetaInitialContent = viewportMeta?.content;
|
|
90
|
+
const themeColorMeta = document.querySelector('meta[name="theme-color"]');
|
|
91
|
+
const renderViewportMetas = (entry) => {
|
|
92
|
+
viewportMeta?.setAttribute('content', entry.osk ? addOsk(viewportMetaInitialContent) : removeOsk(viewportMetaInitialContent));
|
|
93
|
+
themeColorMeta?.setAttribute('content', entry.themeColor);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const initial = {
|
|
97
|
+
themeColor: themeColorMeta?.content,
|
|
98
|
+
osk: hasOsk(viewportMetaInitialContent),
|
|
99
|
+
};
|
|
100
|
+
const viewportStack = [initial];
|
|
101
|
+
|
|
102
|
+
this.#viewport = {
|
|
103
|
+
push(entryId, { themeColor = viewportStack[0].themeColor, osk = viewportStack[0].osk }) {
|
|
104
|
+
if (typeof entryId !== 'string' || !entryId?.trim()) {
|
|
105
|
+
throw new Error('entryId cannot be ommited');
|
|
106
|
+
}
|
|
107
|
+
if (viewportStack.find((e) => e.entryId === entryId)) return;
|
|
108
|
+
viewportStack.unshift({ entryId, themeColor, osk });
|
|
109
|
+
renderViewportMetas(viewportStack[0]);
|
|
110
|
+
},
|
|
111
|
+
pop(entryId) {
|
|
112
|
+
if (typeof entryId !== 'string' || !entryId?.trim()) {
|
|
113
|
+
throw new Error('entryId cannot be ommited');
|
|
114
|
+
}
|
|
115
|
+
const index = viewportStack.findIndex((e) => e.entryId === entryId);
|
|
116
|
+
if (index === -1) return;
|
|
117
|
+
viewportStack.splice(index, 1);
|
|
118
|
+
renderViewportMetas(viewportStack[0]);
|
|
119
|
+
},
|
|
120
|
+
current() {
|
|
121
|
+
return viewportStack[0];
|
|
122
|
+
}
|
|
123
|
+
};
|
|
59
124
|
}
|
|
60
125
|
|
|
61
126
|
async initialize() {
|
|
@@ -600,6 +665,7 @@ export class WebfloClient extends AppRuntime {
|
|
|
600
665
|
state: {},
|
|
601
666
|
data: $response.body,
|
|
602
667
|
env: 'client',
|
|
668
|
+
viewport: this.viewport,
|
|
603
669
|
navigator: this.navigator,
|
|
604
670
|
location: this.location,
|
|
605
671
|
network: this.network, // request, redirect, error, status, remote
|
|
@@ -42,12 +42,12 @@ export class WebfloRootClientA extends WebfloClient {
|
|
|
42
42
|
async initialize() {
|
|
43
43
|
// INITIALIZATIONS
|
|
44
44
|
const instanceController = await super.initialize();
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
// Bind network status handlers
|
|
47
47
|
const onlineHandler = () => Observer.set(this.network, 'status', window.navigator.onLine);
|
|
48
48
|
window.addEventListener('online', onlineHandler, { signal: instanceController.signal });
|
|
49
49
|
window.addEventListener('offline', onlineHandler, { signal: instanceController.signal });
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
// Window opener pinging
|
|
52
52
|
if (window.opener) {
|
|
53
53
|
const beforeunloadHandler = () => window.opener.postMessage('close');
|
|
@@ -93,7 +93,7 @@ export class WebfloRootClientA extends WebfloClient {
|
|
|
93
93
|
scopeObj.data = JSON.parse(this.host.querySelector(`script[rel="hydration"][type="application/json"]`)?.textContent?.trim() || 'null');
|
|
94
94
|
} catch (e) { }
|
|
95
95
|
scopeObj.response = new LiveResponse(scopeObj.data, { headers: { 'Content-Type': 'application/json' } });
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
for (const name of ['X-Message-Port', 'X-Webflo-Dev-Mode']) {
|
|
98
98
|
const metaElement = this.host.querySelector(`meta[name="${name}"]`);
|
|
99
99
|
if (!metaElement) continue;
|
|
@@ -136,7 +136,7 @@ export class WebfloRootClientA extends WebfloClient {
|
|
|
136
136
|
try { window.history.pushState({}, '', newHref); } catch (e) { }
|
|
137
137
|
};
|
|
138
138
|
const instanceController = super.controlClassic/*IMPORTANT*/(locationCallback);
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
// ONPOPSTATE
|
|
141
141
|
const popstateHandler = (e) => {
|
|
142
142
|
if (this.isHashChange(location)) {
|
|
@@ -158,7 +158,7 @@ export class WebfloRootClientA extends WebfloClient {
|
|
|
158
158
|
this.navigate(location.href, {}, detail);
|
|
159
159
|
};
|
|
160
160
|
window.addEventListener('popstate', popstateHandler, { signal: instanceController.signal });
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
return instanceController;
|
|
163
163
|
}
|
|
164
164
|
|
|
@@ -18,6 +18,8 @@ export class WebfloSubClient extends WebfloClient {
|
|
|
18
18
|
|
|
19
19
|
get capabilities() { return this.#superRuntime.capabilities; }
|
|
20
20
|
|
|
21
|
+
get viewport() { return this.#superRuntime.viewport; }
|
|
22
|
+
|
|
21
23
|
get withViewTransitions() { return this.host.hasAttribute('viewtransitions'); }
|
|
22
24
|
|
|
23
25
|
constructor(superRuntime, host) {
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
// ---------------- ToastElement
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
class ToastElement extends HTMLElement {
|
|
4
|
+
|
|
5
|
+
set type(value) {
|
|
6
|
+
if ([undefined, null].includes(value)) {
|
|
7
|
+
this.removeAttribute('type');
|
|
8
|
+
} else this.setAttribute('type', value);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get type() { return this.getAttribute('type'); }
|
|
12
|
+
|
|
13
|
+
get contentHTML() { return ''; }
|
|
14
|
+
|
|
15
|
+
get css() { return ''; }
|
|
4
16
|
|
|
5
17
|
#childToast = null;
|
|
6
18
|
|
|
@@ -21,12 +33,6 @@ export class ToastElement extends HTMLElement {
|
|
|
21
33
|
}
|
|
22
34
|
}
|
|
23
35
|
|
|
24
|
-
connectedCallback() {
|
|
25
|
-
if (!this.popover) {
|
|
26
|
-
this.popover = 'auto';
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
36
|
render({ content, context }, childToast = null, recursion = 1) {
|
|
31
37
|
if (context && recursion > 0) {
|
|
32
38
|
const directChildToast = document.createElement(this.tagName);
|
|
@@ -54,18 +60,12 @@ export class ToastElement extends HTMLElement {
|
|
|
54
60
|
this.innerHTML = content.message;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
this.
|
|
60
|
-
}
|
|
63
|
+
connectedCallback() {
|
|
64
|
+
if (!this.popover) {
|
|
65
|
+
this.popover = 'auto';
|
|
66
|
+
}
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
get type() { return this.getAttribute('type'); }
|
|
64
|
-
|
|
65
|
-
get contentHTML() { return ''; }
|
|
66
|
-
|
|
67
|
-
get css() { return ''; }
|
|
68
|
-
|
|
69
69
|
constructor() {
|
|
70
70
|
super();
|
|
71
71
|
this.attachShadow({ mode: 'open' });
|
|
@@ -315,7 +315,7 @@ export class ToastElement extends HTMLElement {
|
|
|
315
315
|
|
|
316
316
|
// ---------------- ModalElement
|
|
317
317
|
|
|
318
|
-
|
|
318
|
+
class ModalMinmaxEvent extends Event {
|
|
319
319
|
|
|
320
320
|
#ratio;
|
|
321
321
|
get ratio() { return this.#ratio; }
|
|
@@ -326,64 +326,110 @@ export class ModalMinmaxEvent extends Event {
|
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
|
|
329
|
+
class ModalElement extends HTMLElement {
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
const viewElement = this.shadowRoot.querySelector('.view');
|
|
333
|
-
const headerElement = this.shadowRoot.querySelector('header');
|
|
334
|
-
const headerBoxElement = this.shadowRoot.querySelector('.header-box');
|
|
335
|
-
const footerElement = this.shadowRoot.querySelector('footer');
|
|
336
|
-
requestAnimationFrame(() => {
|
|
337
|
-
viewElement.style.setProperty('--header-box-height', headerBoxElement.offsetHeight + 'px');
|
|
338
|
-
viewElement.style.setProperty('--header-max-height', headerElement.offsetHeight + 'px');
|
|
339
|
-
viewElement.style.setProperty('--footer-max-height', footerElement.offsetHeight + 'px');
|
|
340
|
-
if (this.classList.contains('_container')) return;
|
|
341
|
-
viewElement.style.setProperty('--view-width', viewElement.clientWidth/* instead of offsetHeight; safari reasons */ + 'px');
|
|
342
|
-
viewElement.style.setProperty('--view-height', viewElement.clientHeight/* instead of offsetHeight; safari reasons */ + 'px');
|
|
343
|
-
});
|
|
344
|
-
}
|
|
331
|
+
#onminmaxHandler = null;
|
|
345
332
|
|
|
346
|
-
|
|
347
|
-
if (
|
|
348
|
-
this.
|
|
333
|
+
set onminmax(handler) {
|
|
334
|
+
if (this.#onminmaxHandler) {
|
|
335
|
+
this.removeEventListener('onminmax', this.#onminmaxHandler);
|
|
349
336
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
|
|
337
|
+
if (typeof handler === 'function') {
|
|
338
|
+
this.addEventListener('minmax', this.#onminmaxHandler);
|
|
339
|
+
} else if (handler !== null && handler !== undefined) {
|
|
340
|
+
throw new Error('onminmax must be null or a function');
|
|
354
341
|
}
|
|
342
|
+
this.#onminmaxHandler = handler;
|
|
343
|
+
}
|
|
355
344
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
345
|
+
get onminmax() { return this.#onminmaxHandler; }
|
|
346
|
+
|
|
347
|
+
set type(value) {
|
|
348
|
+
if ([undefined, null].includes(value)) {
|
|
349
|
+
this.removeAttribute('type');
|
|
350
|
+
} else this.setAttribute('type', value);
|
|
359
351
|
}
|
|
360
352
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
353
|
+
get type() { return this.getAttribute('type'); }
|
|
354
|
+
|
|
355
|
+
get headerBoxHTML() { return ''; }
|
|
356
|
+
|
|
357
|
+
get headerHTML() { return ''; }
|
|
358
|
+
|
|
359
|
+
get mainHTML() { return ''; }
|
|
360
|
+
|
|
361
|
+
get contentHTML() { return ''; }
|
|
362
|
+
|
|
363
|
+
get footerHTML() { return ''; }
|
|
364
|
+
|
|
365
|
+
get css() { return ''; }
|
|
366
|
+
|
|
367
|
+
#viewElement;
|
|
368
|
+
#sentinelElement;
|
|
369
|
+
#spacingElement;
|
|
370
|
+
#headerElement;
|
|
371
|
+
#headerBoxElement;
|
|
372
|
+
#footerElement;
|
|
373
|
+
|
|
374
|
+
updateScrollViewDimensions() {
|
|
375
|
+
requestAnimationFrame(() => {
|
|
376
|
+
let viewWidth, viewHeight;
|
|
377
|
+
|
|
378
|
+
const swipeDismiss = this.classList.contains('_swipe-dismiss');
|
|
379
|
+
const minmaxScroll = !!window.getComputedStyle(this).getPropertyValue('--modal-minmax-length');
|
|
380
|
+
|
|
381
|
+
if (swipeDismiss || minmaxScroll) {
|
|
382
|
+
requestAnimationFrame(() => {
|
|
383
|
+
let left = 0, top = 0;
|
|
384
|
+
if (this.matches('._left._horz, ._top:not(._horz)')) {
|
|
385
|
+
this.#viewElement.scrollTo({ top, left });
|
|
386
|
+
} else {
|
|
387
|
+
if (this.classList.contains('_horz')) {
|
|
388
|
+
viewWidth = this.#viewElement.clientWidth/* instead of offsetHeight; safari reasons */;
|
|
389
|
+
left = viewWidth - this.#spacingElement.clientWidth;
|
|
390
|
+
} else {
|
|
391
|
+
viewHeight = this.#viewElement.clientHeight/* instead of offsetHeight; safari reasons */;
|
|
392
|
+
top = viewHeight - this.#spacingElement.clientHeight;
|
|
393
|
+
}
|
|
394
|
+
if (this.#viewElement.scrollTop < top || this.#viewElement.scrollLeft < left) {
|
|
395
|
+
this.#viewElement.scrollTo({ top, left });
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
this.#viewElement.style.setProperty('--header-box-height', this.#headerBoxElement.offsetHeight + 'px');
|
|
402
|
+
this.#viewElement.style.setProperty('--header-max-height', this.#headerElement.offsetHeight + 'px');
|
|
403
|
+
this.#viewElement.style.setProperty('--footer-max-height', this.#footerElement.offsetHeight + 'px');
|
|
404
|
+
|
|
405
|
+
if (this.classList.contains('_container')) return;
|
|
406
|
+
if (viewWidth === undefined) viewWidth = this.#viewElement.clientWidth;
|
|
407
|
+
if (viewHeight === undefined) viewHeight = this.#viewElement.clientHeight;
|
|
408
|
+
|
|
409
|
+
this.#viewElement.style.setProperty('--view-width', viewWidth + 'px');
|
|
410
|
+
this.#viewElement.style.setProperty('--view-height', viewHeight + 'px');
|
|
411
|
+
});
|
|
364
412
|
}
|
|
365
413
|
|
|
366
414
|
#unbindMinmaxWorker = null;
|
|
367
415
|
|
|
368
416
|
bindMinmaxWorker() {
|
|
369
417
|
const swipeDismiss = this.classList.contains('_swipe-dismiss');
|
|
370
|
-
const minmaxEvents = this.classList.contains('_minmax');
|
|
418
|
+
const minmaxEvents = this.classList.contains('_minmax-events');
|
|
371
419
|
|
|
372
420
|
if (!swipeDismiss && !minmaxEvents) return;
|
|
373
421
|
|
|
374
|
-
const viewElement = this.shadowRoot.querySelector('.view');
|
|
375
|
-
const sentinelElement = this.shadowRoot.querySelector('.sentinel');
|
|
376
|
-
const spacingElement = viewElement.querySelector('.spacing');
|
|
377
|
-
|
|
378
422
|
const options = {
|
|
379
|
-
root: viewElement,
|
|
423
|
+
root: this.#viewElement,
|
|
380
424
|
threshold: [0, 1]
|
|
381
425
|
};
|
|
382
426
|
|
|
383
427
|
const observer = new IntersectionObserver((entries) => {
|
|
428
|
+
if (!this.#userScrolled) return;
|
|
429
|
+
|
|
384
430
|
for (const entry of entries) {
|
|
385
431
|
// Minmax events
|
|
386
|
-
if (entry.target === spacingElement) {
|
|
432
|
+
if (entry.target === this.#spacingElement) {
|
|
387
433
|
const event = new ModalMinmaxEvent(1 - entry.intersectionRatio);
|
|
388
434
|
this.dispatchEvent(event);
|
|
389
435
|
|
|
@@ -394,65 +440,80 @@ export class ModalElement extends HTMLElement {
|
|
|
394
440
|
}
|
|
395
441
|
|
|
396
442
|
// For auto-closing
|
|
397
|
-
if (entry.target === sentinelElement
|
|
443
|
+
if (entry.target === this.#sentinelElement
|
|
444
|
+
&& entry.isIntersecting
|
|
445
|
+
&& entry.intersectionRatio >= 0.8) {
|
|
398
446
|
this.hidePopover();
|
|
399
|
-
setTimeout(() => spacingElement.scrollIntoView(), 300);
|
|
400
447
|
}
|
|
401
448
|
}
|
|
402
449
|
}, options);
|
|
403
450
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
#onminmaxHandler = null;
|
|
451
|
+
setTimeout(() => {
|
|
452
|
+
if (minmaxEvents) observer.observe(this.#spacingElement);
|
|
453
|
+
if (swipeDismiss) observer.observe(this.#sentinelElement);
|
|
454
|
+
}, 200);
|
|
410
455
|
|
|
411
|
-
|
|
412
|
-
if (this.#onminmaxHandler) {
|
|
413
|
-
this.removeEventListener('onminmax', this.#onminmaxHandler);
|
|
414
|
-
}
|
|
415
|
-
if (typeof handler === 'function') {
|
|
416
|
-
this.addEventListener('minmax', this.#onminmaxHandler);
|
|
417
|
-
} else if (handler !== null && handler !== undefined) {
|
|
418
|
-
throw new Error('onminmax must be null or a function');
|
|
419
|
-
}
|
|
420
|
-
this.#onminmaxHandler = handler;
|
|
456
|
+
this.#unbindMinmaxWorker = () => observer.disconnect();
|
|
421
457
|
}
|
|
422
458
|
|
|
423
|
-
|
|
459
|
+
#userScrolled = false;
|
|
460
|
+
#unbindDimensionsWorker;
|
|
424
461
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
462
|
+
#bindDimensionsWorker() {
|
|
463
|
+
this.#userScrolled = false;
|
|
464
|
+
const handleUserScroll = () => this.#userScrolled = true;
|
|
465
|
+
this.#viewElement.addEventListener('scroll', handleUserScroll);
|
|
430
466
|
|
|
431
|
-
get type() { return this.getAttribute('type'); }
|
|
432
467
|
|
|
433
|
-
|
|
468
|
+
this.updateScrollViewDimensions();
|
|
469
|
+
const handleResize = () => this.updateScrollViewDimensions();
|
|
470
|
+
window.addEventListener('resize', handleResize);
|
|
434
471
|
|
|
435
|
-
|
|
472
|
+
this.#unbindDimensionsWorker?.();
|
|
473
|
+
this.#unbindDimensionsWorker = () => {
|
|
474
|
+
window.removeEventListener('resize', handleResize);
|
|
475
|
+
this.#viewElement.removeEventListener('scroll', handleUserScroll);
|
|
476
|
+
};
|
|
477
|
+
}
|
|
436
478
|
|
|
437
|
-
|
|
479
|
+
connectedCallback() {
|
|
480
|
+
if (!this.popover) {
|
|
481
|
+
this.popover = 'manual';
|
|
482
|
+
}
|
|
483
|
+
if (this.hasAttribute('open')) {
|
|
484
|
+
this.showPopover();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
438
487
|
|
|
439
|
-
|
|
488
|
+
disconnectedCallback() {
|
|
489
|
+
this.#unbindDimensionsWorker?.();
|
|
490
|
+
this.#unbindDimensionsWorker = null;
|
|
491
|
+
this.#unbindMinmaxWorker?.();
|
|
492
|
+
this.#unbindMinmaxWorker = null;
|
|
493
|
+
}
|
|
440
494
|
|
|
441
|
-
get
|
|
495
|
+
static get observedAttributes() {
|
|
496
|
+
return ['class'];
|
|
497
|
+
}
|
|
442
498
|
|
|
443
|
-
|
|
499
|
+
attributeChangedCallback(name, old, _new) {
|
|
500
|
+
if (name === 'class') this.#bindDimensionsWorker();
|
|
501
|
+
}
|
|
444
502
|
|
|
445
503
|
constructor() {
|
|
446
504
|
super();
|
|
447
505
|
this.attachShadow({ mode: 'open' });
|
|
448
506
|
|
|
449
507
|
this.addEventListener('toggle', (e) => {
|
|
450
|
-
if (e.newState
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
508
|
+
if (e.newState === 'open') {
|
|
509
|
+
this.#bindDimensionsWorker();
|
|
510
|
+
this.bindMinmaxWorker();
|
|
511
|
+
} else if (e.newState === 'closed') {
|
|
512
|
+
this.#unbindDimensionsWorker?.();
|
|
513
|
+
this.#unbindDimensionsWorker = null;
|
|
514
|
+
this.#unbindMinmaxWorker?.();
|
|
515
|
+
this.#unbindMinmaxWorker = null;
|
|
516
|
+
}
|
|
456
517
|
});
|
|
457
518
|
|
|
458
519
|
this.shadowRoot.innerHTML = `
|
|
@@ -489,27 +550,20 @@ export class ModalElement extends HTMLElement {
|
|
|
489
550
|
</button>
|
|
490
551
|
</div>
|
|
491
552
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
553
|
+
</header>
|
|
554
|
+
|
|
555
|
+
<div class="scrollport-anchor">
|
|
556
|
+
<div class="scrollport">
|
|
557
|
+
<div class="scrollbar-track">
|
|
558
|
+
<div class="scrollbar-thumb"></div>
|
|
497
559
|
</div>
|
|
498
560
|
</div>
|
|
499
|
-
</
|
|
561
|
+
</div>
|
|
500
562
|
|
|
501
563
|
${this.mainHTML || `<div class="main" part="main">${this.contentHTML || `<slot></slot>`
|
|
502
564
|
}</div>`}
|
|
503
565
|
|
|
504
566
|
<footer part="footer">
|
|
505
|
-
<div class="scrollport-anchor">
|
|
506
|
-
<div class="scrollport">
|
|
507
|
-
<div class="scrollbar-track">
|
|
508
|
-
<div class="scrollbar-thumb"></div>
|
|
509
|
-
</div>
|
|
510
|
-
</div>
|
|
511
|
-
</div>
|
|
512
|
-
|
|
513
567
|
<div class="footer-bar" part="footer-bar">
|
|
514
568
|
<slot
|
|
515
569
|
name="footer"
|
|
@@ -586,7 +640,6 @@ export class ModalElement extends HTMLElement {
|
|
|
586
640
|
|
|
587
641
|
--expanse-length: var(--modal-expanse-length, 0px);
|
|
588
642
|
--minmax-length: var(--modal-minmax-length, 0px);
|
|
589
|
-
--swipe-dismiss-length: var(--modal-swipe-dismiss-length, 0px);
|
|
590
643
|
|
|
591
644
|
--scrollbar-thumb-color: var(--modal-scrollbar-thumb-color, black);
|
|
592
645
|
--scrollbar-thumb-width: var(--modal-scrollbar-thumb-width, 4px);
|
|
@@ -603,6 +656,14 @@ export class ModalElement extends HTMLElement {
|
|
|
603
656
|
--entry-transform: translateY(var(--translation));
|
|
604
657
|
--exit-transform: translateY(calc(var(--translation) * var(--exit-factor)));
|
|
605
658
|
}
|
|
659
|
+
|
|
660
|
+
:host(._swipe-dismiss) .view {
|
|
661
|
+
--swipe-dismiss-length: var(--modal-swipe-dismiss-length, calc(var(--view-height) - var(--minmax-length)));
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
:host(._horz._swipe-dismiss) .view {
|
|
665
|
+
--swipe-dismiss-length: var(--modal-swipe-dismiss-length, calc(var(--view-width) - var(--minmax-length)));
|
|
666
|
+
}
|
|
606
667
|
|
|
607
668
|
/* transform reversal */
|
|
608
669
|
|
|
@@ -648,6 +709,11 @@ export class ModalElement extends HTMLElement {
|
|
|
648
709
|
--view-width: 100cqw;
|
|
649
710
|
}
|
|
650
711
|
|
|
712
|
+
:host(._container._horz) .view {
|
|
713
|
+
--view-height: 100cqh;
|
|
714
|
+
--view-width: calc(100cqw - var(--expanse-length));
|
|
715
|
+
}
|
|
716
|
+
|
|
651
717
|
/* transform reversal */
|
|
652
718
|
|
|
653
719
|
:host(:is(._top:not(._horz), ._left._horz)) .view {
|
|
@@ -724,9 +790,8 @@ export class ModalElement extends HTMLElement {
|
|
|
724
790
|
|
|
725
791
|
/* flex orientation */
|
|
726
792
|
|
|
727
|
-
:host
|
|
793
|
+
:host,
|
|
728
794
|
.view {
|
|
729
|
-
display: flex;
|
|
730
795
|
flex-direction: column;
|
|
731
796
|
align-items: stretch;
|
|
732
797
|
}
|
|
@@ -748,7 +813,8 @@ export class ModalElement extends HTMLElement {
|
|
|
748
813
|
/* spacing */
|
|
749
814
|
|
|
750
815
|
:host>.spacing,
|
|
751
|
-
.view>.spacing
|
|
816
|
+
.view>.spacing,
|
|
817
|
+
.view>.sentinel {
|
|
752
818
|
position: relative;
|
|
753
819
|
display: block;
|
|
754
820
|
flex-shrink: 0;
|
|
@@ -769,17 +835,15 @@ export class ModalElement extends HTMLElement {
|
|
|
769
835
|
:host(:not(._horz)) .view>.spacing { height: var(--minmax-length); }
|
|
770
836
|
:host(._horz) .view>.spacing { width: var(--minmax-length); }
|
|
771
837
|
|
|
772
|
-
:host(:not(.
|
|
773
|
-
:host(.
|
|
774
|
-
|
|
775
|
-
:host(._horz:not(._left)) .view>.spacing { margin-left: var(--swipe-dismiss-length); }
|
|
776
|
-
:host(._horz._left) .view>.spacing { margin-right: var(--swipe-dismiss-length); }
|
|
838
|
+
:host(:not(._horz)) .view>.sentinel { height: var(--swipe-dismiss-length); }
|
|
839
|
+
:host(._horz) .view>.sentinel { width: var(--swipe-dismiss-length); }
|
|
777
840
|
|
|
778
841
|
/* ----------- */
|
|
779
842
|
|
|
780
843
|
.view {
|
|
781
844
|
position: relative;
|
|
782
845
|
flex-grow: 1;
|
|
846
|
+
display: flex;
|
|
783
847
|
|
|
784
848
|
pointer-events: none;
|
|
785
849
|
|
|
@@ -833,16 +897,13 @@ export class ModalElement extends HTMLElement {
|
|
|
833
897
|
position: relative;
|
|
834
898
|
flex-grow: 1;
|
|
835
899
|
|
|
836
|
-
min-height: 100%;
|
|
837
|
-
min-width: 100%;
|
|
838
|
-
|
|
839
900
|
pointer-events: auto;
|
|
840
901
|
|
|
841
902
|
display: flex;
|
|
842
903
|
flex-direction: column;
|
|
843
904
|
}
|
|
844
905
|
|
|
845
|
-
:host(._swipe-dismiss) .container {
|
|
906
|
+
:host(._swipe-dismiss-fadeout) .container {
|
|
846
907
|
animation-timing-function: linear;
|
|
847
908
|
animation-fill-mode: both;
|
|
848
909
|
animation-name: appear;
|
|
@@ -850,7 +911,7 @@ export class ModalElement extends HTMLElement {
|
|
|
850
911
|
animation-range: 0 var(--swipe-dismiss-length);
|
|
851
912
|
}
|
|
852
913
|
|
|
853
|
-
:host(._swipe-dismiss:is(._top:not(._horz), ._left._horz)) .container {
|
|
914
|
+
:host(._swipe-dismiss-fadeout:is(._top:not(._horz), ._left._horz)) .container {
|
|
854
915
|
animation-name: disappear;
|
|
855
916
|
animation-range: calc(100% - var(--swipe-dismiss-length)) 100%;
|
|
856
917
|
}
|
|
@@ -860,7 +921,7 @@ export class ModalElement extends HTMLElement {
|
|
|
860
921
|
header {
|
|
861
922
|
position: sticky;
|
|
862
923
|
top: calc(var(--header-box-height) * -1);
|
|
863
|
-
z-index:
|
|
924
|
+
z-index: 2;
|
|
864
925
|
|
|
865
926
|
display: flex;
|
|
866
927
|
flex-direction: column;
|
|
@@ -870,6 +931,8 @@ export class ModalElement extends HTMLElement {
|
|
|
870
931
|
|
|
871
932
|
border-top-left-radius: var(--radius-top-left);
|
|
872
933
|
border-top-right-radius: var(--radius-top-right);
|
|
934
|
+
|
|
935
|
+
order: 1;
|
|
873
936
|
}
|
|
874
937
|
|
|
875
938
|
:host(:not(._horz)) header {
|
|
@@ -962,6 +1025,8 @@ export class ModalElement extends HTMLElement {
|
|
|
962
1025
|
|
|
963
1026
|
color: var(--footer-color-default);
|
|
964
1027
|
background: var(--footer-background);
|
|
1028
|
+
|
|
1029
|
+
order: 5;
|
|
965
1030
|
}
|
|
966
1031
|
|
|
967
1032
|
:host([type="info"]) footer {
|
|
@@ -986,15 +1051,16 @@ export class ModalElement extends HTMLElement {
|
|
|
986
1051
|
|
|
987
1052
|
/* ----------- */
|
|
988
1053
|
|
|
989
|
-
|
|
1054
|
+
.view {
|
|
990
1055
|
scroll-snap-type: y mandatory;
|
|
991
1056
|
}
|
|
992
1057
|
|
|
993
|
-
:host(._horz
|
|
1058
|
+
:host(._horz) .view {
|
|
994
1059
|
scroll-snap-type: x mandatory;
|
|
995
1060
|
}
|
|
996
1061
|
|
|
997
|
-
.view>.spacing
|
|
1062
|
+
.view>.spacing,
|
|
1063
|
+
.view>.sentinel {
|
|
998
1064
|
scroll-snap-align: var(--scroll-snap-start);
|
|
999
1065
|
}
|
|
1000
1066
|
|
|
@@ -1003,6 +1069,8 @@ export class ModalElement extends HTMLElement {
|
|
|
1003
1069
|
scroll-margin-top: var(--header-min-height);
|
|
1004
1070
|
scroll-margin-bottom: var(--footer-min-height);
|
|
1005
1071
|
scroll-snap-align: var(--scroll-snap-start);
|
|
1072
|
+
|
|
1073
|
+
order: 3;
|
|
1006
1074
|
}
|
|
1007
1075
|
|
|
1008
1076
|
:host(:is(._top, ._left._horz)) .main {
|
|
@@ -1024,42 +1092,108 @@ export class ModalElement extends HTMLElement {
|
|
|
1024
1092
|
/* ----------- */
|
|
1025
1093
|
|
|
1026
1094
|
.scrollport-anchor {
|
|
1027
|
-
|
|
1095
|
+
order: 2;
|
|
1096
|
+
|
|
1097
|
+
position: sticky;
|
|
1098
|
+
top: var(--header-min-height);
|
|
1099
|
+
bottom: var(--footer-min-height);
|
|
1100
|
+
left: 0;
|
|
1101
|
+
right: 0;
|
|
1102
|
+
display: flex;
|
|
1103
|
+
flex-direction: column;
|
|
1104
|
+
|
|
1028
1105
|
height: 0;
|
|
1106
|
+
width: var(--view-width);
|
|
1029
1107
|
}
|
|
1030
1108
|
|
|
1031
|
-
:host(:
|
|
1032
|
-
|
|
1033
|
-
|
|
1109
|
+
:host(:is(._left._horz, ._top:not(._horz))) .scrollport-anchor {
|
|
1110
|
+
justify-content: end;
|
|
1111
|
+
order: 4;
|
|
1034
1112
|
}
|
|
1035
1113
|
|
|
1036
1114
|
.scrollport {
|
|
1037
|
-
position:
|
|
1038
|
-
|
|
1039
|
-
left: 0;
|
|
1040
|
-
right: 0;
|
|
1041
|
-
|
|
1042
|
-
container-type: size;
|
|
1115
|
+
position: relative;
|
|
1116
|
+
|
|
1043
1117
|
height: var(--view-inner-height);
|
|
1044
1118
|
width: var(--view-width);
|
|
1119
|
+
flex-shrink: 0;
|
|
1045
1120
|
|
|
1046
1121
|
pointer-events: none;
|
|
1047
1122
|
}
|
|
1048
1123
|
|
|
1049
|
-
|
|
1124
|
+
:host(._top:not(._horz)) .scrollport {
|
|
1125
|
+
height: calc(var(--view-inner-height) - var(--header-box-height));
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/* -- scroll unfold -- */
|
|
1129
|
+
|
|
1130
|
+
:host(._scroll-unfold) .scrollport {
|
|
1131
|
+
display: flex;
|
|
1132
|
+
flex-direction: column;
|
|
1133
|
+
justify-content: space-between;
|
|
1134
|
+
align-items: stretch;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
:host(._scroll-unfold._horz) .scrollport {
|
|
1138
|
+
flex-direction: row;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
:host(._scroll-unfold) .scrollport::before,
|
|
1142
|
+
:host(._scroll-unfold) .scrollport::after {
|
|
1143
|
+
position: sticky;
|
|
1144
|
+
display: block;
|
|
1145
|
+
content: "";
|
|
1146
|
+
opacity: 0;
|
|
1147
|
+
|
|
1148
|
+
background: var(--background);
|
|
1149
|
+
|
|
1150
|
+
mask-repeat: no-repeat;
|
|
1151
|
+
mask-size: 100% 100%;
|
|
1152
|
+
|
|
1153
|
+
animation-timing-function: linear;
|
|
1154
|
+
animation-fill-mode: forwards;
|
|
1155
|
+
animation-name: appear;
|
|
1156
|
+
animation-timeline: --view-scroll;
|
|
1157
|
+
|
|
1158
|
+
animation-range: var(--scrollbar-progress-range);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
:host(._scroll-unfold:not(._horz)) .scrollport::before,
|
|
1162
|
+
:host(._scroll-unfold:not(._horz)) .scrollport::after {
|
|
1163
|
+
top: var(--header-min-height);
|
|
1164
|
+
height: 25%;
|
|
1165
|
+
|
|
1166
|
+
mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
|
|
1167
|
+
-webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
:host(._scroll-unfold:not(._horz)) .scrollport::after {
|
|
1171
|
+
bottom: var(--footer-min-height);
|
|
1050
1172
|
top: auto;
|
|
1051
|
-
|
|
1052
|
-
|
|
1173
|
+
opacity: 1;
|
|
1174
|
+
animation-name: disappear;
|
|
1175
|
+
transform: scaleY(-1);
|
|
1053
1176
|
}
|
|
1054
1177
|
|
|
1055
|
-
:host(.
|
|
1056
|
-
|
|
1178
|
+
:host(._scroll-unfold._horz) .scrollport::before,
|
|
1179
|
+
:host(._scroll-unfold._horz) .scrollport::after {
|
|
1180
|
+
left: 0;
|
|
1181
|
+
width: 25%;
|
|
1182
|
+
|
|
1183
|
+
mask-image: linear-gradient(to right, black 0%, transparent 100%);
|
|
1184
|
+
-webkit-mask-image: linear-gradient(to right, black 0%, transparent 100%);
|
|
1057
1185
|
}
|
|
1058
1186
|
|
|
1059
|
-
:host(.
|
|
1060
|
-
|
|
1187
|
+
:host(._scroll-unfold._horz) .scrollport::after {
|
|
1188
|
+
right: 0;
|
|
1189
|
+
left: auto;
|
|
1190
|
+
opacity: 1;
|
|
1191
|
+
animation-name: disappear;
|
|
1192
|
+
transform: scaleX(-1);
|
|
1061
1193
|
}
|
|
1062
1194
|
|
|
1195
|
+
/* -- scrollbar -- */
|
|
1196
|
+
|
|
1063
1197
|
:host(._scrollbars) .scrollbar-track {
|
|
1064
1198
|
position: absolute;
|
|
1065
1199
|
display: block;
|
|
@@ -1070,6 +1204,8 @@ export class ModalElement extends HTMLElement {
|
|
|
1070
1204
|
right: 0;
|
|
1071
1205
|
padding: 6px;
|
|
1072
1206
|
|
|
1207
|
+
container-type: size;
|
|
1208
|
+
|
|
1073
1209
|
opacity: 0;
|
|
1074
1210
|
|
|
1075
1211
|
animation: appear linear;
|
|
@@ -1104,6 +1240,7 @@ export class ModalElement extends HTMLElement {
|
|
|
1104
1240
|
:host(._scrollbars._horz) .scrollbar-thumb {
|
|
1105
1241
|
height: var(--scrollbar-thumb-width);
|
|
1106
1242
|
width: var(--scrollbar-thumb-height);
|
|
1243
|
+
|
|
1107
1244
|
--scrollbar-thumb-start: translateX(0);
|
|
1108
1245
|
--scrollbar-thumb-length: translateX(calc(100cqw - 100%));
|
|
1109
1246
|
}
|
|
@@ -1133,6 +1270,39 @@ export class ModalElement extends HTMLElement {
|
|
|
1133
1270
|
backdrop-filter 0.2s;
|
|
1134
1271
|
}
|
|
1135
1272
|
|
|
1273
|
+
:host(._swipe-dismiss._container) {
|
|
1274
|
+
timeline-scope: --view-scroll;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
:host(._swipe-dismiss._container)::backdrop {
|
|
1278
|
+
opacity: 0;
|
|
1279
|
+
|
|
1280
|
+
animation-timing-function: linear;
|
|
1281
|
+
animation-fill-mode: forwards;
|
|
1282
|
+
animation-name: appear;
|
|
1283
|
+
animation-timeline: --view-scroll;
|
|
1284
|
+
|
|
1285
|
+
animation-range: 0 calc(100cqh - var(--expanse-length) - var(--minmax-length));
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
:host(._swipe-dismiss._container._horz)::backdrop {
|
|
1289
|
+
animation-range: 0 calc(100cqw - var(--expanse-length) - var(--minmax-length));
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
:host(._swipe-dismiss._container._top:not(._horz))::backdrop,
|
|
1293
|
+
:host(._swipe-dismiss._container._left._horz)::backdrop {
|
|
1294
|
+
opacity: 1;
|
|
1295
|
+
animation-name: disappear;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
:host(._swipe-dismiss._container._top:not(._horz))::backdrop {
|
|
1299
|
+
animation-range: calc(100% - (100cqh - var(--expanse-length) - var(--minmax-length))) 100%;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
:host(._swipe-dismiss._container._left._horz)::backdrop {
|
|
1303
|
+
animation-range: calc(100% - (100cqw - var(--expanse-length) - var(--minmax-length))) 100%;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1136
1306
|
:host(:popover-open)::backdrop {
|
|
1137
1307
|
backdrop-filter: blur(3px);
|
|
1138
1308
|
}
|
|
@@ -1185,7 +1355,7 @@ export class ModalElement extends HTMLElement {
|
|
|
1185
1355
|
|
|
1186
1356
|
.main {
|
|
1187
1357
|
color: var(--color-default);
|
|
1188
|
-
background
|
|
1358
|
+
background: var(--background);
|
|
1189
1359
|
}
|
|
1190
1360
|
|
|
1191
1361
|
.view:not(:has(footer slot:is(.has-slotted, :not(:empty)))) .main {
|
|
@@ -1227,12 +1397,19 @@ export class ModalElement extends HTMLElement {
|
|
|
1227
1397
|
${this.css}
|
|
1228
1398
|
</style>
|
|
1229
1399
|
`;
|
|
1400
|
+
|
|
1401
|
+
this.#viewElement = this.shadowRoot.querySelector('.view');
|
|
1402
|
+
this.#sentinelElement = this.#viewElement.querySelector('.sentinel');
|
|
1403
|
+
this.#spacingElement = this.#viewElement.querySelector('.spacing');
|
|
1404
|
+
this.#headerElement = this.#viewElement.querySelector('header');
|
|
1405
|
+
this.#headerBoxElement = this.#viewElement.querySelector('.header-box');
|
|
1406
|
+
this.#footerElement = this.#viewElement.querySelector('footer');
|
|
1230
1407
|
}
|
|
1231
1408
|
}
|
|
1232
1409
|
|
|
1233
1410
|
// ---------------- DialogElement
|
|
1234
1411
|
|
|
1235
|
-
|
|
1412
|
+
class DialogResponseEvent extends Event {
|
|
1236
1413
|
|
|
1237
1414
|
#data;
|
|
1238
1415
|
get data() { return this.#data; }
|
|
@@ -1243,7 +1420,7 @@ export class DialogResponseEvent extends Event {
|
|
|
1243
1420
|
}
|
|
1244
1421
|
}
|
|
1245
1422
|
|
|
1246
|
-
|
|
1423
|
+
class DialogElement extends ModalElement {
|
|
1247
1424
|
|
|
1248
1425
|
constructor() {
|
|
1249
1426
|
super();
|
|
@@ -1336,6 +1513,7 @@ export class DialogElement extends ModalElement {
|
|
|
1336
1513
|
}
|
|
1337
1514
|
|
|
1338
1515
|
.main {
|
|
1516
|
+
flex-shrink: 0;
|
|
1339
1517
|
display: flex;
|
|
1340
1518
|
flex-direction: column;
|
|
1341
1519
|
gap: 1rem;
|
|
@@ -1394,7 +1572,7 @@ export class DialogElement extends ModalElement {
|
|
|
1394
1572
|
|
|
1395
1573
|
// ---------------- PromptElement
|
|
1396
1574
|
|
|
1397
|
-
|
|
1575
|
+
class PromptElement extends DialogElement {
|
|
1398
1576
|
|
|
1399
1577
|
static get observedAttributes() {
|
|
1400
1578
|
return ['value', 'placeholder'].concat(super.observedAttributes || []);
|
|
@@ -1403,8 +1581,8 @@ export class PromptElement extends DialogElement {
|
|
|
1403
1581
|
attributeChangedCallback(name, old, _new) {
|
|
1404
1582
|
super.attributeChangedCallback?.(...arguments);
|
|
1405
1583
|
const input = this.shadowRoot.querySelector('input');
|
|
1406
|
-
if (name === 'value')
|
|
1407
|
-
if (name === 'placeholder')
|
|
1584
|
+
if (name === 'value') input.value = _new;
|
|
1585
|
+
if (name === 'placeholder') input.placeholder = _new;
|
|
1408
1586
|
}
|
|
1409
1587
|
|
|
1410
1588
|
set placeholder(value) {
|
|
@@ -1473,7 +1651,7 @@ export class PromptElement extends DialogElement {
|
|
|
1473
1651
|
|
|
1474
1652
|
// ---------------- ConfirmElement
|
|
1475
1653
|
|
|
1476
|
-
|
|
1654
|
+
class ConfirmElement extends DialogElement {
|
|
1477
1655
|
get actionTexts() { return ['No', 'Yes']; }
|
|
1478
1656
|
|
|
1479
1657
|
respondWith(response) { super.respondWith(!!response); }
|
|
@@ -1483,7 +1661,7 @@ export class ConfirmElement extends DialogElement {
|
|
|
1483
1661
|
|
|
1484
1662
|
// ---------------- AlertElement
|
|
1485
1663
|
|
|
1486
|
-
|
|
1664
|
+
class AlertElement extends DialogElement {
|
|
1487
1665
|
get actionTexts() { return ['', 'Got it']; }
|
|
1488
1666
|
}
|
|
1489
1667
|
|