@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
|
@@ -127,13 +127,13 @@
|
|
|
127
127
|
}*/
|
|
128
128
|
.evo-pdf-viewer-container {
|
|
129
129
|
height: calc(var(--evo-pdf-viewer-height) - var(--evo-pdf-viewer-toolbar-height));
|
|
130
|
-
padding: 0 9px 9px
|
|
130
|
+
padding: 0 9px 9px var(--evo-scroll-container-content-padding-right);
|
|
131
131
|
box-sizing: border-box;
|
|
132
132
|
|
|
133
133
|
.evo-scroll-container {
|
|
134
134
|
--evo-scroll-container-track-color: #cccccc;
|
|
135
135
|
.evo-scroll-content {
|
|
136
|
-
|
|
136
|
+
.evo-pdf-viewer-page-wrapper {
|
|
137
137
|
display: block;
|
|
138
138
|
margin: 18px auto;
|
|
139
139
|
background-color: white;
|
|
@@ -145,6 +145,21 @@
|
|
|
145
145
|
margin-bottom: 0;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
canvas {
|
|
150
|
+
display: block;
|
|
151
|
+
width: 100%;
|
|
152
|
+
height: 100%;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.evo-pdf-viewer-link-overlay {
|
|
156
|
+
pointer-events: auto;
|
|
157
|
+
z-index: 1;
|
|
158
|
+
|
|
159
|
+
rect {
|
|
160
|
+
cursor: pointer;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
148
163
|
}
|
|
149
164
|
}
|
|
150
165
|
}
|
|
@@ -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) {
|
|
@@ -26,11 +225,11 @@ export async function createViewer(selector: string) {
|
|
|
26
225
|
const toolbarHeight = parseFloat(getComputedStyle(toolbar).height);
|
|
27
226
|
const pageSelect = toolbar.querySelector('.evo-pdf-viewer-nav-page select') as HTMLSelectElement;
|
|
28
227
|
const scrollContainer = ScrollContainer.getScrollContainer(pdfViewer.querySelector('.evo-scroll-container') as HTMLElement) as ScrollContainer.IScrollContainer;
|
|
29
|
-
|
|
228
|
+
const defaultZoom = parseInt(pdfViewer.getAttribute('data-zoom') ?? '100', 10);
|
|
30
229
|
const zoom = (z: number) => {
|
|
31
230
|
const style = getComputedStyle(pdfViewer);
|
|
32
231
|
const current = Math.floor(parseFloat(style.getPropertyValue('--evo-pdf-viewer-scale')) * 10);
|
|
33
|
-
const scale = z === 0 ? 10 : current + z;
|
|
232
|
+
const scale = z === 0 ? defaultZoom / 10 : current + z;
|
|
34
233
|
if (scale >= 1) {
|
|
35
234
|
const scroll = ScrollContainer.getScroll(scrollContainer);
|
|
36
235
|
pdfViewer.style.setProperty('--evo-pdf-viewer-scale', (scale/10).toString());
|
|
@@ -38,6 +237,7 @@ export async function createViewer(selector: string) {
|
|
|
38
237
|
ScrollContainer.refresh(scrollContainer);
|
|
39
238
|
}
|
|
40
239
|
};
|
|
240
|
+
zoom(0);
|
|
41
241
|
|
|
42
242
|
const gotoPage = (page: number | string) => {
|
|
43
243
|
if (typeof page == 'string') {
|
|
@@ -72,11 +272,13 @@ export async function createViewer(selector: string) {
|
|
|
72
272
|
|
|
73
273
|
const pdf: PDFDocumentProxy = await pdfjs.getDocument(pdfViewer.getAttribute('data-pdf') as string).promise;
|
|
74
274
|
|
|
75
|
-
const bookmarksSelect = toolbar.querySelector('.evo-pdf-viewer-toolbar-bookmarks select')
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
275
|
+
const bookmarksSelect = toolbar.querySelector<HTMLSelectElement>('.evo-pdf-viewer-toolbar-bookmarks select');
|
|
276
|
+
if (bookmarksSelect) {
|
|
277
|
+
bookmarksSelect.addEventListener('change', () => {
|
|
278
|
+
gotoPage(bookmarksSelect.options[bookmarksSelect.selectedIndex].value);
|
|
279
|
+
bookmarksSelect.selectedIndex = 0;
|
|
280
|
+
});
|
|
281
|
+
}
|
|
80
282
|
|
|
81
283
|
const outputScale = window.devicePixelRatio || 1;
|
|
82
284
|
const pages = new Array(pdf.numPages).fill(1).map((v, i) => { return i + 1; });
|
|
@@ -96,7 +298,6 @@ export async function createViewer(selector: string) {
|
|
|
96
298
|
}
|
|
97
299
|
}, { passive: true });
|
|
98
300
|
|
|
99
|
-
|
|
100
301
|
const pdfContainer = pdfViewer.querySelector('.evo-scroll-content') as HTMLElement;
|
|
101
302
|
pdfViewer.querySelector<HTMLElement>('.evo-pdf-viewer-nav-page-count')!.innerText = pages.length.toString();
|
|
102
303
|
pageSelect.options.remove(0);
|
|
@@ -123,7 +324,16 @@ export async function createViewer(selector: string) {
|
|
|
123
324
|
canvas.style.aspectRatio = `${width}/${height}`;
|
|
124
325
|
canvas.style.width = `calc(${Math.floor(viewport.width) - (trackWidth * 2)}px * var(--evo-pdf-viewer-scale)`;
|
|
125
326
|
// canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
126
|
-
|
|
327
|
+
|
|
328
|
+
// Create wrapper for canvas and overlay
|
|
329
|
+
const pageWrapper = document.createElement('div');
|
|
330
|
+
pageWrapper.className = 'evo-pdf-viewer-page-wrapper';
|
|
331
|
+
pageWrapper.style.position = 'relative';
|
|
332
|
+
pageWrapper.style.width = canvas.style.width;
|
|
333
|
+
pageWrapper.style.aspectRatio = canvas.style.aspectRatio;
|
|
334
|
+
pageWrapper.appendChild(canvas);
|
|
335
|
+
|
|
336
|
+
pdfContainer.append(pageWrapper);
|
|
127
337
|
|
|
128
338
|
pdfPage.render({
|
|
129
339
|
canvas,
|
|
@@ -131,6 +341,14 @@ export async function createViewer(selector: string) {
|
|
|
131
341
|
transform: outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : undefined,
|
|
132
342
|
viewport
|
|
133
343
|
});
|
|
344
|
+
|
|
345
|
+
// Extract and render links
|
|
346
|
+
const links = await getPageLinks(pdfPage);
|
|
347
|
+
if (links.length > 0) {
|
|
348
|
+
const linkOverlay = createLinkOverlay(canvas, links, viewport, pageNum, pdfViewer, gotoPage, pdf);
|
|
349
|
+
pageWrapper.appendChild(linkOverlay);
|
|
350
|
+
}
|
|
351
|
+
|
|
134
352
|
ScrollContainer.refresh(scrollContainer);
|
|
135
353
|
}
|
|
136
354
|
|