@inizioevoke/astro-core 1.5.2 → 1.6.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 +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.css +16 -1
- package/src/components/PdfViewer/PdfViewer.ts +217 -1
- package/src/components/PdfViewer/pdf.min.mjs +4 -4
- package/src/components/PdfViewer/pdf.worker.min.mjs +7 -4
- 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
|
@@ -4,6 +4,25 @@ import * as ScrollContainer from '../ScrollContainer/archive-20260417';
|
|
|
4
4
|
|
|
5
5
|
pdfjs.GlobalWorkerOptions.workerSrc = './pdf.worker.min.mjs';
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
export interface PdfViewerCustomEventDetail {
|
|
9
|
+
type: string;
|
|
10
|
+
cmd?: string;
|
|
11
|
+
args?: Record<string, string>
|
|
12
|
+
}
|
|
13
|
+
declare global {
|
|
14
|
+
interface ElementEventMap {
|
|
15
|
+
'pdfviewer-click': CustomEvent<PdfViewerCustomEventDetail>
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface LinkAnnotation {
|
|
20
|
+
rect: [number, number, number, number];
|
|
21
|
+
url?: string;
|
|
22
|
+
dest?: any;
|
|
23
|
+
newWindow?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
7
26
|
interface PdfViewerSettings {
|
|
8
27
|
workerDir?: string;
|
|
9
28
|
workerSrc?: string;
|
|
@@ -18,6 +37,186 @@ export function init(settings: PdfViewerSettings = {}) {
|
|
|
18
37
|
pdfjs.GlobalWorkerOptions.workerSrc = settings.workerSrc;
|
|
19
38
|
}
|
|
20
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Extract link annotations from a PDF page
|
|
42
|
+
*/
|
|
43
|
+
async function getPageLinks(pdfPage: any): Promise<LinkAnnotation[]> {
|
|
44
|
+
const annotations = await pdfPage.getAnnotations();
|
|
45
|
+
const links: LinkAnnotation[] = [];
|
|
46
|
+
|
|
47
|
+
for (const annotation of annotations) {
|
|
48
|
+
// Only process link annotations (type 2 is subtype for URI links and internal links)
|
|
49
|
+
if (annotation.subtype === 'Link') {
|
|
50
|
+
const link: LinkAnnotation = {
|
|
51
|
+
rect: annotation.rect
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (annotation.url) {
|
|
55
|
+
link.url = annotation.url;
|
|
56
|
+
link.newWindow = annotation.newWindow;
|
|
57
|
+
} else if (annotation.dest) {
|
|
58
|
+
link.dest = annotation.dest;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
links.push(link);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return links;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Convert PDF coordinates to viewport coordinates
|
|
70
|
+
* PDF coords: [left, bottom, right, top] with origin at bottom-left
|
|
71
|
+
* Return: {x, y, width, height} with origin at top-left
|
|
72
|
+
*/
|
|
73
|
+
function convertPdfRectToViewport(
|
|
74
|
+
pdfRect: [number, number, number, number],
|
|
75
|
+
viewport: any
|
|
76
|
+
): { x: number; y: number; width: number; height: number } {
|
|
77
|
+
const [pdfLeft, pdfBottom, pdfRight, pdfTop] = pdfRect;
|
|
78
|
+
const pageHeight = viewport.height / viewport.scale;
|
|
79
|
+
|
|
80
|
+
// Scale coordinates
|
|
81
|
+
const x = pdfLeft * viewport.scale;
|
|
82
|
+
const y = (pageHeight - pdfTop) * viewport.scale;
|
|
83
|
+
const width = (pdfRight - pdfLeft) * viewport.scale;
|
|
84
|
+
const height = (pdfTop - pdfBottom) * viewport.scale;
|
|
85
|
+
|
|
86
|
+
return { x, y, width, height };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Custom PDF link events
|
|
91
|
+
* To create a custom event, create a link in the PDF that starts with `http://pdfviewer`
|
|
92
|
+
* Arguments can be passed via the path and query string
|
|
93
|
+
* http://pdfviewer/type/cmd/?args=1
|
|
94
|
+
*/
|
|
95
|
+
/**
|
|
96
|
+
* Create an SVG overlay with link areas above a canvas element
|
|
97
|
+
*/
|
|
98
|
+
function createLinkOverlay(
|
|
99
|
+
canvas: HTMLCanvasElement,
|
|
100
|
+
links: LinkAnnotation[],
|
|
101
|
+
viewport: any,
|
|
102
|
+
pageNum: number,
|
|
103
|
+
pdfViewer: HTMLElement,
|
|
104
|
+
gotoPage: (page: number | string) => void,
|
|
105
|
+
pdf: PDFDocumentProxy
|
|
106
|
+
): SVGSVGElement {
|
|
107
|
+
const trackWidth = parseInt(
|
|
108
|
+
getComputedStyle(canvas.parentElement!).getPropertyValue('--evo-scroll-container-track-width')
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const canvasWidth = Math.floor(viewport.width) - trackWidth * 2;
|
|
112
|
+
const canvasHeight = Math.floor(viewport.height);
|
|
113
|
+
|
|
114
|
+
// Create SVG overlay
|
|
115
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
116
|
+
svg.setAttribute('class', 'evo-pdf-viewer-link-overlay');
|
|
117
|
+
svg.setAttribute('data-page', pageNum.toString());
|
|
118
|
+
svg.setAttribute('viewBox', `0 0 ${viewport.width} ${viewport.height}`);
|
|
119
|
+
svg.setAttribute('width', `calc(${canvasWidth}px * var(--evo-pdf-viewer-scale))`);
|
|
120
|
+
svg.setAttribute('height', `calc(${canvasHeight}px * var(--evo-pdf-viewer-scale))`);
|
|
121
|
+
svg.style.position = 'absolute';
|
|
122
|
+
svg.style.top = '0';
|
|
123
|
+
svg.style.left = '0';
|
|
124
|
+
svg.style.cursor = 'pointer';
|
|
125
|
+
|
|
126
|
+
// Add link rectangles
|
|
127
|
+
for (const link of links) {
|
|
128
|
+
const rect = convertPdfRectToViewport(link.rect, viewport);
|
|
129
|
+
|
|
130
|
+
// Create invisible clickable area
|
|
131
|
+
const linkRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
132
|
+
linkRect.setAttribute('x', rect.x.toString());
|
|
133
|
+
linkRect.setAttribute('y', rect.y.toString());
|
|
134
|
+
linkRect.setAttribute('width', rect.width.toString());
|
|
135
|
+
linkRect.setAttribute('height', rect.height.toString());
|
|
136
|
+
linkRect.setAttribute('fill', 'transparent');
|
|
137
|
+
linkRect.setAttribute('stroke', 'none');
|
|
138
|
+
linkRect.style.cursor = 'pointer';
|
|
139
|
+
|
|
140
|
+
// Handle click on link
|
|
141
|
+
linkRect.addEventListener('click', (e) => {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
e.stopPropagation();
|
|
144
|
+
|
|
145
|
+
if (link.url) {
|
|
146
|
+
if (link.url.startsWith('http://pdfviewer')) {
|
|
147
|
+
const url = new URL(link.url);
|
|
148
|
+
const [ type, cmd ] = url.pathname.split('/').filter(p => p.trim() !== '');
|
|
149
|
+
const args = url.search === '' ? undefined :
|
|
150
|
+
Object.fromEntries(url.search.slice(1).split('&')
|
|
151
|
+
.map((pair) => {
|
|
152
|
+
return pair.split('=').slice(0, 2);
|
|
153
|
+
})
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
pdfViewer.dispatchEvent(new CustomEvent('pdfviewer-click', {
|
|
157
|
+
detail: { type: type ?? '', cmd, args }
|
|
158
|
+
}));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// Check for custom protocol
|
|
162
|
+
// if (link.url.includes('://')) {
|
|
163
|
+
// const protocol = link.url.split('://')[0];
|
|
164
|
+
// if (!['http', 'https', 'ftp', 'ftps', 'mailto'].includes(protocol)) {
|
|
165
|
+
// // Dispatch custom event for non-standard protocols
|
|
166
|
+
// const event = new CustomEvent('pdf-link-click', {
|
|
167
|
+
// detail: {
|
|
168
|
+
// type: 'uri',
|
|
169
|
+
// uri: link.url,
|
|
170
|
+
// pageNum,
|
|
171
|
+
// newWindow: link.newWindow
|
|
172
|
+
// }
|
|
173
|
+
// });
|
|
174
|
+
// pdfViewer.dispatchEvent(event);
|
|
175
|
+
// return;
|
|
176
|
+
// }
|
|
177
|
+
// }
|
|
178
|
+
|
|
179
|
+
// Handle standard URLs
|
|
180
|
+
const target = link.newWindow ? '_blank' : '_self';
|
|
181
|
+
window.open(link.url, target);
|
|
182
|
+
} else if (link.dest) {
|
|
183
|
+
// Handle internal navigation
|
|
184
|
+
// dest is typically an array [pageRef, ...] where pageRef is {num, gen} or a page index
|
|
185
|
+
if (Array.isArray(link.dest)) {
|
|
186
|
+
const destRef = link.dest[0];
|
|
187
|
+
|
|
188
|
+
// Check if it's a reference object with num and gen properties
|
|
189
|
+
if (destRef && typeof destRef === 'object' && 'num' in destRef && 'gen' in destRef) {
|
|
190
|
+
// Resolve the reference to a page index using the PDF document
|
|
191
|
+
pdf.getPageIndex(destRef).then((pageIndex: number) => {
|
|
192
|
+
gotoPage(pageIndex + 1); // PDF pages are 0-indexed, but gotoPage expects 1-indexed
|
|
193
|
+
}).catch((err: Error) => {
|
|
194
|
+
console.error('Failed to resolve PDF link destination:', err);
|
|
195
|
+
});
|
|
196
|
+
} else if (typeof destRef === 'number') {
|
|
197
|
+
// Direct page index
|
|
198
|
+
gotoPage(destRef + 1);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Dispatch event for custom dest handling
|
|
203
|
+
const event = new CustomEvent('pdf-link-click', {
|
|
204
|
+
detail: {
|
|
205
|
+
type: 'goto',
|
|
206
|
+
dest: link.dest,
|
|
207
|
+
pageNum
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
pdfViewer.dispatchEvent(event);
|
|
211
|
+
}
|
|
212
|
+
}, { passive: false });
|
|
213
|
+
|
|
214
|
+
svg.appendChild(linkRect);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return svg;
|
|
218
|
+
}
|
|
219
|
+
|
|
21
220
|
export async function createViewer(selector: string) {
|
|
22
221
|
const pdfViewer = document.querySelector<HTMLElement>(selector);
|
|
23
222
|
if (pdfViewer) {
|
|
@@ -123,7 +322,16 @@ export async function createViewer(selector: string) {
|
|
|
123
322
|
canvas.style.aspectRatio = `${width}/${height}`;
|
|
124
323
|
canvas.style.width = `calc(${Math.floor(viewport.width) - (trackWidth * 2)}px * var(--evo-pdf-viewer-scale)`;
|
|
125
324
|
// canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
126
|
-
|
|
325
|
+
|
|
326
|
+
// Create wrapper for canvas and overlay
|
|
327
|
+
const pageWrapper = document.createElement('div');
|
|
328
|
+
pageWrapper.className = 'evo-pdf-viewer-page-wrapper';
|
|
329
|
+
pageWrapper.style.position = 'relative';
|
|
330
|
+
pageWrapper.style.width = canvas.style.width;
|
|
331
|
+
pageWrapper.style.aspectRatio = canvas.style.aspectRatio;
|
|
332
|
+
pageWrapper.appendChild(canvas);
|
|
333
|
+
|
|
334
|
+
pdfContainer.append(pageWrapper);
|
|
127
335
|
|
|
128
336
|
pdfPage.render({
|
|
129
337
|
canvas,
|
|
@@ -131,6 +339,14 @@ export async function createViewer(selector: string) {
|
|
|
131
339
|
transform: outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : undefined,
|
|
132
340
|
viewport
|
|
133
341
|
});
|
|
342
|
+
|
|
343
|
+
// Extract and render links
|
|
344
|
+
const links = await getPageLinks(pdfPage);
|
|
345
|
+
if (links.length > 0) {
|
|
346
|
+
const linkOverlay = createLinkOverlay(canvas, links, viewport, pageNum, pdfViewer, gotoPage, pdf);
|
|
347
|
+
pageWrapper.appendChild(linkOverlay);
|
|
348
|
+
}
|
|
349
|
+
|
|
134
350
|
ScrollContainer.refresh(scrollContainer);
|
|
135
351
|
}
|
|
136
352
|
|