@inizioevoke/astro-core 1.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 +31 -0
- package/src/components/Modal/Modal.astro +36 -0
- package/src/components/Modal/Modal.css +145 -0
- package/src/components/Modal/Modal.ts +109 -0
- package/src/components/Modal/ModalOverlay.astro +1 -0
- package/src/components/Modal/ModalTrigger.astro +29 -0
- package/src/components/PdfViewer/PdfViewer.astro +103 -0
- package/src/components/PdfViewer/PdfViewer.css +198 -0
- package/src/components/PdfViewer/PdfViewer.ts +183 -0
- package/src/components/PdfViewer/pdf.min.mjs +28 -0
- package/src/components/PdfViewer/pdf.mjs +26465 -0
- package/src/components/PdfViewer/pdf.mjs.map +1 -0
- package/src/components/PdfViewer/pdf.worker.min.mjs +28 -0
- package/src/components/PdfViewer/pdf.worker.mjs +63057 -0
- package/src/components/PdfViewer/pdf.worker.mjs.map +1 -0
- package/src/components/ScrollContainer/ScrollContainer.astro +55 -0
- package/src/components/ScrollContainer/ScrollContainer.css +64 -0
- package/src/components/ScrollContainer/ScrollContainer.ts +143 -0
- package/src/components/Tabs/TabItem.astro +24 -0
- package/src/components/Tabs/Tabs.astro +94 -0
- package/src/components/Tabs/Tabs.css +70 -0
- package/src/components/Tabs/Tabs.ts +22 -0
- package/src/components/Zoom/Zoom.astro +64 -0
- package/src/components/Zoom/ZoomContent.astro +0 -0
- package/src/components/Zoom/ZoomModal.astro +0 -0
- package/src/components/index.ts +8 -0
- package/src/integrations/index.ts +2 -0
- package/src/integrations/prune-build.ts +71 -0
- package/src/integrations/relative-links.ts +178 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import './ScrollContainer.css';
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
6
|
+
outerWidth?: string | number;
|
|
7
|
+
innerWidth?: string | number;
|
|
8
|
+
height?: string | number;
|
|
9
|
+
vertical?: boolean;
|
|
10
|
+
horizontal?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
outerWidth,
|
|
15
|
+
innerWidth,
|
|
16
|
+
height,
|
|
17
|
+
vertical = true,
|
|
18
|
+
horizontal = false,
|
|
19
|
+
...rest
|
|
20
|
+
} = Astro.props;
|
|
21
|
+
|
|
22
|
+
const containerAttrs = {...rest};
|
|
23
|
+
const cssClass = containerAttrs['class'] ?? '';
|
|
24
|
+
['class'].forEach((a) => {
|
|
25
|
+
delete containerAttrs[a as keyof typeof containerAttrs];
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
containerAttrs.style = typeof containerAttrs.style == 'string' ? `${containerAttrs.style}${containerAttrs.style.endsWith(';') ? '' : ';'}` : '';
|
|
29
|
+
if (outerWidth) {
|
|
30
|
+
containerAttrs.style = `${containerAttrs.style}width:${typeof outerWidth == 'number' || /^\d+$/.test(outerWidth) ? `${outerWidth}px` : outerWidth};`;
|
|
31
|
+
}
|
|
32
|
+
if (height) {
|
|
33
|
+
containerAttrs.style = `${containerAttrs.style}height:${typeof height == 'number' || /^\d+$/.test(height) ? `${height}px` : height};`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const contentAttrs: Record<string, string> = {};
|
|
37
|
+
if (innerWidth) {
|
|
38
|
+
contentAttrs.style = `width:${typeof innerWidth == 'number' || /^\d+$/.test(innerWidth) ? `${innerWidth}px` : innerWidth};`
|
|
39
|
+
}
|
|
40
|
+
---
|
|
41
|
+
<div class:list={['evo-scroll-container', cssClass]} {...containerAttrs}>
|
|
42
|
+
<div class="evo-scroll-content" {...contentAttrs}>
|
|
43
|
+
<slot />
|
|
44
|
+
</div>
|
|
45
|
+
{vertical && <div class="evo-scroll-track evo-scroll-track-y">
|
|
46
|
+
<div class="evo-scroll-thumb"></div>
|
|
47
|
+
</div>}
|
|
48
|
+
{horizontal && <div class="evo-scroll-track evo-scroll-track-x">
|
|
49
|
+
<div class="evo-scroll-thumb"></div>
|
|
50
|
+
</div>}
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<script>
|
|
54
|
+
import './ScrollContainer';
|
|
55
|
+
</script>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
@layer inizioevoke-astro-components {
|
|
2
|
+
:root {
|
|
3
|
+
--evo-scroll-container-border-radius: 4px;
|
|
4
|
+
--evo-scroll-container-track-width: 8px;
|
|
5
|
+
--evo-scroll-container-track-top: 0px;
|
|
6
|
+
--evo-scroll-container-track-bottom: 0px;
|
|
7
|
+
--evo-scroll-container-track-color: #dddddd;
|
|
8
|
+
--evo-scroll-container-thumb-color: #666666;
|
|
9
|
+
--evo-scroll-container-thumb-border-width: 0px;
|
|
10
|
+
--evo-scroll-container-thumb-border-color: var(--evo-scroll-container-track-color);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.evo-scroll-container {
|
|
14
|
+
position: relative;
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 100%;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
|
|
20
|
+
* {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.evo-scroll-content {
|
|
25
|
+
height: 100%;
|
|
26
|
+
overflow-y: scroll;
|
|
27
|
+
padding-right: calc(1rem + var(--evo-scroll-container-track-width));
|
|
28
|
+
-ms-overflow-style: none;
|
|
29
|
+
scrollbar-width: none;
|
|
30
|
+
&::-webkit-scrollbar {
|
|
31
|
+
display: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
*:first-child {
|
|
35
|
+
margin-top: 0 !important;
|
|
36
|
+
}
|
|
37
|
+
*:last-child {
|
|
38
|
+
margin-bottom: 0 !important;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.evo-scroll-track {
|
|
43
|
+
position: absolute;
|
|
44
|
+
top: var(--evo-scroll-container-track-top);
|
|
45
|
+
right: 0px;
|
|
46
|
+
width: var(--evo-scroll-container-track-width);
|
|
47
|
+
height: calc(100% - var(--evo-scroll-container-track-top) - var(--evo-scroll-container-track-bottom));
|
|
48
|
+
background: var(--evo-scroll-container-track-color);
|
|
49
|
+
border-radius: var(--evo-scroll-container-border-radius);
|
|
50
|
+
|
|
51
|
+
.evo-scroll-thumb {
|
|
52
|
+
box-sizing: border-box;
|
|
53
|
+
position: absolute;
|
|
54
|
+
width: 100%;
|
|
55
|
+
background-color: var(--evo-scroll-container-thumb-color);
|
|
56
|
+
border: var(--evo-scroll-container-thumb-border-width) solid var(--evo-scroll-container-thumb-border-color);
|
|
57
|
+
border-radius: var(--evo-scroll-container-border-radius);
|
|
58
|
+
height: 50px;
|
|
59
|
+
cursor: grab;
|
|
60
|
+
font-size: 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
|
|
2
|
+
export interface IScrollContainer {
|
|
3
|
+
container: HTMLElement;
|
|
4
|
+
content: HTMLElement;
|
|
5
|
+
track: HTMLElement;
|
|
6
|
+
thumb: HTMLElement;
|
|
7
|
+
isDragging: boolean;
|
|
8
|
+
startY: number;
|
|
9
|
+
startScrollTop: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function refresh(scrollContainer?: IScrollContainer) {
|
|
13
|
+
if (scrollContainer) {
|
|
14
|
+
updateThumbSize(scrollContainer);
|
|
15
|
+
} else {
|
|
16
|
+
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
17
|
+
updateThumbSize(scrollContainer);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getScroll(scrollContainer: IScrollContainer): { left: number, top: number } {
|
|
23
|
+
return {
|
|
24
|
+
left: scrollContainer.content.scrollLeft,
|
|
25
|
+
top: scrollContainer.content.scrollTop
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function scroll(scrollContainer: IScrollContainer, { left, top, behavior = 'auto' }: { left?: number, top?: number, behavior?: 'auto' | 'instant' | 'smooth' } = {}) {
|
|
29
|
+
const current = getScroll(scrollContainer);
|
|
30
|
+
scrollContainer.content.scroll({ left: left ?? current.left, top: top ?? current.top, behavior });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getScrollContainer(selector: string | HTMLElement): IScrollContainer | undefined {
|
|
34
|
+
const key = typeof selector == 'string' ? document.querySelector<HTMLElement>(selector) : selector;
|
|
35
|
+
return key ? scrollContainers.get(key) : undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function thumbMouseDown(scrollContainer: IScrollContainer, e: MouseEvent) {
|
|
39
|
+
scrollContainer.isDragging = true;
|
|
40
|
+
scrollContainer.startY = e.clientY;
|
|
41
|
+
scrollContainer.startScrollTop = scrollContainer.content.scrollTop;
|
|
42
|
+
scrollContainer.thumb.style.cursor = 'grabbing';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function updateThumbSize(scrollContainer: IScrollContainer) {
|
|
46
|
+
const { container, content, thumb, track } = scrollContainer;
|
|
47
|
+
const scrollHeight = content.scrollHeight - getPaddingY(content);
|
|
48
|
+
const containerHeight = container.clientHeight - getPaddingY(container);
|
|
49
|
+
const visibleRatio = containerHeight / scrollHeight;
|
|
50
|
+
const thumbHeight = visibleRatio * containerHeight;
|
|
51
|
+
thumb.style.height = `${thumbHeight > 20 ? thumbHeight : 20}px`;
|
|
52
|
+
track.style.display = visibleRatio < 1 ? 'block' : 'none';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function scrollContent(scrollContainer: IScrollContainer) {
|
|
56
|
+
const { container, content, thumb, track } = scrollContainer;
|
|
57
|
+
const scrollHeight = content.scrollHeight - getPaddingY(content);
|
|
58
|
+
const containerHeight = container.clientHeight - getPaddingY(container);
|
|
59
|
+
const scrollRatio = content.scrollTop / (scrollHeight - containerHeight);
|
|
60
|
+
const top = scrollRatio * (track.clientHeight - thumb.clientHeight);
|
|
61
|
+
thumb.style.top = `${top}px`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getPaddingY(content: HTMLElement): number {
|
|
65
|
+
const style = getComputedStyle(content);
|
|
66
|
+
return parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getScrollContainerElement(element: HTMLElement) {
|
|
70
|
+
let container = element;
|
|
71
|
+
while (container && !container.classList.contains('scroll-container')) {
|
|
72
|
+
container = container.parentElement as HTMLElement;
|
|
73
|
+
}
|
|
74
|
+
return container;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function createScrollContainer(container: HTMLElement): IScrollContainer {
|
|
78
|
+
return {
|
|
79
|
+
container: container,
|
|
80
|
+
content: container.querySelector('.evo-scroll-content') as HTMLElement,
|
|
81
|
+
track: container.querySelector('.evo-scroll-track') as HTMLElement,
|
|
82
|
+
thumb: container.querySelector('.evo-scroll-thumb') as HTMLElement,
|
|
83
|
+
isDragging: false,
|
|
84
|
+
startY: 0,
|
|
85
|
+
startScrollTop: 0
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const scrollContainers = new Map<HTMLElement, IScrollContainer>();
|
|
90
|
+
|
|
91
|
+
const intersectionObserver = new IntersectionObserver((entries) => {
|
|
92
|
+
entries.forEach((entry) => {
|
|
93
|
+
if (entry.isIntersecting) {
|
|
94
|
+
updateThumbSize(scrollContainers.get(entry.target as HTMLElement) as IScrollContainer);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
{ threshold: 0.1 }
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const mutationObserver = new MutationObserver((mutations) => {
|
|
102
|
+
const containers: IScrollContainer[] = [];
|
|
103
|
+
mutations.forEach((mutation) => {
|
|
104
|
+
const sc = scrollContainers.get(getScrollContainerElement(mutation.target as HTMLElement));
|
|
105
|
+
if (sc && !containers.includes(sc)) {
|
|
106
|
+
containers.push(sc);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
containers.forEach((container) => {
|
|
110
|
+
updateThumbSize(container);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
document.querySelectorAll<HTMLElement>('.evo-scroll-container').forEach((container) => {
|
|
115
|
+
const scrollContainer = createScrollContainer(container);
|
|
116
|
+
scrollContainers.set(container, scrollContainer);
|
|
117
|
+
scrollContainer.thumb.addEventListener('mousedown', (e) => { thumbMouseDown(scrollContainer, e); }, { passive: true });
|
|
118
|
+
scrollContainer.content.addEventListener('scroll', (e) => { scrollContent(scrollContainer); }, { passive: true });
|
|
119
|
+
intersectionObserver.observe(container);
|
|
120
|
+
mutationObserver.observe(container, { childList: true, subtree: true });
|
|
121
|
+
});
|
|
122
|
+
document.addEventListener('mousemove', (e) => {
|
|
123
|
+
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
124
|
+
if (!scrollContainer.isDragging) return;
|
|
125
|
+
const { container, content, thumb } = scrollContainer;
|
|
126
|
+
const deltaY = e.clientY - scrollContainer.startY;
|
|
127
|
+
const scrollRatio =
|
|
128
|
+
(content.scrollHeight - container.clientHeight) /
|
|
129
|
+
(container.clientHeight - thumb.clientHeight);
|
|
130
|
+
scrollContainer.content.scrollTop = scrollContainer.startScrollTop + deltaY * scrollRatio;
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
document.addEventListener('mouseup', () => {
|
|
134
|
+
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
135
|
+
scrollContainer.isDragging = false;
|
|
136
|
+
scrollContainer.thumb.style.cursor = 'grab';
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
window.addEventListener('resize', () => {
|
|
140
|
+
[...scrollContainers.values()].forEach((scrollContainer) => {
|
|
141
|
+
updateThumbSize(scrollContainer);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import { undefined } from 'astro:schema';
|
|
4
|
+
interface Props {
|
|
5
|
+
active?: true;
|
|
6
|
+
tabAttrs?: HTMLAttributes<'div'>
|
|
7
|
+
panelAttrs?: HTMLAttributes<'div'>;
|
|
8
|
+
}
|
|
9
|
+
const {
|
|
10
|
+
active,
|
|
11
|
+
tabAttrs,
|
|
12
|
+
panelAttrs
|
|
13
|
+
} = Astro.props;
|
|
14
|
+
const tabTitle = await Astro.slots.render('title');
|
|
15
|
+
const attrs = { ...tabAttrs, title: tabTitle };
|
|
16
|
+
console.log(attrs);
|
|
17
|
+
---
|
|
18
|
+
<div
|
|
19
|
+
data-tab-attrs={attrs ? JSON.stringify(attrs) : undefined}
|
|
20
|
+
data-tab-active={active ?? false}
|
|
21
|
+
{...panelAttrs}
|
|
22
|
+
role="tabpanel"
|
|
23
|
+
hidden="hidden"
|
|
24
|
+
><slot /></div>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import './Tabs.css';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
listType?: 'ul' | 'none'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { listType = 'ul' } = Astro.props;
|
|
10
|
+
|
|
11
|
+
const TabList = listType === 'none' ? 'div' : 'ul';
|
|
12
|
+
const tabListAttrs: HTMLAttributes<'div'> = {
|
|
13
|
+
role: 'tablist'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const html = await Astro.slots.render('default');
|
|
17
|
+
|
|
18
|
+
interface Tab extends HTMLAttributes<'div'> {
|
|
19
|
+
title: string;
|
|
20
|
+
index: number;
|
|
21
|
+
active: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const tabs: Tab[] = [];
|
|
25
|
+
let index = 0;
|
|
26
|
+
|
|
27
|
+
// Process each element with data-tab-title, capturing and removing custom attributes
|
|
28
|
+
const itemHtml = html.replace(
|
|
29
|
+
/(<[^>]+\bdata-tab-attrs="([^"]*)"[^>]*>)/g,
|
|
30
|
+
(match) => {
|
|
31
|
+
const currentIndex = index++;
|
|
32
|
+
|
|
33
|
+
const getAttr = (attr: string) => {
|
|
34
|
+
const m = match.match(new RegExp(`${attr}="([^"]*)"`));
|
|
35
|
+
return m ? m[1] : undefined;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const tabAttrs: HTMLAttributes<'div'> = {};
|
|
39
|
+
const attrs = getAttr('data-tab-attrs') ?? '';
|
|
40
|
+
if (attrs) {
|
|
41
|
+
console.log(attrs);
|
|
42
|
+
Object.assign(tabAttrs, JSON.parse(attrs));
|
|
43
|
+
}
|
|
44
|
+
// const encodedTitle = getAttr('data-tab-title') ?? '';
|
|
45
|
+
// const cssClass = getAttr('data-tab-class');
|
|
46
|
+
const active = getAttr('data-tab-active') === 'true';
|
|
47
|
+
|
|
48
|
+
tabs.push({
|
|
49
|
+
...tabAttrs,
|
|
50
|
+
title: tabAttrs.title ?? `Tab ${index + 1}`,
|
|
51
|
+
index: currentIndex,
|
|
52
|
+
active,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Remove custom attrs, add data-tab, remove hidden if active
|
|
56
|
+
let updated = match
|
|
57
|
+
.replace(/\s*data-tab-attrs="[^"]*"/, '')
|
|
58
|
+
.replace(/\s*data-tab-active="[^"]*"/, '')
|
|
59
|
+
.replace(/\s*hidden(?:="[^"]*")?/, '')
|
|
60
|
+
+ (active ? '' : '');
|
|
61
|
+
|
|
62
|
+
// Insert data-tab index
|
|
63
|
+
updated = updated.replace(/^<(\w+)/, `<$1 data-tab="${currentIndex}"`);
|
|
64
|
+
|
|
65
|
+
// Re-add hidden if not active
|
|
66
|
+
if (!active) {
|
|
67
|
+
updated = updated.replace(/^<(\w+)/, `<$1 hidden`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return updated;
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const tabHtml = tabs.map((tab) => {
|
|
75
|
+
const cssClass = ['evo-tab'];
|
|
76
|
+
tab.class && cssClass.push(tab.class);
|
|
77
|
+
tab.active && cssClass.push('evo-tab-active');
|
|
78
|
+
|
|
79
|
+
if (listType === 'none') {
|
|
80
|
+
return `<button class="${cssClass.join(' ')}" data-tab="${tab.index}" role="tab">${tab.title}</button>`;
|
|
81
|
+
} else {
|
|
82
|
+
return `<li class="${cssClass.join(' ')}"><button data-tab="${tab.index}" role="tab">${tab.title}</button></li>`;
|
|
83
|
+
}
|
|
84
|
+
}).join('');
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
<div class="evo-tab-container">
|
|
88
|
+
<TabList {...tabListAttrs} set:html={tabHtml} />
|
|
89
|
+
<div class="evo-tab-panels" set:html={itemHtml}></div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<script>
|
|
93
|
+
import './Tabs';
|
|
94
|
+
</script>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
@layer inizioevoke-astro-components {
|
|
2
|
+
.evo-tab-container {
|
|
3
|
+
[role="tablist"] {
|
|
4
|
+
display: flex;
|
|
5
|
+
margin: 0;
|
|
6
|
+
padding: 0;
|
|
7
|
+
|
|
8
|
+
.evo-tab-active {
|
|
9
|
+
background-color: white;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
button {
|
|
13
|
+
padding: 4px 8px;
|
|
14
|
+
font-size: 1rem;
|
|
15
|
+
border: none;
|
|
16
|
+
border-radius: 0;
|
|
17
|
+
background-color: transparent;
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* <Tabs /> or <Tabs listType="ul" /> */
|
|
23
|
+
ul[role="tablist"] {
|
|
24
|
+
position: relative;
|
|
25
|
+
top: 1px;
|
|
26
|
+
li {
|
|
27
|
+
display: block;
|
|
28
|
+
margin: 0px 8px;
|
|
29
|
+
padding: 4px 8px;
|
|
30
|
+
list-style: none;
|
|
31
|
+
background-color: #888888;
|
|
32
|
+
border: 1px solid #888888;
|
|
33
|
+
&.evo-tab-active {
|
|
34
|
+
border-bottom-color: white;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&:first-of-type {
|
|
38
|
+
margin-left: 0;
|
|
39
|
+
}
|
|
40
|
+
&:last-of-type {
|
|
41
|
+
margin-right: 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* <Tabs listType="none" /> */
|
|
47
|
+
div[role="tablist"] {
|
|
48
|
+
button {
|
|
49
|
+
margin: 0px 8px;
|
|
50
|
+
padding: 8px 16px;
|
|
51
|
+
background-color: #888888;
|
|
52
|
+
|
|
53
|
+
&:first-of-type {
|
|
54
|
+
margin-left: 0;
|
|
55
|
+
}
|
|
56
|
+
&:last-of-type {
|
|
57
|
+
margin-right: 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.evo-tab-panels {
|
|
63
|
+
margin: 0;
|
|
64
|
+
padding: 8px;
|
|
65
|
+
color: black;
|
|
66
|
+
background-color: white;
|
|
67
|
+
border: 1px solid #888888;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
function handleTabClick(e: Event) {
|
|
3
|
+
const btn = (e.currentTarget ?? e.target) as HTMLElement;
|
|
4
|
+
const tab = btn.getAttribute('data-tab');
|
|
5
|
+
const list = btn.closest('[role="tablist"]') as HTMLElement;
|
|
6
|
+
list.querySelectorAll('.evo-tab-active').forEach((t) => { t.classList.remove('evo-tab-active'); });
|
|
7
|
+
btn.closest('.evo-tab')?.classList.add('evo-tab-active');
|
|
8
|
+
|
|
9
|
+
list.closest('.evo-tab-container')?.querySelector('.evo-tab-panels')?.querySelectorAll('[data-tab]')?.forEach((panel) => {
|
|
10
|
+
if (panel.getAttribute('data-tab') === tab) {
|
|
11
|
+
panel.removeAttribute('hidden');
|
|
12
|
+
} else if (!panel.hasAttribute('hidden')) {
|
|
13
|
+
panel.setAttribute('hidden', 'hidden');
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
document.querySelectorAll<HTMLElement>('.evo-tab-container').forEach((container) => {
|
|
19
|
+
container.querySelectorAll<HTMLElement>('[role="tab"]').forEach((tab) => {
|
|
20
|
+
tab.addEventListener('click', handleTabClick);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
4
|
+
import Modal from '../Modal/Modal.astro';
|
|
5
|
+
import ModalTrigger from '../Modal/ModalTrigger.astro';
|
|
6
|
+
import type { ModalAnimation } from '../Modal/Modal';
|
|
7
|
+
|
|
8
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
9
|
+
animation?: ModalAnimation;
|
|
10
|
+
modalIncludeTitle?: boolean;
|
|
11
|
+
modalIncludeFooter?: boolean;
|
|
12
|
+
modalSlot?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
animation,
|
|
17
|
+
modalIncludeTitle = true,
|
|
18
|
+
modalIncludeFooter = true,
|
|
19
|
+
modalSlot,
|
|
20
|
+
...rest
|
|
21
|
+
} = Astro.props;
|
|
22
|
+
|
|
23
|
+
const uuid = randomUUID().replace(/-/g, '');
|
|
24
|
+
const modalId = `evo-modal-${uuid}`;
|
|
25
|
+
|
|
26
|
+
const attrs: HTMLAttributes<'div'> & { 'data-uuid': string } = {
|
|
27
|
+
...rest,
|
|
28
|
+
'data-uuid': uuid
|
|
29
|
+
};
|
|
30
|
+
---
|
|
31
|
+
<div {...attrs}>
|
|
32
|
+
<div class="evo-zoom-wrapper">
|
|
33
|
+
<div class="evo-zoom-trigger"><ModalTrigger modal={`#${modalId}`} animation={animation}><slot name="trigger">+</slot></ModalTrigger></div>
|
|
34
|
+
{Astro.slots.has('header') &&
|
|
35
|
+
<div class="evo-zoom-header"><slot name="header" /></div>
|
|
36
|
+
}
|
|
37
|
+
<slot name="content" />
|
|
38
|
+
{Astro.slots.has('footer') &&
|
|
39
|
+
<div class="evo-zoom-footer"><slot name="footer" /></div>
|
|
40
|
+
}
|
|
41
|
+
</div>
|
|
42
|
+
<Modal slot={modalSlot} id={modalId} class="evo-zoom-modal">
|
|
43
|
+
{Astro.slots.has('modal-header') &&
|
|
44
|
+
<div class="evo-zoom-modal-header"><slot name="modal-header" /></div>
|
|
45
|
+
}
|
|
46
|
+
{modalIncludeTitle && !Astro.slots.has('modal-header') && Astro.slots.has('header') &&
|
|
47
|
+
<div class="evo-zoom-modal-header"><slot name="header" /></div>
|
|
48
|
+
}
|
|
49
|
+
<div class="evo-zoom-modal-content">
|
|
50
|
+
{Astro.slots.has('modal-content') &&
|
|
51
|
+
<slot name="modal-content" />
|
|
52
|
+
}
|
|
53
|
+
{!Astro.slots.has('modal-content') &&
|
|
54
|
+
<slot name="content" />
|
|
55
|
+
}
|
|
56
|
+
</div>
|
|
57
|
+
{Astro.slots.has('modal-footer') &&
|
|
58
|
+
<div class="evo-zoom-modal-footer"><slot name="modal-footer" /></div>
|
|
59
|
+
}
|
|
60
|
+
{modalIncludeFooter && !Astro.slots.has('modal-footer') && Astro.slots.has('footer') &&
|
|
61
|
+
<div class="evo-zoom-modal-footer"><slot name="footer" /></div>
|
|
62
|
+
}
|
|
63
|
+
</Modal>
|
|
64
|
+
</div>
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as Modal } from './Modal/Modal.astro';
|
|
2
|
+
export { default as ModalOverlay } from './Modal/ModalOverlay.astro';
|
|
3
|
+
export { default as ModalTrigger } from './Modal/ModalTrigger.astro';
|
|
4
|
+
export { default as PdfViewer } from './PdfViewer/PdfViewer.astro';
|
|
5
|
+
export { default as ScrollContainer } from './ScrollContainer/ScrollContainer.astro';
|
|
6
|
+
export { default as Tabs } from './Tabs/Tabs.astro';
|
|
7
|
+
export { default as TabItem } from './Tabs/TabItem.astro';
|
|
8
|
+
export { default as Zoom } from './Zoom/Zoom.astro';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readdir, readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { basename, extname, join, sep } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import type { AstroIntegration } from 'astro';
|
|
5
|
+
|
|
6
|
+
async function getFiles(dir: string, { includeExts, excludeExts }: { includeExts?: string[], excludeExts?: string[] } = {}): Promise<string[]> {
|
|
7
|
+
let files: string[] = [];
|
|
8
|
+
const content = await readdir(dir, { withFileTypes: true });
|
|
9
|
+
for (const c of content) {
|
|
10
|
+
if (c.isDirectory()) {
|
|
11
|
+
files = files.concat(await getFiles(join(dir, c.name), { includeExts, excludeExts }));
|
|
12
|
+
} else {
|
|
13
|
+
const ext = extname(c.name);
|
|
14
|
+
if ((!includeExts || includeExts.includes(ext)) && (!excludeExts || !excludeExts.includes(ext))) {
|
|
15
|
+
files.push(join(dir, c.name));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface PruneBuildOpts {
|
|
23
|
+
keep?: (string | RegExp)[]
|
|
24
|
+
}
|
|
25
|
+
export default function(opts?: PruneBuildOpts): AstroIntegration {
|
|
26
|
+
return {
|
|
27
|
+
name: 'prune-build',
|
|
28
|
+
hooks: {
|
|
29
|
+
'astro:build:done': async ({ dir, logger }) => {
|
|
30
|
+
logger.info('Pruning unused assets from build');
|
|
31
|
+
let dirPath = fileURLToPath(dir);
|
|
32
|
+
if (dirPath.endsWith(sep)) {
|
|
33
|
+
dirPath = dirPath.slice(0, -1);
|
|
34
|
+
}
|
|
35
|
+
const assets = await getFiles(dirPath, { excludeExts: ['.html'] });
|
|
36
|
+
const files = await getFiles(dirPath, { includeExts: ['.css', '.html', '.js'] });
|
|
37
|
+
const keep = new Set<string>();
|
|
38
|
+
|
|
39
|
+
for (const file of files) {
|
|
40
|
+
const content = await readFile(file, 'utf8');
|
|
41
|
+
for (const asset of assets) {
|
|
42
|
+
if (content.includes(basename(asset))) {
|
|
43
|
+
keep.add(asset);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const asset of assets) {
|
|
49
|
+
if (!keep.has(asset)) {
|
|
50
|
+
let prune = true;
|
|
51
|
+
const relPath = asset.replace(dirPath, '').replace(/\\/g, '/');
|
|
52
|
+
if (opts?.keep && opts.keep.length > 0) {
|
|
53
|
+
for (const k of opts.keep) {
|
|
54
|
+
if ((typeof k == 'string' && k === relPath) || (k instanceof RegExp && k.test(relPath))) {
|
|
55
|
+
prune = false;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (prune) {
|
|
61
|
+
logger.warn(`Pruning asset: ${relPath}`);
|
|
62
|
+
await rm(asset, { force: true });
|
|
63
|
+
} else {
|
|
64
|
+
logger.info(`Retaining asset: ${relPath}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|