@webqit/webflo 0.20.32 → 0.20.33
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,108 @@ 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
|
+
if (this.classList.contains('_horz')) {
|
|
386
|
+
viewWidth = this.#viewElement.clientWidth/* instead of offsetHeight; safari reasons */;
|
|
387
|
+
left = viewWidth - this.#spacingElement.clientWidth;
|
|
388
|
+
} else {
|
|
389
|
+
viewHeight = this.#viewElement.clientHeight/* instead of offsetHeight; safari reasons */;
|
|
390
|
+
top = viewHeight - this.#spacingElement.clientHeight;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (this.#viewElement.scrollTop < top || this.#viewElement.scrollLeft < left) {
|
|
394
|
+
this.#viewElement.scrollTo({ top, left });
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
this.#viewElement.style.setProperty('--header-box-height', this.#headerBoxElement.offsetHeight + 'px');
|
|
400
|
+
this.#viewElement.style.setProperty('--header-max-height', this.#headerElement.offsetHeight + 'px');
|
|
401
|
+
this.#viewElement.style.setProperty('--footer-max-height', this.#footerElement.offsetHeight + 'px');
|
|
402
|
+
|
|
403
|
+
if (this.classList.contains('_container')) return;
|
|
404
|
+
if (viewWidth === undefined) viewWidth = this.#viewElement.clientWidth;
|
|
405
|
+
if (viewHeight === undefined) viewHeight = this.#viewElement.clientHeight;
|
|
406
|
+
|
|
407
|
+
this.#viewElement.style.setProperty('--view-width', viewWidth + 'px');
|
|
408
|
+
this.#viewElement.style.setProperty('--view-height', viewHeight + 'px');
|
|
409
|
+
});
|
|
364
410
|
}
|
|
365
411
|
|
|
366
412
|
#unbindMinmaxWorker = null;
|
|
367
413
|
|
|
368
414
|
bindMinmaxWorker() {
|
|
369
415
|
const swipeDismiss = this.classList.contains('_swipe-dismiss');
|
|
370
|
-
const minmaxEvents = this.classList.contains('_minmax');
|
|
416
|
+
const minmaxEvents = this.classList.contains('_minmax-events');
|
|
371
417
|
|
|
372
418
|
if (!swipeDismiss && !minmaxEvents) return;
|
|
373
419
|
|
|
374
|
-
const viewElement = this.shadowRoot.querySelector('.view');
|
|
375
|
-
const sentinelElement = this.shadowRoot.querySelector('.sentinel');
|
|
376
|
-
const spacingElement = viewElement.querySelector('.spacing');
|
|
377
|
-
|
|
378
420
|
const options = {
|
|
379
|
-
root: viewElement,
|
|
421
|
+
root: this.#viewElement,
|
|
380
422
|
threshold: [0, 1]
|
|
381
423
|
};
|
|
382
424
|
|
|
383
425
|
const observer = new IntersectionObserver((entries) => {
|
|
426
|
+
if (!this.#userScrolled) return;
|
|
427
|
+
|
|
384
428
|
for (const entry of entries) {
|
|
385
429
|
// Minmax events
|
|
386
|
-
if (entry.target === spacingElement) {
|
|
430
|
+
if (entry.target === this.#spacingElement) {
|
|
387
431
|
const event = new ModalMinmaxEvent(1 - entry.intersectionRatio);
|
|
388
432
|
this.dispatchEvent(event);
|
|
389
433
|
|
|
@@ -394,65 +438,80 @@ export class ModalElement extends HTMLElement {
|
|
|
394
438
|
}
|
|
395
439
|
|
|
396
440
|
// For auto-closing
|
|
397
|
-
if (entry.target === sentinelElement
|
|
441
|
+
if (entry.target === this.#sentinelElement
|
|
442
|
+
&& entry.isIntersecting
|
|
443
|
+
&& entry.intersectionRatio >= 0.8) {
|
|
398
444
|
this.hidePopover();
|
|
399
|
-
setTimeout(() => spacingElement.scrollIntoView(), 300);
|
|
400
445
|
}
|
|
401
446
|
}
|
|
402
447
|
}, options);
|
|
403
448
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
#onminmaxHandler = null;
|
|
449
|
+
setTimeout(() => {
|
|
450
|
+
if (minmaxEvents) observer.observe(this.#spacingElement);
|
|
451
|
+
if (swipeDismiss) observer.observe(this.#sentinelElement);
|
|
452
|
+
}, 200);
|
|
410
453
|
|
|
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;
|
|
454
|
+
this.#unbindMinmaxWorker = () => observer.disconnect();
|
|
421
455
|
}
|
|
422
456
|
|
|
423
|
-
|
|
457
|
+
#userScrolled = false;
|
|
458
|
+
#unbindDimensionsWorker;
|
|
424
459
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
460
|
+
#bindDimensionsWorker() {
|
|
461
|
+
this.#userScrolled = false;
|
|
462
|
+
const handleUserScroll = () => this.#userScrolled = true;
|
|
463
|
+
this.#viewElement.addEventListener('scroll', handleUserScroll);
|
|
430
464
|
|
|
431
|
-
get type() { return this.getAttribute('type'); }
|
|
432
465
|
|
|
433
|
-
|
|
466
|
+
this.updateScrollViewDimensions();
|
|
467
|
+
const handleResize = () => this.updateScrollViewDimensions();
|
|
468
|
+
window.addEventListener('resize', handleResize);
|
|
434
469
|
|
|
435
|
-
|
|
470
|
+
this.#unbindDimensionsWorker?.();
|
|
471
|
+
this.#unbindDimensionsWorker = () => {
|
|
472
|
+
window.removeEventListener('resize', handleResize);
|
|
473
|
+
this.#viewElement.removeEventListener('scroll', handleUserScroll);
|
|
474
|
+
};
|
|
475
|
+
}
|
|
436
476
|
|
|
437
|
-
|
|
477
|
+
connectedCallback() {
|
|
478
|
+
if (!this.popover) {
|
|
479
|
+
this.popover = 'manual';
|
|
480
|
+
}
|
|
481
|
+
if (this.hasAttribute('open')) {
|
|
482
|
+
this.showPopover();
|
|
483
|
+
}
|
|
484
|
+
}
|
|
438
485
|
|
|
439
|
-
|
|
486
|
+
disconnectedCallback() {
|
|
487
|
+
this.#unbindDimensionsWorker?.();
|
|
488
|
+
this.#unbindDimensionsWorker = null;
|
|
489
|
+
this.#unbindMinmaxWorker?.();
|
|
490
|
+
this.#unbindMinmaxWorker = null;
|
|
491
|
+
}
|
|
440
492
|
|
|
441
|
-
get
|
|
493
|
+
static get observedAttributes() {
|
|
494
|
+
return ['class'];
|
|
495
|
+
}
|
|
442
496
|
|
|
443
|
-
|
|
497
|
+
attributeChangedCallback(name, old, _new) {
|
|
498
|
+
if (name === 'class') this.#bindDimensionsWorker();
|
|
499
|
+
}
|
|
444
500
|
|
|
445
501
|
constructor() {
|
|
446
502
|
super();
|
|
447
503
|
this.attachShadow({ mode: 'open' });
|
|
448
504
|
|
|
449
505
|
this.addEventListener('toggle', (e) => {
|
|
450
|
-
if (e.newState
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
506
|
+
if (e.newState === 'open') {
|
|
507
|
+
this.#bindDimensionsWorker();
|
|
508
|
+
this.bindMinmaxWorker();
|
|
509
|
+
} else if (e.newState === 'closed') {
|
|
510
|
+
this.#unbindDimensionsWorker?.();
|
|
511
|
+
this.#unbindDimensionsWorker = null;
|
|
512
|
+
this.#unbindMinmaxWorker?.();
|
|
513
|
+
this.#unbindMinmaxWorker = null;
|
|
514
|
+
}
|
|
456
515
|
});
|
|
457
516
|
|
|
458
517
|
this.shadowRoot.innerHTML = `
|
|
@@ -489,27 +548,20 @@ export class ModalElement extends HTMLElement {
|
|
|
489
548
|
</button>
|
|
490
549
|
</div>
|
|
491
550
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
551
|
+
</header>
|
|
552
|
+
|
|
553
|
+
<div class="scrollport-anchor">
|
|
554
|
+
<div class="scrollport">
|
|
555
|
+
<div class="scrollbar-track">
|
|
556
|
+
<div class="scrollbar-thumb"></div>
|
|
497
557
|
</div>
|
|
498
558
|
</div>
|
|
499
|
-
</
|
|
559
|
+
</div>
|
|
500
560
|
|
|
501
561
|
${this.mainHTML || `<div class="main" part="main">${this.contentHTML || `<slot></slot>`
|
|
502
562
|
}</div>`}
|
|
503
563
|
|
|
504
564
|
<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
565
|
<div class="footer-bar" part="footer-bar">
|
|
514
566
|
<slot
|
|
515
567
|
name="footer"
|
|
@@ -586,7 +638,6 @@ export class ModalElement extends HTMLElement {
|
|
|
586
638
|
|
|
587
639
|
--expanse-length: var(--modal-expanse-length, 0px);
|
|
588
640
|
--minmax-length: var(--modal-minmax-length, 0px);
|
|
589
|
-
--swipe-dismiss-length: var(--modal-swipe-dismiss-length, 0px);
|
|
590
641
|
|
|
591
642
|
--scrollbar-thumb-color: var(--modal-scrollbar-thumb-color, black);
|
|
592
643
|
--scrollbar-thumb-width: var(--modal-scrollbar-thumb-width, 4px);
|
|
@@ -603,6 +654,14 @@ export class ModalElement extends HTMLElement {
|
|
|
603
654
|
--entry-transform: translateY(var(--translation));
|
|
604
655
|
--exit-transform: translateY(calc(var(--translation) * var(--exit-factor)));
|
|
605
656
|
}
|
|
657
|
+
|
|
658
|
+
:host(._swipe-dismiss) .view {
|
|
659
|
+
--swipe-dismiss-length: var(--modal-swipe-dismiss-length, calc(var(--view-height) - var(--minmax-length)));
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
:host(._horz._swipe-dismiss) .view {
|
|
663
|
+
--swipe-dismiss-length: var(--modal-swipe-dismiss-length, calc(var(--view-width) - var(--minmax-length)));
|
|
664
|
+
}
|
|
606
665
|
|
|
607
666
|
/* transform reversal */
|
|
608
667
|
|
|
@@ -648,6 +707,11 @@ export class ModalElement extends HTMLElement {
|
|
|
648
707
|
--view-width: 100cqw;
|
|
649
708
|
}
|
|
650
709
|
|
|
710
|
+
:host(._container._horz) .view {
|
|
711
|
+
--view-height: 100cqh;
|
|
712
|
+
--view-width: calc(100cqw - var(--expanse-length));
|
|
713
|
+
}
|
|
714
|
+
|
|
651
715
|
/* transform reversal */
|
|
652
716
|
|
|
653
717
|
:host(:is(._top:not(._horz), ._left._horz)) .view {
|
|
@@ -724,9 +788,8 @@ export class ModalElement extends HTMLElement {
|
|
|
724
788
|
|
|
725
789
|
/* flex orientation */
|
|
726
790
|
|
|
727
|
-
:host
|
|
791
|
+
:host,
|
|
728
792
|
.view {
|
|
729
|
-
display: flex;
|
|
730
793
|
flex-direction: column;
|
|
731
794
|
align-items: stretch;
|
|
732
795
|
}
|
|
@@ -748,7 +811,8 @@ export class ModalElement extends HTMLElement {
|
|
|
748
811
|
/* spacing */
|
|
749
812
|
|
|
750
813
|
:host>.spacing,
|
|
751
|
-
.view>.spacing
|
|
814
|
+
.view>.spacing,
|
|
815
|
+
.view>.sentinel {
|
|
752
816
|
position: relative;
|
|
753
817
|
display: block;
|
|
754
818
|
flex-shrink: 0;
|
|
@@ -769,17 +833,15 @@ export class ModalElement extends HTMLElement {
|
|
|
769
833
|
:host(:not(._horz)) .view>.spacing { height: var(--minmax-length); }
|
|
770
834
|
:host(._horz) .view>.spacing { width: var(--minmax-length); }
|
|
771
835
|
|
|
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); }
|
|
836
|
+
:host(:not(._horz)) .view>.sentinel { height: var(--swipe-dismiss-length); }
|
|
837
|
+
:host(._horz) .view>.sentinel { width: var(--swipe-dismiss-length); }
|
|
777
838
|
|
|
778
839
|
/* ----------- */
|
|
779
840
|
|
|
780
841
|
.view {
|
|
781
842
|
position: relative;
|
|
782
843
|
flex-grow: 1;
|
|
844
|
+
display: flex;
|
|
783
845
|
|
|
784
846
|
pointer-events: none;
|
|
785
847
|
|
|
@@ -833,16 +895,13 @@ export class ModalElement extends HTMLElement {
|
|
|
833
895
|
position: relative;
|
|
834
896
|
flex-grow: 1;
|
|
835
897
|
|
|
836
|
-
min-height: 100%;
|
|
837
|
-
min-width: 100%;
|
|
838
|
-
|
|
839
898
|
pointer-events: auto;
|
|
840
899
|
|
|
841
900
|
display: flex;
|
|
842
901
|
flex-direction: column;
|
|
843
902
|
}
|
|
844
903
|
|
|
845
|
-
:host(._swipe-dismiss) .container {
|
|
904
|
+
:host(._swipe-dismiss-fadeout) .container {
|
|
846
905
|
animation-timing-function: linear;
|
|
847
906
|
animation-fill-mode: both;
|
|
848
907
|
animation-name: appear;
|
|
@@ -850,7 +909,7 @@ export class ModalElement extends HTMLElement {
|
|
|
850
909
|
animation-range: 0 var(--swipe-dismiss-length);
|
|
851
910
|
}
|
|
852
911
|
|
|
853
|
-
:host(._swipe-dismiss:is(._top:not(._horz), ._left._horz)) .container {
|
|
912
|
+
:host(._swipe-dismiss-fadeout:is(._top:not(._horz), ._left._horz)) .container {
|
|
854
913
|
animation-name: disappear;
|
|
855
914
|
animation-range: calc(100% - var(--swipe-dismiss-length)) 100%;
|
|
856
915
|
}
|
|
@@ -860,7 +919,7 @@ export class ModalElement extends HTMLElement {
|
|
|
860
919
|
header {
|
|
861
920
|
position: sticky;
|
|
862
921
|
top: calc(var(--header-box-height) * -1);
|
|
863
|
-
z-index:
|
|
922
|
+
z-index: 2;
|
|
864
923
|
|
|
865
924
|
display: flex;
|
|
866
925
|
flex-direction: column;
|
|
@@ -870,6 +929,8 @@ export class ModalElement extends HTMLElement {
|
|
|
870
929
|
|
|
871
930
|
border-top-left-radius: var(--radius-top-left);
|
|
872
931
|
border-top-right-radius: var(--radius-top-right);
|
|
932
|
+
|
|
933
|
+
order: 1;
|
|
873
934
|
}
|
|
874
935
|
|
|
875
936
|
:host(:not(._horz)) header {
|
|
@@ -962,6 +1023,8 @@ export class ModalElement extends HTMLElement {
|
|
|
962
1023
|
|
|
963
1024
|
color: var(--footer-color-default);
|
|
964
1025
|
background: var(--footer-background);
|
|
1026
|
+
|
|
1027
|
+
order: 5;
|
|
965
1028
|
}
|
|
966
1029
|
|
|
967
1030
|
:host([type="info"]) footer {
|
|
@@ -986,15 +1049,16 @@ export class ModalElement extends HTMLElement {
|
|
|
986
1049
|
|
|
987
1050
|
/* ----------- */
|
|
988
1051
|
|
|
989
|
-
|
|
1052
|
+
.view {
|
|
990
1053
|
scroll-snap-type: y mandatory;
|
|
991
1054
|
}
|
|
992
1055
|
|
|
993
|
-
:host(._horz
|
|
1056
|
+
:host(._horz) .view {
|
|
994
1057
|
scroll-snap-type: x mandatory;
|
|
995
1058
|
}
|
|
996
1059
|
|
|
997
|
-
.view>.spacing
|
|
1060
|
+
.view>.spacing,
|
|
1061
|
+
.view>.sentinel {
|
|
998
1062
|
scroll-snap-align: var(--scroll-snap-start);
|
|
999
1063
|
}
|
|
1000
1064
|
|
|
@@ -1003,6 +1067,8 @@ export class ModalElement extends HTMLElement {
|
|
|
1003
1067
|
scroll-margin-top: var(--header-min-height);
|
|
1004
1068
|
scroll-margin-bottom: var(--footer-min-height);
|
|
1005
1069
|
scroll-snap-align: var(--scroll-snap-start);
|
|
1070
|
+
|
|
1071
|
+
order: 3;
|
|
1006
1072
|
}
|
|
1007
1073
|
|
|
1008
1074
|
:host(:is(._top, ._left._horz)) .main {
|
|
@@ -1024,42 +1090,108 @@ export class ModalElement extends HTMLElement {
|
|
|
1024
1090
|
/* ----------- */
|
|
1025
1091
|
|
|
1026
1092
|
.scrollport-anchor {
|
|
1027
|
-
|
|
1093
|
+
order: 2;
|
|
1094
|
+
|
|
1095
|
+
position: sticky;
|
|
1096
|
+
top: var(--header-min-height);
|
|
1097
|
+
bottom: var(--footer-min-height);
|
|
1098
|
+
left: 0;
|
|
1099
|
+
right: 0;
|
|
1100
|
+
display: flex;
|
|
1101
|
+
flex-direction: column;
|
|
1102
|
+
|
|
1028
1103
|
height: 0;
|
|
1104
|
+
width: var(--view-width);
|
|
1029
1105
|
}
|
|
1030
1106
|
|
|
1031
|
-
:host(:
|
|
1032
|
-
|
|
1033
|
-
|
|
1107
|
+
:host(:is(._left._horz, ._top:not(._horz))) .scrollport-anchor {
|
|
1108
|
+
justify-content: end;
|
|
1109
|
+
order: 4;
|
|
1034
1110
|
}
|
|
1035
1111
|
|
|
1036
1112
|
.scrollport {
|
|
1037
|
-
position:
|
|
1038
|
-
|
|
1039
|
-
left: 0;
|
|
1040
|
-
right: 0;
|
|
1041
|
-
|
|
1042
|
-
container-type: size;
|
|
1113
|
+
position: relative;
|
|
1114
|
+
|
|
1043
1115
|
height: var(--view-inner-height);
|
|
1044
1116
|
width: var(--view-width);
|
|
1117
|
+
flex-shrink: 0;
|
|
1045
1118
|
|
|
1046
1119
|
pointer-events: none;
|
|
1047
1120
|
}
|
|
1048
1121
|
|
|
1049
|
-
|
|
1122
|
+
:host(._top:not(._horz)) .scrollport {
|
|
1123
|
+
height: calc(var(--view-inner-height) - var(--header-box-height));
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/* -- scroll unfold -- */
|
|
1127
|
+
|
|
1128
|
+
:host(._scroll-unfold) .scrollport {
|
|
1129
|
+
display: flex;
|
|
1130
|
+
flex-direction: column;
|
|
1131
|
+
justify-content: space-between;
|
|
1132
|
+
align-items: stretch;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
:host(._scroll-unfold._horz) .scrollport {
|
|
1136
|
+
flex-direction: row;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
:host(._scroll-unfold) .scrollport::before,
|
|
1140
|
+
:host(._scroll-unfold) .scrollport::after {
|
|
1141
|
+
position: sticky;
|
|
1142
|
+
display: block;
|
|
1143
|
+
content: "";
|
|
1144
|
+
opacity: 0;
|
|
1145
|
+
|
|
1146
|
+
background: var(--background);
|
|
1147
|
+
|
|
1148
|
+
mask-repeat: no-repeat;
|
|
1149
|
+
mask-size: 100% 100%;
|
|
1150
|
+
|
|
1151
|
+
animation-timing-function: linear;
|
|
1152
|
+
animation-fill-mode: forwards;
|
|
1153
|
+
animation-name: appear;
|
|
1154
|
+
animation-timeline: --view-scroll;
|
|
1155
|
+
|
|
1156
|
+
animation-range: var(--scrollbar-progress-range);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
:host(._scroll-unfold:not(._horz)) .scrollport::before,
|
|
1160
|
+
:host(._scroll-unfold:not(._horz)) .scrollport::after {
|
|
1161
|
+
top: var(--header-min-height);
|
|
1162
|
+
height: 25%;
|
|
1163
|
+
|
|
1164
|
+
mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
|
|
1165
|
+
-webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
:host(._scroll-unfold:not(._horz)) .scrollport::after {
|
|
1169
|
+
bottom: var(--footer-min-height);
|
|
1050
1170
|
top: auto;
|
|
1051
|
-
|
|
1052
|
-
|
|
1171
|
+
opacity: 1;
|
|
1172
|
+
animation-name: disappear;
|
|
1173
|
+
transform: scaleY(-1);
|
|
1053
1174
|
}
|
|
1054
1175
|
|
|
1055
|
-
:host(.
|
|
1056
|
-
|
|
1176
|
+
:host(._scroll-unfold._horz) .scrollport::before,
|
|
1177
|
+
:host(._scroll-unfold._horz) .scrollport::after {
|
|
1178
|
+
left: 0;
|
|
1179
|
+
width: 25%;
|
|
1180
|
+
|
|
1181
|
+
mask-image: linear-gradient(to right, black 0%, transparent 100%);
|
|
1182
|
+
-webkit-mask-image: linear-gradient(to right, black 0%, transparent 100%);
|
|
1057
1183
|
}
|
|
1058
1184
|
|
|
1059
|
-
:host(.
|
|
1060
|
-
|
|
1185
|
+
:host(._scroll-unfold._horz) .scrollport::after {
|
|
1186
|
+
right: 0;
|
|
1187
|
+
left: auto;
|
|
1188
|
+
opacity: 1;
|
|
1189
|
+
animation-name: disappear;
|
|
1190
|
+
transform: scaleX(-1);
|
|
1061
1191
|
}
|
|
1062
1192
|
|
|
1193
|
+
/* -- scrollbar -- */
|
|
1194
|
+
|
|
1063
1195
|
:host(._scrollbars) .scrollbar-track {
|
|
1064
1196
|
position: absolute;
|
|
1065
1197
|
display: block;
|
|
@@ -1070,6 +1202,8 @@ export class ModalElement extends HTMLElement {
|
|
|
1070
1202
|
right: 0;
|
|
1071
1203
|
padding: 6px;
|
|
1072
1204
|
|
|
1205
|
+
container-type: size;
|
|
1206
|
+
|
|
1073
1207
|
opacity: 0;
|
|
1074
1208
|
|
|
1075
1209
|
animation: appear linear;
|
|
@@ -1104,6 +1238,7 @@ export class ModalElement extends HTMLElement {
|
|
|
1104
1238
|
:host(._scrollbars._horz) .scrollbar-thumb {
|
|
1105
1239
|
height: var(--scrollbar-thumb-width);
|
|
1106
1240
|
width: var(--scrollbar-thumb-height);
|
|
1241
|
+
|
|
1107
1242
|
--scrollbar-thumb-start: translateX(0);
|
|
1108
1243
|
--scrollbar-thumb-length: translateX(calc(100cqw - 100%));
|
|
1109
1244
|
}
|
|
@@ -1185,7 +1320,7 @@ export class ModalElement extends HTMLElement {
|
|
|
1185
1320
|
|
|
1186
1321
|
.main {
|
|
1187
1322
|
color: var(--color-default);
|
|
1188
|
-
background
|
|
1323
|
+
background: var(--background);
|
|
1189
1324
|
}
|
|
1190
1325
|
|
|
1191
1326
|
.view:not(:has(footer slot:is(.has-slotted, :not(:empty)))) .main {
|
|
@@ -1227,12 +1362,19 @@ export class ModalElement extends HTMLElement {
|
|
|
1227
1362
|
${this.css}
|
|
1228
1363
|
</style>
|
|
1229
1364
|
`;
|
|
1365
|
+
|
|
1366
|
+
this.#viewElement = this.shadowRoot.querySelector('.view');
|
|
1367
|
+
this.#sentinelElement = this.#viewElement.querySelector('.sentinel');
|
|
1368
|
+
this.#spacingElement = this.#viewElement.querySelector('.spacing');
|
|
1369
|
+
this.#headerElement = this.#viewElement.querySelector('header');
|
|
1370
|
+
this.#headerBoxElement = this.#viewElement.querySelector('.header-box');
|
|
1371
|
+
this.#footerElement = this.#viewElement.querySelector('footer');
|
|
1230
1372
|
}
|
|
1231
1373
|
}
|
|
1232
1374
|
|
|
1233
1375
|
// ---------------- DialogElement
|
|
1234
1376
|
|
|
1235
|
-
|
|
1377
|
+
class DialogResponseEvent extends Event {
|
|
1236
1378
|
|
|
1237
1379
|
#data;
|
|
1238
1380
|
get data() { return this.#data; }
|
|
@@ -1243,7 +1385,7 @@ export class DialogResponseEvent extends Event {
|
|
|
1243
1385
|
}
|
|
1244
1386
|
}
|
|
1245
1387
|
|
|
1246
|
-
|
|
1388
|
+
class DialogElement extends ModalElement {
|
|
1247
1389
|
|
|
1248
1390
|
constructor() {
|
|
1249
1391
|
super();
|
|
@@ -1336,6 +1478,7 @@ export class DialogElement extends ModalElement {
|
|
|
1336
1478
|
}
|
|
1337
1479
|
|
|
1338
1480
|
.main {
|
|
1481
|
+
flex-shrink: 0;
|
|
1339
1482
|
display: flex;
|
|
1340
1483
|
flex-direction: column;
|
|
1341
1484
|
gap: 1rem;
|
|
@@ -1394,7 +1537,7 @@ export class DialogElement extends ModalElement {
|
|
|
1394
1537
|
|
|
1395
1538
|
// ---------------- PromptElement
|
|
1396
1539
|
|
|
1397
|
-
|
|
1540
|
+
class PromptElement extends DialogElement {
|
|
1398
1541
|
|
|
1399
1542
|
static get observedAttributes() {
|
|
1400
1543
|
return ['value', 'placeholder'].concat(super.observedAttributes || []);
|
|
@@ -1403,8 +1546,8 @@ export class PromptElement extends DialogElement {
|
|
|
1403
1546
|
attributeChangedCallback(name, old, _new) {
|
|
1404
1547
|
super.attributeChangedCallback?.(...arguments);
|
|
1405
1548
|
const input = this.shadowRoot.querySelector('input');
|
|
1406
|
-
if (name === 'value')
|
|
1407
|
-
if (name === 'placeholder')
|
|
1549
|
+
if (name === 'value') input.value = _new;
|
|
1550
|
+
if (name === 'placeholder') input.placeholder = _new;
|
|
1408
1551
|
}
|
|
1409
1552
|
|
|
1410
1553
|
set placeholder(value) {
|
|
@@ -1473,7 +1616,7 @@ export class PromptElement extends DialogElement {
|
|
|
1473
1616
|
|
|
1474
1617
|
// ---------------- ConfirmElement
|
|
1475
1618
|
|
|
1476
|
-
|
|
1619
|
+
class ConfirmElement extends DialogElement {
|
|
1477
1620
|
get actionTexts() { return ['No', 'Yes']; }
|
|
1478
1621
|
|
|
1479
1622
|
respondWith(response) { super.respondWith(!!response); }
|
|
@@ -1483,7 +1626,7 @@ export class ConfirmElement extends DialogElement {
|
|
|
1483
1626
|
|
|
1484
1627
|
// ---------------- AlertElement
|
|
1485
1628
|
|
|
1486
|
-
|
|
1629
|
+
class AlertElement extends DialogElement {
|
|
1487
1630
|
get actionTexts() { return ['', 'Got it']; }
|
|
1488
1631
|
}
|
|
1489
1632
|
|