@inizioevoke/astro-core 1.6.5 → 2.0.0
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 +3 -5
- package/src/components/FlipCard/FlipCard.ts +15 -9
- package/src/components/ISI/{RightISI.astro → Right/RightISI.astro} +16 -9
- package/src/components/ISI/Right/RightISI.css +61 -0
- package/src/components/ISI/{RightISI.md → Right/RightISI.md} +0 -1
- package/src/components/ISI/Right/RightISI.ts +78 -0
- package/src/components/ISI/Right/index.ts +1 -0
- package/src/components/ISI/index.ts +5 -0
- package/src/components/Modal/Modal.astro +11 -5
- package/src/components/Modal/Modal.css +49 -39
- package/src/components/Modal/Modal.ts +186 -169
- package/src/components/PdfViewer/PdfViewer.astro +6 -3
- package/src/components/PdfViewer/PdfViewer.css +5 -3
- package/src/components/PdfViewer/PdfViewer.ts +226 -198
- package/src/components/ScrollContainer/ScrollContainer.ts +23 -2
- package/src/components/index.ts +1 -0
- package/src/lib/gestures/index.ts +2 -0
- package/src/lib/gestures/pinch.ts +66 -0
- package/src/lib/gestures/swipe.ts +153 -0
- package/src/lib/index.ts +2 -0
- package/src/components/ISI/RightISI.css +0 -31
- package/src/components/ISI/RightISI.ts +0 -27
- package/src/components/Modal/v2/Modal.astro +0 -42
- package/src/components/Modal/v2/Modal.css +0 -155
- package/src/components/Modal/v2/Modal.ts +0 -219
- package/src/components/Modal/v2/ModalOverlay.astro +0 -14
- package/src/components/Modal/v2/ModalTrigger.astro +0 -29
- package/src/components/Modal/v2/index.ts +0 -1
- package/src/components/ScrollContainer/archive-20260417.ts +0 -150
- package/src/components/v2.ts +0 -3
- package/src/lib/swipe.ts +0 -109
- package/src/lib/v2.ts +0 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
export type SwipeDirection = 'left' | 'right' | 'left-right' | 'up' | 'down' | 'up-down';
|
|
2
|
+
export type SwipeThreshold = '1/4' | '1/3' | '1/2' | number;
|
|
3
|
+
|
|
4
|
+
export interface OnSwipeHandlerArgs {
|
|
5
|
+
direction: SwipeDirection;
|
|
6
|
+
threshold: number;
|
|
7
|
+
distance: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
},
|
|
11
|
+
duration: number;
|
|
12
|
+
}
|
|
13
|
+
export type OnSwipeHandler = (args: OnSwipeHandlerArgs) => void;
|
|
14
|
+
|
|
15
|
+
export interface SwipeController {
|
|
16
|
+
enable: () => void;
|
|
17
|
+
disable: () => void;
|
|
18
|
+
dispose: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createSwipeListener(direction: SwipeDirection, threshold: SwipeThreshold, handler: OnSwipeHandler): SwipeController {
|
|
22
|
+
const swipeThreshold: number = typeof threshold == 'number' ? threshold : (() => {
|
|
23
|
+
const ratios: Record<string, number> = { '1/4': 0.25, '1/3': 0.333, '1/2': 0.5 };
|
|
24
|
+
switch (direction) {
|
|
25
|
+
case 'up':
|
|
26
|
+
case 'down':
|
|
27
|
+
case 'up-down':
|
|
28
|
+
return Math.round(window.innerHeight * (ratios[threshold] ?? ratios['1/3'])); // screen.height
|
|
29
|
+
default:
|
|
30
|
+
return Math.round(window.innerWidth * (ratios[threshold] ?? ratios['1/3'])); // screen.width
|
|
31
|
+
}
|
|
32
|
+
})();
|
|
33
|
+
|
|
34
|
+
let listening = false;
|
|
35
|
+
let x: number = NaN;
|
|
36
|
+
let y: number = NaN;
|
|
37
|
+
let start: number = NaN;
|
|
38
|
+
|
|
39
|
+
const isUp = (deltaX: number, deltaY: number) => {
|
|
40
|
+
return Math.abs(deltaY) >= swipeThreshold && Math.abs(deltaX) < swipeThreshold && deltaY > 0;
|
|
41
|
+
}
|
|
42
|
+
const isDown = (deltaX: number, deltaY: number) => {
|
|
43
|
+
return Math.abs(deltaY) >= swipeThreshold && Math.abs(deltaX) < swipeThreshold && deltaY < 0;
|
|
44
|
+
}
|
|
45
|
+
const isLeft = (deltaX: number, deltaY: number) => {
|
|
46
|
+
return Math.abs(deltaX) >= swipeThreshold && Math.abs(deltaY) < swipeThreshold && deltaX > 0;
|
|
47
|
+
}
|
|
48
|
+
const isRight = (deltaX: number, deltaY: number) => {
|
|
49
|
+
return Math.abs(deltaX) >= swipeThreshold && Math.abs(deltaY) < swipeThreshold && deltaX < 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const handleSwipe = (deltaX: number, deltaY: number, duration: number) => {
|
|
53
|
+
let swipeDirection: SwipeDirection | undefined = undefined;
|
|
54
|
+
switch (direction) {
|
|
55
|
+
case 'up':
|
|
56
|
+
if (isUp(deltaX, deltaY)) {
|
|
57
|
+
swipeDirection = 'up';
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
case 'down':
|
|
61
|
+
if (isDown(deltaX, deltaY)) {
|
|
62
|
+
swipeDirection = 'down';
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
case 'up-down':
|
|
66
|
+
if (isUp(deltaX, deltaY)) {
|
|
67
|
+
swipeDirection = 'up';
|
|
68
|
+
} else if (isDown(deltaX, deltaY)) {
|
|
69
|
+
swipeDirection = 'down';
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case 'left':
|
|
73
|
+
if (isLeft(deltaX, deltaY)) {
|
|
74
|
+
swipeDirection = 'left';
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case 'right':
|
|
78
|
+
if (isRight(deltaX, deltaY)) {
|
|
79
|
+
swipeDirection = 'right';
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case 'left-right':
|
|
83
|
+
if (isLeft(deltaX, deltaY)) {
|
|
84
|
+
swipeDirection = 'left';
|
|
85
|
+
} else if (isRight(deltaX, deltaY)) {
|
|
86
|
+
swipeDirection = 'right';
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (swipeDirection) {
|
|
91
|
+
try {
|
|
92
|
+
handler({ direction: swipeDirection, threshold: swipeThreshold, distance: { x: deltaX, y: deltaY }, duration});
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error(err);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const touchstart = (e: TouchEvent) => {
|
|
100
|
+
if (!listening) return;
|
|
101
|
+
if (e.touches.length === 1 && isNaN(x)) {
|
|
102
|
+
start = performance.now();
|
|
103
|
+
x = e.touches[0].clientX;
|
|
104
|
+
y = e.touches[0].clientY;
|
|
105
|
+
} else {
|
|
106
|
+
start = NaN;
|
|
107
|
+
x = NaN;
|
|
108
|
+
y = NaN;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const touchcancel = () => {
|
|
113
|
+
x = NaN;
|
|
114
|
+
y = NaN;
|
|
115
|
+
start = NaN;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const touchend = (e: TouchEvent) => {
|
|
119
|
+
if (!listening) return;
|
|
120
|
+
if (e.changedTouches.length === 1 && !isNaN(x) && !isNaN(y)) {
|
|
121
|
+
const deltaX = x - e.changedTouches[0].clientX;
|
|
122
|
+
const deltaY = y - e.changedTouches[0].clientY;
|
|
123
|
+
handleSwipe(deltaX, deltaY, performance.now() - start);
|
|
124
|
+
x = NaN;
|
|
125
|
+
y = NaN;
|
|
126
|
+
start = NaN;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const enable = () => {
|
|
131
|
+
if (!listening) {
|
|
132
|
+
listening = true;
|
|
133
|
+
window.addEventListener('touchstart', touchstart, { passive: true });
|
|
134
|
+
window.addEventListener('touchend', touchend, { passive: true });
|
|
135
|
+
window.addEventListener('touchcancel', touchcancel, { passive: true });
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const disable = () => {
|
|
140
|
+
listening = false;
|
|
141
|
+
window.removeEventListener('touchstart', touchstart);
|
|
142
|
+
window.removeEventListener('touchend', touchend);
|
|
143
|
+
window.removeEventListener('touchcancel', touchcancel);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
enable();
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
enable,
|
|
150
|
+
disable,
|
|
151
|
+
dispose: disable
|
|
152
|
+
};
|
|
153
|
+
}
|
package/src/lib/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * as FlipCard from '../components/FlipCard/FlipCard';
|
|
2
|
+
export * as ISI from '../components/ISI/index';
|
|
3
|
+
export * as gestures from './gestures';
|
|
2
4
|
export * as Modal from '../components/Modal/Modal';
|
|
3
5
|
export * as PdfViewer from '../components/PdfViewer/PdfViewer';
|
|
4
6
|
export * as ScrollContainer from '../components/ScrollContainer/ScrollContainer';
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
:root {
|
|
2
|
-
--evo-isi-width: 25vw;
|
|
3
|
-
--evo-isi-width-expanded: 100vw;
|
|
4
|
-
--evo-isi-height: 100vh;
|
|
5
|
-
--evo-isi-header-height: 0px;
|
|
6
|
-
--evo-isi-footer-height: 0px;
|
|
7
|
-
}
|
|
8
|
-
.evo-isi {
|
|
9
|
-
position: fixed;
|
|
10
|
-
right: 0;
|
|
11
|
-
top: 0;
|
|
12
|
-
display: block;
|
|
13
|
-
width: var(--evo-isi-width);
|
|
14
|
-
height: var(--evo-isi-height);
|
|
15
|
-
overflow: hidden;
|
|
16
|
-
z-index: 99;
|
|
17
|
-
background-color: #ffffff;
|
|
18
|
-
|
|
19
|
-
transform-origin: right top;
|
|
20
|
-
transition-property: width;
|
|
21
|
-
transition-duration: 500ms;
|
|
22
|
-
transition-timing-function: ease-in-out;
|
|
23
|
-
|
|
24
|
-
.evo-isi-wrapper {
|
|
25
|
-
.evo-isi-text {
|
|
26
|
-
.evo-scroll-container {
|
|
27
|
-
/* height: calc(var(--evo-isi-height) - var(--evo-isi-header-height) - var(--evo-isi-footer-height)) */
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import * as ScrollContainer from '../ScrollContainer/ScrollContainer';
|
|
2
|
-
|
|
3
|
-
const isi = document.querySelector('.evo-isi') as HTMLElement;
|
|
4
|
-
const isiHeader = isi.querySelector<HTMLElement>('.evo-isi-header');
|
|
5
|
-
const isiFooter = isi.querySelector<HTMLElement>('.evo-isi-footer');
|
|
6
|
-
const isiText = isi.querySelector('.evo-isi-text') as HTMLElement;
|
|
7
|
-
|
|
8
|
-
export function resize() {
|
|
9
|
-
if (isi) {
|
|
10
|
-
const isiStyle = getComputedStyle(isi);
|
|
11
|
-
const isiHeight = parseInt(isiStyle.height);
|
|
12
|
-
const headerHeight = isiHeader ? parseInt(getComputedStyle(isiHeader).height) : 0;
|
|
13
|
-
const footerHeight = isiFooter ? parseInt(getComputedStyle(isiFooter).height) : 0;
|
|
14
|
-
|
|
15
|
-
const isiTextStyle = getComputedStyle(isiText);
|
|
16
|
-
const textPadTop = parseInt(isiTextStyle.paddingTop);
|
|
17
|
-
const textPadBot = parseInt(isiTextStyle.paddingBottom);
|
|
18
|
-
|
|
19
|
-
const scrollContainer = ScrollContainer.getScrollContainer(isi.querySelector('.evo-isi-text .evo-scroll-container') as HTMLElement);
|
|
20
|
-
if (scrollContainer) {
|
|
21
|
-
const height = isiHeight - headerHeight - footerHeight - textPadTop - textPadBot;
|
|
22
|
-
ScrollContainer.resize(scrollContainer, { height });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
addEventListener('load', resize, { passive: true });
|
|
27
|
-
window.addEventListener('resize', resize, { passive: true });
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
-
import './Modal.css';
|
|
4
|
-
|
|
5
|
-
interface Props extends HTMLAttributes<'div'> {
|
|
6
|
-
id: string;
|
|
7
|
-
closeButton?: 'default' | 'minimal' | 'none' | 'false' | false;
|
|
8
|
-
closeButtonAtts?: Record<string, string>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const {
|
|
12
|
-
id,
|
|
13
|
-
closeButton = 'default',
|
|
14
|
-
closeButtonAtts,
|
|
15
|
-
...rest
|
|
16
|
-
} = Astro.props;
|
|
17
|
-
|
|
18
|
-
const attrs = {...rest};
|
|
19
|
-
const cssClass = attrs['class'] ?? '';
|
|
20
|
-
|
|
21
|
-
['class'].forEach((a) => {
|
|
22
|
-
delete attrs[a as keyof typeof attrs];
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const showCloseButton = closeButton !== false && closeButton !== 'false' && closeButton !== 'none';
|
|
26
|
-
const closeButtonCssClassList = ['evo-modal-close', 'evo-modal-action-hide'];
|
|
27
|
-
if (showCloseButton) {
|
|
28
|
-
closeButtonCssClassList.push(`evo-modal-theme-${closeButton}`)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
<evo-modal id={id} class:list={['evo-modal', cssClass]} {...attrs}>
|
|
33
|
-
{showCloseButton && <button class:list={closeButtonCssClassList} {...closeButtonAtts}><span class="evo-modal-x"><slot name="close">X</slot></span></button>}
|
|
34
|
-
<slot name="outer" />
|
|
35
|
-
<div class="evo-modal-wrapper">
|
|
36
|
-
<slot />
|
|
37
|
-
</div>
|
|
38
|
-
</evo-modal>
|
|
39
|
-
|
|
40
|
-
<script>
|
|
41
|
-
import './Modal';
|
|
42
|
-
</script>
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
@layer inizioevoke-astro-components {
|
|
3
|
-
:root {
|
|
4
|
-
--evo-modal-viewport-width: 100vw;
|
|
5
|
-
--evo-modal-viewport-height: 100vh;
|
|
6
|
-
--evo-modal-position: absolute;
|
|
7
|
-
--evo-modal-width: 800px;
|
|
8
|
-
--evo-modal-height: 450px;
|
|
9
|
-
--evo-modal-animation-duration: 300ms;
|
|
10
|
-
--evo-modal-z-index: 99;
|
|
11
|
-
--evo-modal-overlay-blur: 2px;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.evo-modal-overlay {
|
|
15
|
-
position: fixed;
|
|
16
|
-
left: 0;
|
|
17
|
-
top: 0;
|
|
18
|
-
display: none;
|
|
19
|
-
opacity: 0;
|
|
20
|
-
width: 100%;
|
|
21
|
-
height: 100%;
|
|
22
|
-
backdrop-filter: blur(var(--evo-modal-overlay-blur));
|
|
23
|
-
background-color: rgba(0,0,0,0.66);
|
|
24
|
-
z-index: calc(var(--evo-modal-z-index) - 1);
|
|
25
|
-
animation-duration: var(--evo-modal-animation-duration);
|
|
26
|
-
animation-iteration-count: 1;
|
|
27
|
-
animation-timing-function: linear;
|
|
28
|
-
animation-fill-mode: forwards;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
evo-modal {
|
|
32
|
-
position: var(--evo-modal-position);
|
|
33
|
-
left: calc((var(--evo-modal-viewport-width) - var(--evo-modal-width)) / 2);
|
|
34
|
-
top: calc((var(--evo-modal-viewport-height) - var(--evo-modal-height)) / 2);
|
|
35
|
-
width: var(--evo-modal-width);
|
|
36
|
-
height: var(--evo-modal-height);
|
|
37
|
-
display: none;
|
|
38
|
-
margin: 0;
|
|
39
|
-
padding: 0;
|
|
40
|
-
opacity: 0;
|
|
41
|
-
color: #000000;
|
|
42
|
-
background-color: #ffffff;
|
|
43
|
-
z-index: var(--evo-modal-z-index);
|
|
44
|
-
box-sizing: border-box;
|
|
45
|
-
animation-duration: var(--evo-modal-animation-duration);
|
|
46
|
-
animation-iteration-count: 1;
|
|
47
|
-
animation-timing-function: linear;
|
|
48
|
-
animation-fill-mode: forwards;
|
|
49
|
-
|
|
50
|
-
.evo-modal-close {
|
|
51
|
-
/* the button click area is larger than the visible button */
|
|
52
|
-
position: absolute;
|
|
53
|
-
right: 0px;
|
|
54
|
-
top: 0px;
|
|
55
|
-
width: 40px;
|
|
56
|
-
height: 40px;
|
|
57
|
-
background-color: transparent;
|
|
58
|
-
border: none;
|
|
59
|
-
cursor: pointer;
|
|
60
|
-
z-index: var(--evo-modal-z-index);
|
|
61
|
-
box-sizing: border-box;
|
|
62
|
-
|
|
63
|
-
&.evo-modal-theme-default {
|
|
64
|
-
.evo-modal-x {
|
|
65
|
-
position: absolute;
|
|
66
|
-
right: 8px;
|
|
67
|
-
top: 8px;
|
|
68
|
-
display: block;
|
|
69
|
-
width: 24px;
|
|
70
|
-
height: 24px;
|
|
71
|
-
background-color: #990000;
|
|
72
|
-
border: none;
|
|
73
|
-
border-radius: 50%;
|
|
74
|
-
text-align: left;
|
|
75
|
-
text-indent: -9999px;
|
|
76
|
-
transform: rotate(45deg);
|
|
77
|
-
|
|
78
|
-
&::before,
|
|
79
|
-
&::after {
|
|
80
|
-
content: "";
|
|
81
|
-
position: absolute;
|
|
82
|
-
background-color: #ffffff;
|
|
83
|
-
}
|
|
84
|
-
&::before {
|
|
85
|
-
left: 5px;
|
|
86
|
-
top: 11px;
|
|
87
|
-
width: 14px;
|
|
88
|
-
height: 2px;
|
|
89
|
-
}
|
|
90
|
-
&::after {
|
|
91
|
-
left: 11px;
|
|
92
|
-
top: 5px;
|
|
93
|
-
width: 2px;
|
|
94
|
-
height: 14px;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.evo-modal-wrapper {
|
|
101
|
-
position: relative;
|
|
102
|
-
width: 100%;
|
|
103
|
-
height: 100%;
|
|
104
|
-
padding: 1rem;
|
|
105
|
-
overflow: hidden;
|
|
106
|
-
box-sizing: border-box;
|
|
107
|
-
|
|
108
|
-
h1,h2,h3 {
|
|
109
|
-
&:first-child {
|
|
110
|
-
margin-top: 0;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.evo-modal-visible {
|
|
117
|
-
display: block;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.evo-modal-hide-none {
|
|
121
|
-
display: none;
|
|
122
|
-
opacity: 0;
|
|
123
|
-
}
|
|
124
|
-
.evo-modal-hide-fade {
|
|
125
|
-
/* display: block; */
|
|
126
|
-
opacity: 0;
|
|
127
|
-
animation-name: evo_modal_hide;
|
|
128
|
-
}
|
|
129
|
-
.evo-modal-show-none {
|
|
130
|
-
display: block;
|
|
131
|
-
opacity: 1;
|
|
132
|
-
}
|
|
133
|
-
.evo-modal-show-fade {
|
|
134
|
-
/* display: block; */
|
|
135
|
-
opacity: 1;
|
|
136
|
-
animation-name: evo_modal_show;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
@keyframes evo_modal_show {
|
|
140
|
-
0% {
|
|
141
|
-
opacity: 0;
|
|
142
|
-
}
|
|
143
|
-
100% {
|
|
144
|
-
opacity: 1;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
@keyframes evo_modal_hide {
|
|
148
|
-
0% {
|
|
149
|
-
opacity: 1;
|
|
150
|
-
}
|
|
151
|
-
100% {
|
|
152
|
-
opacity: 0;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const TAG_NAME = 'evo-modal';
|
|
3
|
-
const CSS_VISIBLE = 'evo-modal-visible';
|
|
4
|
-
const CSS_PREFIX_SHOW = 'evo-modal-show';
|
|
5
|
-
const CSS_PREFIX_HIDE = 'evo-modal-hide';
|
|
6
|
-
const CSS_OVERLAY = 'evo-modal-overlay';
|
|
7
|
-
|
|
8
|
-
export type ModalAnimation = 'none' | 'fade';
|
|
9
|
-
|
|
10
|
-
export interface ModalEventDetail {
|
|
11
|
-
modal: EvoModalElement;
|
|
12
|
-
replacedBy?: EvoModalElement;
|
|
13
|
-
}
|
|
14
|
-
export type ModalEventCallback = (e: CustomEvent<ModalEventDetail>) => void;
|
|
15
|
-
|
|
16
|
-
declare global {
|
|
17
|
-
interface ElementEventMap {
|
|
18
|
-
'evomodalvisible': CustomEvent<ModalEventDetail>,
|
|
19
|
-
'evomodalhidden': CustomEvent<ModalEventDetail>
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
type ModalEventType = 'evomodalvisible' | 'evomodalhidden';
|
|
23
|
-
|
|
24
|
-
export class EvoModalElement extends HTMLElement {
|
|
25
|
-
#animation: ModalAnimation = 'fade';
|
|
26
|
-
|
|
27
|
-
constructor() {
|
|
28
|
-
super();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
connectedCallback() {
|
|
32
|
-
this.querySelectorAll<HTMLElement>('.evo-modal-action-hide').forEach((trigger) => {
|
|
33
|
-
trigger.addEventListener('click', () => {
|
|
34
|
-
this.hideModal();
|
|
35
|
-
}, { passive: true });
|
|
36
|
-
})
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
showModal({ animation = 'fade' }: { animation?: ModalAnimation } = {}): Promise<void> {
|
|
40
|
-
return new Promise<void>((resolve) => {
|
|
41
|
-
if (!this.classList.contains(CSS_VISIBLE)) {
|
|
42
|
-
[...document.querySelectorAll<EvoModalElement>(`${TAG_NAME}.${CSS_VISIBLE}`)]
|
|
43
|
-
.forEach((modal) => {
|
|
44
|
-
modal.#hideModal({ overlay: false, replacedBy: this });
|
|
45
|
-
});
|
|
46
|
-
this.#animation = animation;
|
|
47
|
-
this.classList.add(CSS_VISIBLE, `${CSS_PREFIX_SHOW}-${animation}`);
|
|
48
|
-
showOverlay(this.#animation);
|
|
49
|
-
|
|
50
|
-
setTimeout(() => {
|
|
51
|
-
this.dispatchEvent(createModalEvent('evomodalvisible', this));
|
|
52
|
-
resolve();
|
|
53
|
-
}, animation !== 'none' ? getAnimationDuration(this) : 0)
|
|
54
|
-
} else {
|
|
55
|
-
resolve();
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
hideModal({ animation }: { animation?: ModalAnimation } = {}): Promise<void> {
|
|
61
|
-
return this.#hideModal({ animation });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
#hideModal({ animation, overlay = true, replacedBy }: { animation?: ModalAnimation, overlay?: boolean, replacedBy?: EvoModalElement } = {}): Promise<void> {
|
|
65
|
-
return new Promise<void>((resolve) => {
|
|
66
|
-
if (this.classList.contains(CSS_VISIBLE)) {
|
|
67
|
-
if (!animation) {
|
|
68
|
-
animation = this.#animation;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const duration = getAnimationDuration(this);
|
|
72
|
-
|
|
73
|
-
this.classList.add(`${CSS_PREFIX_HIDE}-${this.#animation}`);
|
|
74
|
-
this.classList.remove(`${CSS_PREFIX_SHOW}-${this.#animation}`);
|
|
75
|
-
if (overlay) {
|
|
76
|
-
hideOverlay({ modal: this, animation: this.#animation, duration });
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
setTimeout(() => {
|
|
80
|
-
this.classList.remove('evo-modal-visible', `${CSS_PREFIX_HIDE}-${this.#animation}`);
|
|
81
|
-
this.dispatchEvent(createModalEvent('evomodalhidden', this, { replacedBy }));
|
|
82
|
-
resolve();
|
|
83
|
-
}, animation !== 'none' ? duration : 0);
|
|
84
|
-
} else {
|
|
85
|
-
resolve();
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (!customElements.get(TAG_NAME)) {
|
|
91
|
-
customElements.define(TAG_NAME, EvoModalElement);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function createModalEvent(type: ModalEventType, modal: EvoModalElement, { replacedBy }: { replacedBy?: EvoModalElement } = {}) {
|
|
95
|
-
return new CustomEvent(type, {
|
|
96
|
-
detail: { modal, replacedBy },
|
|
97
|
-
bubbles: true,
|
|
98
|
-
composed: true
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
interface ShowModalOpts {
|
|
103
|
-
animation?: ModalAnimation;
|
|
104
|
-
onShow?: ModalEventCallback;
|
|
105
|
-
onHide?: ModalEventCallback;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export async function showModal(selector: string | HTMLElement, { animation, onShow, onHide }: ShowModalOpts = {}): Promise<EvoModalElement | undefined> {
|
|
109
|
-
if (typeof selector == 'string' && selector !== TAG_NAME && !/[#\.]/.test(selector)) {
|
|
110
|
-
selector = `#${selector}`; // assume this is an id
|
|
111
|
-
}
|
|
112
|
-
const modal = typeof selector == 'string' ? document.querySelector<EvoModalElement>(selector) : selector;
|
|
113
|
-
if (modal && modal instanceof EvoModalElement) {
|
|
114
|
-
if (typeof onShow == 'function') {
|
|
115
|
-
modal.addEventListener('evomodalvisible', onShow, { once: true });
|
|
116
|
-
}
|
|
117
|
-
if (typeof onHide == 'function') {
|
|
118
|
-
modal.addEventListener('evomodalhidden', onHide, { once: true });
|
|
119
|
-
}
|
|
120
|
-
await modal.showModal({ animation });
|
|
121
|
-
return modal;
|
|
122
|
-
} else {
|
|
123
|
-
return undefined;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export async function hideModal(selector: string | HTMLElement, animation?: ModalAnimation) {
|
|
128
|
-
const modal = typeof selector == 'string' ? document.querySelector<EvoModalElement>(selector) : selector;
|
|
129
|
-
if (modal && modal instanceof EvoModalElement) {
|
|
130
|
-
await modal.hideModal({ animation });
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export async function hideModals(animation?: ModalAnimation) {
|
|
135
|
-
const modals = [...document.querySelectorAll<EvoModalElement>(`${TAG_NAME}.${CSS_VISIBLE}`)];
|
|
136
|
-
await Promise.all(modals.map((m) => { return m.hideModal({ animation })}));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function showOverlay(animation: ModalAnimation = 'fade') {
|
|
140
|
-
const overlay = getOverlay();
|
|
141
|
-
if (overlay && !overlay.classList.contains(CSS_VISIBLE)) {
|
|
142
|
-
overlay.classList.add(CSS_VISIBLE);
|
|
143
|
-
requestAnimationFrame(() => {
|
|
144
|
-
overlay.classList.add(`${CSS_PREFIX_SHOW}-${animation}`);
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
export function hideOverlay({ modal, animation = 'fade', duration }: { modal?: EvoModalElement, animation?: ModalAnimation, duration?: number } = {}) {
|
|
149
|
-
const overlay = getOverlay();
|
|
150
|
-
if (overlay && overlay.classList.contains(CSS_VISIBLE)) {
|
|
151
|
-
const cssShow = [...overlay.classList]
|
|
152
|
-
.filter((c) => {
|
|
153
|
-
return c.startsWith(CSS_PREFIX_SHOW);
|
|
154
|
-
});
|
|
155
|
-
const cssHide = [...overlay.classList]
|
|
156
|
-
.filter((c) => {
|
|
157
|
-
return c.startsWith(CSS_PREFIX_HIDE);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
if (cssShow.length !== 0 && cssHide.length === 0) {
|
|
161
|
-
cssHide.push(`${CSS_PREFIX_HIDE}-${animation}`)
|
|
162
|
-
overlay.classList.add(cssHide[0]);
|
|
163
|
-
overlay.classList.remove(cssShow[0]);
|
|
164
|
-
setTimeout(() => {
|
|
165
|
-
overlay.classList.remove(CSS_VISIBLE, cssHide[0]);
|
|
166
|
-
}, duration ?? getAnimationDuration(modal));
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Call this function when dynamically adding a trigger to the DOM
|
|
174
|
-
*/
|
|
175
|
-
export function bindTriggers(container = document) {
|
|
176
|
-
container.querySelectorAll<HTMLElement>('[data-evo-modal-show]').forEach((trigger) => {
|
|
177
|
-
if (!trigger.hasAttribute('data-evo-modal-bound')) {
|
|
178
|
-
trigger.setAttribute('data-evo-modal-bound','true');
|
|
179
|
-
trigger.addEventListener('click', async (e) => {
|
|
180
|
-
e.preventDefault();
|
|
181
|
-
const target = (e.currentTarget ?? e.target) as HTMLElement;
|
|
182
|
-
const animation = (target.getAttribute('data-evo-modal-animation') ?? undefined) as ModalAnimation | undefined;
|
|
183
|
-
await showModal(target.getAttribute('data-evo-modal-show') as string, { animation });
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function getOverlay() {
|
|
190
|
-
return document.querySelector<HTMLElement>(`.${CSS_OVERLAY}`);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function getAnimationDuration(element: HTMLElement = document.body) {
|
|
194
|
-
let duration = 300;
|
|
195
|
-
const cssDuration = getComputedStyle(element).getPropertyValue('--evo-modal-animation-duration').trim();
|
|
196
|
-
if (cssDuration) {
|
|
197
|
-
// Match ms or s suffixes
|
|
198
|
-
const match = cssDuration.match(/^(\d+(?:\.\d+)?)(m?s)$/);
|
|
199
|
-
if (match) {
|
|
200
|
-
const value = parseFloat(match[1]);
|
|
201
|
-
const unit = match[2];
|
|
202
|
-
duration = unit === 'ms' ? value : value * 1000;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return duration;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
document.addEventListener('DOMContentLoaded', (e) => {
|
|
209
|
-
bindTriggers();
|
|
210
|
-
|
|
211
|
-
if (!getOverlay()) {
|
|
212
|
-
const overlay = document.createElement('div');
|
|
213
|
-
overlay.classList.add(CSS_OVERLAY);
|
|
214
|
-
overlay.addEventListener('click', () => {
|
|
215
|
-
hideModals();
|
|
216
|
-
}, { passive: true });
|
|
217
|
-
document.body.append(overlay);
|
|
218
|
-
}
|
|
219
|
-
}, { passive: true });
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
|
|
4
|
-
interface Props extends HTMLAttributes<'div'> {
|
|
5
|
-
class?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const {
|
|
9
|
-
class: cssClass,
|
|
10
|
-
...rest
|
|
11
|
-
} = Astro.props;
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
<div class:list={['evo-modal-overlay', cssClass]} {...rest}><slot /></div>
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
import type { ModalAnimation } from './Modal';
|
|
4
|
-
|
|
5
|
-
interface Props extends HTMLAttributes<'button'> {
|
|
6
|
-
modal: string;
|
|
7
|
-
animation?: ModalAnimation;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const {
|
|
11
|
-
modal,
|
|
12
|
-
animation,
|
|
13
|
-
...rest
|
|
14
|
-
} = Astro.props;
|
|
15
|
-
|
|
16
|
-
interface ButtonAttrs extends HTMLAttributes<'button'> {
|
|
17
|
-
'data-evo-modal-show': string;
|
|
18
|
-
'data-evo-modal-animation'?: ModalAnimation
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const attrs: ButtonAttrs = {...rest, 'data-evo-modal-show': modal};
|
|
22
|
-
if (animation) {
|
|
23
|
-
attrs['data-evo-modal-animation'] = animation;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const cssClass = attrs['class'];
|
|
27
|
-
delete attrs['class'];
|
|
28
|
-
---
|
|
29
|
-
<button class:list={['evo-modal-trigger', cssClass]} {...attrs}><slot /></button>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './Modal';
|