@inizioevoke/astro-core 1.5.2 → 1.6.1
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 +6 -3
- package/src/components/FlipCard/FlipCard.css +37 -35
- package/src/components/FlipCard/FlipCard.ts +17 -1
- package/src/components/Modal/Modal.ts +16 -6
- package/src/components/Modal/v2/Modal.astro +42 -0
- package/src/components/Modal/v2/Modal.css +155 -0
- package/src/components/Modal/v2/Modal.ts +219 -0
- package/src/components/Modal/v2/ModalOverlay.astro +14 -0
- package/src/components/Modal/v2/ModalTrigger.astro +29 -0
- package/src/components/Modal/v2/index.ts +1 -0
- package/src/components/PdfViewer/PdfViewer.astro +4 -1
- package/src/components/PdfViewer/PdfViewer.css +17 -2
- package/src/components/PdfViewer/PdfViewer.ts +227 -9
- package/src/components/PdfViewer/pdf.min.mjs +4 -4
- package/src/components/PdfViewer/pdf.worker.min.mjs +7 -4
- package/src/components/ScrollContainer/ScrollContainer.css +2 -1
- package/src/components/ScrollContainer/ScrollContainer.ts +32 -97
- package/src/components/v2.ts +3 -0
- package/src/lib/swipe.ts +109 -0
- package/src/lib/v2.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inizioevoke/astro-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
"./lib": "./src/lib/index.ts",
|
|
13
13
|
"./lib/FlipCard": "./src/components/FlipCard/index.ts",
|
|
14
14
|
"./lib/Modal": "./src/components/Modal/index.ts",
|
|
15
|
-
"./lib/ScrollContainer": "./src/components/ScrollContainer/index.ts"
|
|
15
|
+
"./lib/ScrollContainer": "./src/components/ScrollContainer/index.ts",
|
|
16
|
+
"./v2": "./src/components/v2.ts",
|
|
17
|
+
"./v2/lib": "./src/lib/v2.ts",
|
|
18
|
+
"./v2/lib/Modal": "./src/components/Modal/v2/index.ts"
|
|
16
19
|
},
|
|
17
20
|
"files": [
|
|
18
21
|
"src",
|
|
@@ -27,7 +30,7 @@
|
|
|
27
30
|
"@astrojs/ts-plugin": "^1.10.7",
|
|
28
31
|
"@types/node": "^25.5.0",
|
|
29
32
|
"astro": "^6.0.5",
|
|
30
|
-
"pdfjs-dist": "^5.
|
|
33
|
+
"pdfjs-dist": "^5.7.284",
|
|
31
34
|
"typescript": "^5.9.3"
|
|
32
35
|
}
|
|
33
36
|
}
|
|
@@ -1,50 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
@layer inizioevoke-astro-components {
|
|
2
|
+
evo-flip-card {
|
|
3
|
+
--evo-flip-card-width: 640px;
|
|
4
|
+
--evo-flip-card-height: 360px;
|
|
5
|
+
--evo-flip-card-duration: 0.6s;
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
height: var(--evo-flip-card-height);
|
|
9
|
-
perspective: 1000px;
|
|
10
|
-
|
|
11
|
-
.evo-flip-card-wrapper {
|
|
12
|
-
position: relative;
|
|
13
|
-
width: 100%;
|
|
7
|
+
display: block;
|
|
8
|
+
width: var(--evo-flip-card-width);
|
|
14
9
|
height: var(--evo-flip-card-height);
|
|
15
|
-
|
|
16
|
-
transform-style: preserve-3d;
|
|
17
|
-
transform-origin: center center;
|
|
10
|
+
perspective: 1000px;
|
|
18
11
|
|
|
19
|
-
.evo-flip-card-
|
|
20
|
-
|
|
21
|
-
position: absolute;
|
|
22
|
-
left: 0;
|
|
23
|
-
top: 0;
|
|
12
|
+
.evo-flip-card-wrapper {
|
|
13
|
+
position: relative;
|
|
24
14
|
width: 100%;
|
|
25
15
|
height: var(--evo-flip-card-height);
|
|
26
|
-
-
|
|
27
|
-
backface-visibility: hidden;
|
|
16
|
+
transition: transform var(--evo-flip-card-duration);
|
|
28
17
|
transform-style: preserve-3d;
|
|
29
|
-
transform: rotateY(0deg);
|
|
30
18
|
transform-origin: center center;
|
|
31
19
|
|
|
32
|
-
.evo-flip-card-
|
|
20
|
+
.evo-flip-card-front,
|
|
21
|
+
.evo-flip-card-back {
|
|
33
22
|
position: absolute;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
left: 0;
|
|
24
|
+
top: 0;
|
|
25
|
+
width: 100%;
|
|
26
|
+
height: var(--evo-flip-card-height);
|
|
27
|
+
-webkit-backface-visibility: hidden;
|
|
28
|
+
backface-visibility: hidden;
|
|
29
|
+
transform-style: preserve-3d;
|
|
30
|
+
transform: rotateY(0deg);
|
|
31
|
+
transform-origin: center center;
|
|
32
|
+
|
|
33
|
+
.evo-flip-card-trigger {
|
|
34
|
+
position: absolute;
|
|
35
|
+
top: 8px;
|
|
36
|
+
right: 8px;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
z-index: 99;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
.evo-flip-card-back {
|
|
42
|
+
transform: rotateY(180deg);
|
|
38
43
|
}
|
|
39
44
|
}
|
|
40
|
-
.evo-flip-card-back {
|
|
41
|
-
transform: rotateY(180deg);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
&[flipped="true"] {
|
|
47
|
+
.evo-flip-card-wrapper {
|
|
48
|
+
transform: rotateY(180deg);
|
|
49
|
+
}
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -7,17 +7,33 @@ export class EvoFlipCardElement extends HTMLElement {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
#flipped: boolean = false;
|
|
10
|
+
#boundHandleTriggerClick: (e: MouseEvent) => void;
|
|
10
11
|
|
|
11
12
|
constructor() {
|
|
12
13
|
super();
|
|
14
|
+
this.#boundHandleTriggerClick = this.#handleTriggerClick.bind(this);
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
connectedCallback() {
|
|
16
18
|
[...this.querySelectorAll<HTMLButtonElement>('button.evo-flip-card-trigger')].forEach((btn) => {
|
|
17
|
-
btn.addEventListener('click', this.#
|
|
19
|
+
btn.addEventListener('click', this.#boundHandleTriggerClick);
|
|
18
20
|
});
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
disconnectedCallback() {
|
|
24
|
+
[...this.querySelectorAll<HTMLButtonElement>('button.evo-flip-card-trigger')].forEach((btn) => {
|
|
25
|
+
btn.removeEventListener('click', this.#boundHandleTriggerClick);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
|
|
30
|
+
if (oldValue === newValue) return;
|
|
31
|
+
|
|
32
|
+
if (name === 'flipped') {
|
|
33
|
+
this.#flipped = newValue?.toLowerCase() === 'true';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
21
37
|
get flipped() {
|
|
22
38
|
return this.#flipped;
|
|
23
39
|
}
|
|
@@ -43,7 +43,7 @@ export function show(selector: string | HTMLElement, { animation = 'fade', on }:
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
resolve();
|
|
46
|
-
}, animation !== 'none' ?
|
|
46
|
+
}, animation !== 'none' ? getAnimationDuration(modal) : 0);
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
}
|
|
@@ -105,7 +105,7 @@ function __hide(modal: HTMLElement, animation: ModalAnimation) {
|
|
|
105
105
|
modal.classList.remove('evo-modal-visible', `evo-modal-hide-${animation}`);
|
|
106
106
|
triggerEventCallbacks(modal, 'hide');
|
|
107
107
|
resolve();
|
|
108
|
-
}, animation !== 'none' ?
|
|
108
|
+
}, animation !== 'none' ? getAnimationDuration(modal) : 0);
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
export function onHide(selector: string | HTMLElement, callback: ModalEventCallback, { once }: ModalEventCallbackArgs = {}) {
|
|
@@ -173,10 +173,20 @@ overlay.addEventListener('click', () => {
|
|
|
173
173
|
triggerEventCallbacks(overlay, 'overlay');
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
function getAnimationDuration(element: HTMLElement = document.body) {
|
|
177
|
+
let duration = 0;
|
|
178
|
+
const cssDuration = getComputedStyle(element).getPropertyValue('--evo-modal-animation-duration').trim();
|
|
179
|
+
if (cssDuration) {
|
|
180
|
+
// Match ms or s suffixes
|
|
181
|
+
const match = cssDuration.match(/^(\d+(?:\.\d+)?)(m?s)$/);
|
|
182
|
+
if (match) {
|
|
183
|
+
const value = parseFloat(match[1]);
|
|
184
|
+
const unit = match[2];
|
|
185
|
+
duration = unit === 'ms' ? value : value * 1000;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return duration;
|
|
189
|
+
};
|
|
180
190
|
|
|
181
191
|
document.querySelectorAll<HTMLElement>('.evo-modal').forEach((modal) => {
|
|
182
192
|
modal.querySelectorAll<HTMLElement>('.evo-modal-action-hide').forEach((btn) => {
|
|
@@ -0,0 +1,42 @@
|
|
|
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>
|
|
@@ -0,0 +1,155 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
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 });
|
|
@@ -0,0 +1,14 @@
|
|
|
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>
|
|
@@ -0,0 +1,29 @@
|
|
|
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>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Modal';
|
|
@@ -6,8 +6,9 @@ interface Props {
|
|
|
6
6
|
id: string;
|
|
7
7
|
class?: string;
|
|
8
8
|
height?: number | string;
|
|
9
|
-
theme?: '
|
|
9
|
+
theme?: 'light' | 'dark' | 'black';
|
|
10
10
|
pdf: string;
|
|
11
|
+
zoom?: 50 | 60 | 70 | 80 | 90 | 100 | 110 | 120 | 130 | 140 | 150;
|
|
11
12
|
page?: number;
|
|
12
13
|
pages?: string;
|
|
13
14
|
bookmarks?: Record<string, number>;
|
|
@@ -23,6 +24,7 @@ const {
|
|
|
23
24
|
height,
|
|
24
25
|
theme,
|
|
25
26
|
pdf,
|
|
27
|
+
zoom,
|
|
26
28
|
page = 1,
|
|
27
29
|
bookmarks,
|
|
28
30
|
bookmarkText = 'Jump to',
|
|
@@ -40,6 +42,7 @@ if (height) {
|
|
|
40
42
|
class:list={['evo-pdf-viewer', cssClass, theme ? `evo-pdf-viewer-theme-${theme}` : null]}
|
|
41
43
|
data-pdf={pdf}
|
|
42
44
|
data-page={page}
|
|
45
|
+
data-zoom={zoom}
|
|
43
46
|
{...attrs}
|
|
44
47
|
>
|
|
45
48
|
<div class="evo-pdf-viewer-toolbar">
|