@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
|
@@ -1,42 +1,245 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
1
2
|
import * as pdfjs from './pdf.min.mjs';
|
|
3
|
+
import workerSrc from './pdf.worker.min.mjs?url';
|
|
2
4
|
import type { PDFDocumentProxy } from 'pdfjs-dist';
|
|
3
|
-
import
|
|
5
|
+
import { EvoScrollContainerElement } from '../ScrollContainer/ScrollContainer';
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
const TAG_NAME = 'evo-pdf-viewer';
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
export interface PdfViewerCustomEventDetail {
|
|
9
|
+
export interface EvoPdfViewerEventDetail {
|
|
9
10
|
type: string;
|
|
10
11
|
cmd?: string;
|
|
11
12
|
args?: Record<string, string>
|
|
12
13
|
}
|
|
14
|
+
export type EvoPdfViewerEvent = CustomEvent<EvoPdfViewerEventDetail>;
|
|
13
15
|
declare global {
|
|
14
16
|
interface ElementEventMap {
|
|
15
|
-
'
|
|
17
|
+
'evopdfviewer-click': EvoPdfViewerEvent
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
interface
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
dest?: any;
|
|
23
|
-
newWindow?: boolean;
|
|
21
|
+
export interface IEvoPdfViewerElement {
|
|
22
|
+
scrollToPage: (page: number | string) => void;
|
|
23
|
+
zoom: (z: number) => void;
|
|
24
24
|
}
|
|
25
|
+
export class EvoPdfViewerElement extends HTMLElement implements IEvoPdfViewerElement {
|
|
26
|
+
#toolbar!: HTMLElement;
|
|
27
|
+
#pageSelect!: HTMLSelectElement;
|
|
28
|
+
#scrollContainer!: EvoScrollContainerElement;
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
#toolbarHeight = 0;
|
|
31
|
+
#defaultZoom = 100;
|
|
32
|
+
#pageCount = 0;
|
|
33
|
+
|
|
34
|
+
constructor() {
|
|
35
|
+
super();
|
|
36
|
+
pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;
|
|
33
37
|
}
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
|
|
39
|
+
connectedCallback() {
|
|
40
|
+
this.#toolbar = this.querySelector('.evo-pdf-viewer-toolbar') as HTMLElement;
|
|
41
|
+
this.#pageSelect = this.#toolbar.querySelector('.evo-pdf-viewer-nav-page select') as HTMLSelectElement;
|
|
42
|
+
this.#scrollContainer = this.querySelector('evo-scroll-container') as EvoScrollContainerElement;
|
|
43
|
+
|
|
44
|
+
this.#toolbarHeight = parseFloat(getComputedStyle(this.#toolbar).height);
|
|
45
|
+
this.#defaultZoom = parseInt(this.getAttribute('data-zoom') ?? '100', 10);
|
|
46
|
+
|
|
47
|
+
// *** nav
|
|
48
|
+
this.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-prev')?.addEventListener('click', () => {
|
|
49
|
+
const page = parseInt(this.#pageSelect.options[this.#pageSelect.selectedIndex].value, 10) - 1;
|
|
50
|
+
if (page > 0) {
|
|
51
|
+
this.scrollToPage(page);
|
|
52
|
+
}
|
|
53
|
+
}, { passive: true });
|
|
54
|
+
|
|
55
|
+
this.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-next')?.addEventListener('click', () => {
|
|
56
|
+
const page = parseInt(this.#pageSelect.options[this.#pageSelect.selectedIndex].value, 10) + 1;
|
|
57
|
+
if (page <= this.#pageCount) {
|
|
58
|
+
this.scrollToPage(page);
|
|
59
|
+
}
|
|
60
|
+
}, { passive: true });
|
|
61
|
+
|
|
62
|
+
this.#pageSelect.addEventListener('change', () => {
|
|
63
|
+
const page = this.#pageSelect.options[this.#pageSelect.selectedIndex].value;
|
|
64
|
+
this.scrollToPage(page);
|
|
65
|
+
}, { passive: true });
|
|
66
|
+
|
|
67
|
+
// *** zoom
|
|
68
|
+
this.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-zoom-out')?.addEventListener('click', () => {
|
|
69
|
+
this.zoom(-1);
|
|
70
|
+
}, { passive: true });
|
|
71
|
+
|
|
72
|
+
this.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-zoom-in')?.addEventListener('click', () => {
|
|
73
|
+
this.zoom(1);
|
|
74
|
+
}, { passive: true });
|
|
75
|
+
|
|
76
|
+
this.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-zoom-reset')?.addEventListener('click', () => {
|
|
77
|
+
this.zoom(0);
|
|
78
|
+
}, { passive: true });
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
// *** bookmarks
|
|
82
|
+
const bookmarksSelect = this.#toolbar.querySelector<HTMLSelectElement>('.evo-pdf-viewer-toolbar-bookmarks select');
|
|
83
|
+
if (bookmarksSelect) {
|
|
84
|
+
bookmarksSelect.addEventListener('change', () => {
|
|
85
|
+
this.scrollToPage(bookmarksSelect.options[bookmarksSelect.selectedIndex].value);
|
|
86
|
+
bookmarksSelect.selectedIndex = 0;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.#scrollContainer.addEventListener('scroll', this.#updateCurrentPage, { passive: true });
|
|
91
|
+
|
|
92
|
+
this.#renderPDF()
|
|
93
|
+
.then(() => {
|
|
94
|
+
this.zoom(0);
|
|
95
|
+
this.#updateCurrentPage();
|
|
96
|
+
})
|
|
97
|
+
.catch((err) => {
|
|
98
|
+
console.error('A PDF rendering error occurred', err);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
scrollToPage(page: number | string) {
|
|
104
|
+
if (typeof page == 'string') {
|
|
105
|
+
page = parseInt(page, 10);
|
|
106
|
+
}
|
|
107
|
+
// requestAnimationFrame(() => {
|
|
108
|
+
const canvas = this.#scrollContainer.querySelector<HTMLElement>(`canvas[data-page="${page}"]`);
|
|
109
|
+
if (canvas) {
|
|
110
|
+
this.#scrollContainer.scroll({
|
|
111
|
+
top: canvas.getBoundingClientRect().top + this.#scrollContainer.scrollTop - this.#toolbarHeight
|
|
112
|
+
}); // , behavior: 'smooth' });
|
|
113
|
+
this.#pageSelect.selectedIndex = page - 1;
|
|
114
|
+
}
|
|
115
|
+
// }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
zoom(z: number) {
|
|
119
|
+
const style = getComputedStyle(this);
|
|
120
|
+
const current = Math.floor(parseFloat(style.getPropertyValue('--evo-pdf-viewer-scale')) * 10);
|
|
121
|
+
const scale = z === 0 ? this.#defaultZoom / 10 : current + z;
|
|
122
|
+
if (scale >= 1) {
|
|
123
|
+
const scroll = {
|
|
124
|
+
left: this.#scrollContainer.scrollLeft,
|
|
125
|
+
top: this.#scrollContainer.scrollTop
|
|
126
|
+
};
|
|
127
|
+
this.style.setProperty('--evo-pdf-viewer-scale', (scale/10).toString());
|
|
128
|
+
this.#scrollContainer.scroll({
|
|
129
|
+
left: scroll.left * scale / current,
|
|
130
|
+
top: scroll.top * scale / current
|
|
131
|
+
});
|
|
132
|
+
this.#scrollContainer.refresh();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async #renderPDF() {
|
|
137
|
+
const outputScale = window.devicePixelRatio || 1;
|
|
138
|
+
const pdf: PDFDocumentProxy = await pdfjs.getDocument(this.getAttribute('data-pdf') as string).promise;
|
|
139
|
+
|
|
140
|
+
const pages = new Array(pdf.numPages).fill(1).map((v, i) => { return i + 1; });
|
|
141
|
+
this.#pageCount = pages.length;
|
|
142
|
+
|
|
143
|
+
this.querySelector<HTMLElement>('.evo-pdf-viewer-nav-page-count')!.innerText = pages.length.toString();
|
|
144
|
+
|
|
145
|
+
this.#pageSelect.options.remove(0);
|
|
146
|
+
for (const pageNum of pages) {
|
|
147
|
+
const option = document.createElement('option');
|
|
148
|
+
option.value = pageNum.toString();
|
|
149
|
+
option.innerText = `${pageNum}`;
|
|
150
|
+
this.#pageSelect.options.add(option);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for (const pageNum of pages) {
|
|
154
|
+
|
|
155
|
+
const pdfPage = await pdf.getPage(pageNum);
|
|
156
|
+
const pdfPageWidth = pdfPage.getViewport({ scale: 1}).width;
|
|
157
|
+
const containerStyle = getComputedStyle(this.#scrollContainer.content);
|
|
158
|
+
const trackWidth = parseInt(containerStyle.getPropertyValue('--evo-scroll-container-track-width'));
|
|
159
|
+
const viewport = pdfPage.getViewport({ scale: parseFloat(containerStyle.width) / pdfPageWidth });
|
|
160
|
+
|
|
161
|
+
const canvas = document.createElement('canvas');
|
|
162
|
+
canvas.setAttribute('data-page', pageNum.toString());
|
|
163
|
+
const width = Math.floor(viewport.width * outputScale);
|
|
164
|
+
const height = Math.floor(viewport.height * outputScale);
|
|
165
|
+
canvas.width = width;
|
|
166
|
+
canvas.height = height;
|
|
167
|
+
canvas.style.aspectRatio = `${width}/${height}`;
|
|
168
|
+
canvas.style.width = `calc(${Math.floor(viewport.width) - (trackWidth * 2)}px * var(--evo-pdf-viewer-scale))`;
|
|
169
|
+
// canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
170
|
+
|
|
171
|
+
// Create wrapper for canvas and overlay
|
|
172
|
+
const pageWrapper = document.createElement('div');
|
|
173
|
+
pageWrapper.className = 'evo-pdf-viewer-page-wrapper';
|
|
174
|
+
pageWrapper.style.position = 'relative';
|
|
175
|
+
pageWrapper.style.width = canvas.style.width;
|
|
176
|
+
pageWrapper.style.aspectRatio = canvas.style.aspectRatio;
|
|
177
|
+
pageWrapper.appendChild(canvas);
|
|
178
|
+
|
|
179
|
+
this.#scrollContainer.content.append(pageWrapper);
|
|
180
|
+
|
|
181
|
+
pdfPage.render({
|
|
182
|
+
canvas,
|
|
183
|
+
// canvasContext: canvas.getContext('2d') as CanvasRenderingContext2D,
|
|
184
|
+
transform: outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : undefined,
|
|
185
|
+
viewport
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Extract and render links
|
|
189
|
+
const links = await getPageLinks(pdfPage);
|
|
190
|
+
if (links.length > 0) {
|
|
191
|
+
const linkOverlay = createLinkOverlay(canvas, links, viewport, pageNum, this, this.scrollToPage.bind(this), pdf);
|
|
192
|
+
pageWrapper.appendChild(linkOverlay);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.#scrollContainer.refresh();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
#updateCurrentPage() {
|
|
200
|
+
const canvases = this.#scrollContainer.content.querySelectorAll<HTMLCanvasElement>('canvas');
|
|
201
|
+
let visiblePage: HTMLCanvasElement | undefined;
|
|
202
|
+
let maxVisibility = 0;
|
|
203
|
+
|
|
204
|
+
canvases.forEach((canvas) => {
|
|
205
|
+
const rect = canvas.getBoundingClientRect();
|
|
206
|
+
const containerRect = this.#scrollContainer.getBoundingClientRect();
|
|
207
|
+
|
|
208
|
+
// Calculate intersection height
|
|
209
|
+
const intersectTop = Math.max(rect.top, containerRect.top);
|
|
210
|
+
const intersectBottom = Math.min(rect.bottom, containerRect.bottom);
|
|
211
|
+
const intersectionHeight = Math.max(0, intersectBottom - intersectTop);
|
|
212
|
+
|
|
213
|
+
// Calculate visibility ratio
|
|
214
|
+
const visibility = intersectionHeight / (rect.height || 1);
|
|
215
|
+
|
|
216
|
+
if (visibility > maxVisibility) {
|
|
217
|
+
maxVisibility = visibility;
|
|
218
|
+
visiblePage = canvas;
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (visiblePage && maxVisibility >= 0.1) {
|
|
223
|
+
const pageNum = visiblePage.getAttribute('data-page');
|
|
224
|
+
if (pageNum) {
|
|
225
|
+
this.#pageSelect.selectedIndex = parseInt(pageNum, 10) - 1;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
36
228
|
}
|
|
37
|
-
pdfjs.GlobalWorkerOptions.workerSrc = settings.workerSrc;
|
|
38
229
|
}
|
|
39
230
|
|
|
231
|
+
if (!customElements.get(TAG_NAME)) {
|
|
232
|
+
customElements.define(TAG_NAME, EvoPdfViewerElement);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
interface LinkAnnotation {
|
|
238
|
+
rect: [number, number, number, number];
|
|
239
|
+
url?: string;
|
|
240
|
+
dest?: any;
|
|
241
|
+
newWindow?: boolean;
|
|
242
|
+
}
|
|
40
243
|
/**
|
|
41
244
|
* Extract link annotations from a PDF page
|
|
42
245
|
*/
|
|
@@ -143,7 +346,7 @@ function createLinkOverlay(
|
|
|
143
346
|
e.stopPropagation();
|
|
144
347
|
|
|
145
348
|
if (link.url) {
|
|
146
|
-
if (link.url.startsWith('http://pdfviewer')) {
|
|
349
|
+
if (link.url.startsWith('http://pdfviewer.evo')) {
|
|
147
350
|
const url = new URL(link.url);
|
|
148
351
|
const [ type, cmd ] = url.pathname.split('/').filter(p => p.trim() !== '');
|
|
149
352
|
const args = url.search === '' ? undefined :
|
|
@@ -153,7 +356,7 @@ function createLinkOverlay(
|
|
|
153
356
|
})
|
|
154
357
|
);
|
|
155
358
|
|
|
156
|
-
pdfViewer.dispatchEvent(new CustomEvent('
|
|
359
|
+
pdfViewer.dispatchEvent(new CustomEvent('evopdfviewer-click', {
|
|
157
360
|
detail: { type: type ?? '', cmd, args }
|
|
158
361
|
}));
|
|
159
362
|
return;
|
|
@@ -215,179 +418,4 @@ function createLinkOverlay(
|
|
|
215
418
|
}
|
|
216
419
|
|
|
217
420
|
return svg;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export async function createViewer(selector: string) {
|
|
221
|
-
const pdfViewer = document.querySelector<HTMLElement>(selector);
|
|
222
|
-
if (pdfViewer) {
|
|
223
|
-
// const ddlPage = pdfViewer.querySelector('select') as HTMLSelectElement;
|
|
224
|
-
const toolbar = pdfViewer.querySelector('.evo-pdf-viewer-toolbar') as HTMLElement;
|
|
225
|
-
const toolbarHeight = parseFloat(getComputedStyle(toolbar).height);
|
|
226
|
-
const pageSelect = toolbar.querySelector('.evo-pdf-viewer-nav-page select') as HTMLSelectElement;
|
|
227
|
-
const scrollContainer = ScrollContainer.getScrollContainer(pdfViewer.querySelector('.evo-scroll-container') as HTMLElement) as ScrollContainer.IScrollContainer;
|
|
228
|
-
const defaultZoom = parseInt(pdfViewer.getAttribute('data-zoom') ?? '100', 10);
|
|
229
|
-
const zoom = (z: number) => {
|
|
230
|
-
const style = getComputedStyle(pdfViewer);
|
|
231
|
-
const current = Math.floor(parseFloat(style.getPropertyValue('--evo-pdf-viewer-scale')) * 10);
|
|
232
|
-
const scale = z === 0 ? defaultZoom / 10 : current + z;
|
|
233
|
-
if (scale >= 1) {
|
|
234
|
-
const scroll = ScrollContainer.getScroll(scrollContainer);
|
|
235
|
-
pdfViewer.style.setProperty('--evo-pdf-viewer-scale', (scale/10).toString());
|
|
236
|
-
ScrollContainer.scroll(scrollContainer, { left: scroll.left * scale / current, top: scroll.top * scale / current });
|
|
237
|
-
ScrollContainer.refresh(scrollContainer);
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
zoom(0);
|
|
241
|
-
|
|
242
|
-
const gotoPage = (page: number | string) => {
|
|
243
|
-
if (typeof page == 'string') {
|
|
244
|
-
page = parseInt(page, 10);
|
|
245
|
-
}
|
|
246
|
-
requestAnimationFrame(() => {
|
|
247
|
-
const container = pdfViewer.querySelector('.evo-scroll-content') as HTMLElement;
|
|
248
|
-
const canvas = container.querySelector<HTMLElement>(`canvas[data-page="${page}"]`);
|
|
249
|
-
if (canvas) {
|
|
250
|
-
container.scroll({ top: canvas.getBoundingClientRect().top + container.scrollTop - toolbarHeight }); // , behavior: 'smooth' });
|
|
251
|
-
pageSelect.selectedIndex = page - 1;
|
|
252
|
-
}
|
|
253
|
-
})
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
pageSelect.addEventListener('change', (e) => {
|
|
257
|
-
const page = pageSelect.options[pageSelect.selectedIndex].value;
|
|
258
|
-
gotoPage(page);
|
|
259
|
-
}, { passive: true });
|
|
260
|
-
|
|
261
|
-
pdfViewer.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-zoom-out')?.addEventListener('click', () => {
|
|
262
|
-
zoom(-1);
|
|
263
|
-
}, { passive: true });
|
|
264
|
-
|
|
265
|
-
pdfViewer.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-zoom-in')?.addEventListener('click', () => {
|
|
266
|
-
zoom(1);
|
|
267
|
-
}, { passive: true });
|
|
268
|
-
|
|
269
|
-
pdfViewer.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-zoom-reset')?.addEventListener('click', () => {
|
|
270
|
-
zoom(0);
|
|
271
|
-
}, { passive: true });
|
|
272
|
-
|
|
273
|
-
const pdf: PDFDocumentProxy = await pdfjs.getDocument(pdfViewer.getAttribute('data-pdf') as string).promise;
|
|
274
|
-
|
|
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
|
-
}
|
|
282
|
-
|
|
283
|
-
const outputScale = window.devicePixelRatio || 1;
|
|
284
|
-
const pages = new Array(pdf.numPages).fill(1).map((v, i) => { return i + 1; });
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
pdfViewer.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-prev')?.addEventListener('click', () => {
|
|
288
|
-
const page = parseInt(pageSelect.options[pageSelect.selectedIndex].value, 10) - 1;
|
|
289
|
-
if (page > 0) {
|
|
290
|
-
gotoPage(page);
|
|
291
|
-
}
|
|
292
|
-
}, { passive: true });
|
|
293
|
-
|
|
294
|
-
pdfViewer.querySelector<HTMLButtonElement>('button.evo-pdf-viewer-toolbar-next')?.addEventListener('click', () => {
|
|
295
|
-
const page = parseInt(pageSelect.options[pageSelect.selectedIndex].value, 10) + 1;
|
|
296
|
-
if (page <= pages.length) {
|
|
297
|
-
gotoPage(page);
|
|
298
|
-
}
|
|
299
|
-
}, { passive: true });
|
|
300
|
-
|
|
301
|
-
const pdfContainer = pdfViewer.querySelector('.evo-scroll-content') as HTMLElement;
|
|
302
|
-
pdfViewer.querySelector<HTMLElement>('.evo-pdf-viewer-nav-page-count')!.innerText = pages.length.toString();
|
|
303
|
-
pageSelect.options.remove(0);
|
|
304
|
-
for (const pageNum of pages) {
|
|
305
|
-
const option = document.createElement('option');
|
|
306
|
-
option.value = pageNum.toString();
|
|
307
|
-
option.innerText = `${pageNum}`;
|
|
308
|
-
pageSelect.options.add(option);
|
|
309
|
-
}
|
|
310
|
-
for (const pageNum of pages) {
|
|
311
|
-
|
|
312
|
-
const pdfPage = await pdf.getPage(pageNum);
|
|
313
|
-
const pdfPageWidth = pdfPage.getViewport({ scale: 1}).width;
|
|
314
|
-
const containerStyle = getComputedStyle(pdfContainer);
|
|
315
|
-
const trackWidth = parseInt(containerStyle.getPropertyValue('--evo-scroll-container-track-width'));
|
|
316
|
-
const viewport = pdfPage.getViewport({ scale: parseFloat(containerStyle.width) / pdfPageWidth });
|
|
317
|
-
|
|
318
|
-
const canvas = document.createElement('canvas');
|
|
319
|
-
canvas.setAttribute('data-page', pageNum.toString());
|
|
320
|
-
const width = Math.floor(viewport.width * outputScale);
|
|
321
|
-
const height = Math.floor(viewport.height * outputScale);
|
|
322
|
-
canvas.width = width;
|
|
323
|
-
canvas.height = height;
|
|
324
|
-
canvas.style.aspectRatio = `${width}/${height}`;
|
|
325
|
-
canvas.style.width = `calc(${Math.floor(viewport.width) - (trackWidth * 2)}px * var(--evo-pdf-viewer-scale)`;
|
|
326
|
-
// canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
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);
|
|
337
|
-
|
|
338
|
-
pdfPage.render({
|
|
339
|
-
canvas,
|
|
340
|
-
// canvasContext: canvas.getContext('2d') as CanvasRenderingContext2D,
|
|
341
|
-
transform: outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : undefined,
|
|
342
|
-
viewport
|
|
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
|
-
|
|
352
|
-
ScrollContainer.refresh(scrollContainer);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Function to check and update current page
|
|
356
|
-
const updateCurrentPage = () => {
|
|
357
|
-
const canvases = pdfContainer.querySelectorAll<HTMLCanvasElement>('canvas');
|
|
358
|
-
let visiblePage: HTMLCanvasElement | undefined;
|
|
359
|
-
let maxVisibility = 0;
|
|
360
|
-
|
|
361
|
-
canvases.forEach((canvas) => {
|
|
362
|
-
const rect = canvas.getBoundingClientRect();
|
|
363
|
-
const containerRect = scrollContainer.container.getBoundingClientRect();
|
|
364
|
-
|
|
365
|
-
// Calculate intersection height
|
|
366
|
-
const intersectTop = Math.max(rect.top, containerRect.top);
|
|
367
|
-
const intersectBottom = Math.min(rect.bottom, containerRect.bottom);
|
|
368
|
-
const intersectionHeight = Math.max(0, intersectBottom - intersectTop);
|
|
369
|
-
|
|
370
|
-
// Calculate visibility ratio
|
|
371
|
-
const visibility = intersectionHeight / (rect.height || 1);
|
|
372
|
-
|
|
373
|
-
if (visibility > maxVisibility) {
|
|
374
|
-
maxVisibility = visibility;
|
|
375
|
-
visiblePage = canvas;
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
if (visiblePage && maxVisibility >= 0.1) {
|
|
380
|
-
const pageNum = visiblePage.getAttribute('data-page');
|
|
381
|
-
if (pageNum) {
|
|
382
|
-
pageSelect.selectedIndex = parseInt(pageNum, 10) - 1;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
// Update on scroll
|
|
388
|
-
scrollContainer.content.addEventListener('scroll', updateCurrentPage, { passive: true });
|
|
389
|
-
|
|
390
|
-
// Initial update
|
|
391
|
-
updateCurrentPage();
|
|
392
|
-
}
|
|
393
|
-
}
|
|
421
|
+
}
|
|
@@ -24,8 +24,25 @@ const mutationObserver = new MutationObserver((mutations) => {
|
|
|
24
24
|
});
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
export interface IEvoScrollContainerElement {
|
|
28
|
+
content: HTMLElement;
|
|
29
|
+
scrollLeft: number;
|
|
30
|
+
scrollTop: number;
|
|
31
|
+
scrollWidth: number;
|
|
32
|
+
scrollHeight: number;
|
|
33
|
+
configureScrollbar: (settings: ScrollbarSettings) => void;
|
|
34
|
+
getScroll: () => { left: number, top: number };
|
|
35
|
+
resize: (opts: { height?: number }) => void;
|
|
36
|
+
resetScrollbar: () => void;
|
|
37
|
+
refresh: () => void;
|
|
38
|
+
scroll(opts?: ScrollToOptions): void;
|
|
39
|
+
scroll(x: number, y: number): void;
|
|
40
|
+
scrollTo(opts?: ScrollToOptions): void;
|
|
41
|
+
scrollTo(x: number, y: number): void;
|
|
42
|
+
scrollBy(opts?: ScrollToOptions): void;
|
|
43
|
+
scrollBy(x: number, y: number): void;
|
|
44
|
+
}
|
|
45
|
+
export class EvoScrollContainerElement extends HTMLElement implements IEvoScrollContainerElement {
|
|
29
46
|
static get htmlTagName() {
|
|
30
47
|
return 'evo-scroll-container';
|
|
31
48
|
}
|
|
@@ -69,6 +86,10 @@ export class EvoScrollContainerElement extends HTMLElement {
|
|
|
69
86
|
window.removeEventListener('resize', this.#boundResizeHandler);
|
|
70
87
|
}
|
|
71
88
|
|
|
89
|
+
get content() {
|
|
90
|
+
return this.#content;
|
|
91
|
+
}
|
|
92
|
+
|
|
72
93
|
scroll(opts?: ScrollToOptions): void;
|
|
73
94
|
scroll(x: number, y: number): void;
|
|
74
95
|
scroll(...args: any[]) {
|
package/src/components/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as FlipCard } from './FlipCard/FlipCard.astro';
|
|
2
|
+
export { default as RightISI } from './ISI/Right/RightISI.astro';
|
|
2
3
|
export { default as Modal } from './Modal/Modal.astro';
|
|
3
4
|
export { default as ModalOverlay } from './Modal/ModalOverlay.astro';
|
|
4
5
|
export { default as ModalTrigger } from './Modal/ModalTrigger.astro';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export function createPinchListener() {
|
|
2
|
+
type PinchDirection = 'in' | 'out';
|
|
3
|
+
|
|
4
|
+
let initialDist: number = 0;
|
|
5
|
+
let lastDistance: number = 0;
|
|
6
|
+
let currentScale: number = 1;
|
|
7
|
+
let activeScale: number = 1;
|
|
8
|
+
|
|
9
|
+
function getTouchDistance(touches: TouchList) {
|
|
10
|
+
return Math.hypot(touches[0].clientX - touches[1].clientX, touches[0].clientY - touches[1].clientY);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function reset() {
|
|
14
|
+
window.removeEventListener('touchmove', touchmove);
|
|
15
|
+
window.removeEventListener('touchend', touchend);
|
|
16
|
+
currentScale = activeScale;
|
|
17
|
+
initialDist = 0;
|
|
18
|
+
lastDistance = 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const touchstart = (e: TouchEvent) => {
|
|
22
|
+
if (e.touches.length === 2) {
|
|
23
|
+
initialDist = getTouchDistance(e.touches);
|
|
24
|
+
lastDistance = initialDist;
|
|
25
|
+
window.addEventListener('touchmove', touchmove, { passive: true });
|
|
26
|
+
window.addEventListener('touchend', touchend, { passive: true });
|
|
27
|
+
} else {
|
|
28
|
+
reset();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const touchmove = (e: TouchEvent) => {
|
|
33
|
+
if (e.touches.length === 2) {
|
|
34
|
+
const currentDistance = getTouchDistance(e.touches);
|
|
35
|
+
const ratio = currentDistance / initialDist;
|
|
36
|
+
|
|
37
|
+
const rawScale = currentScale * ratio;
|
|
38
|
+
|
|
39
|
+
// Detect if we're crossing 1 from either direction
|
|
40
|
+
const crossingOne =
|
|
41
|
+
(currentScale > 1 && rawScale < 1) ||
|
|
42
|
+
(currentScale < 1 && rawScale > 1);
|
|
43
|
+
|
|
44
|
+
activeScale = crossingOne ? 1 : rawScale; // snap to 1
|
|
45
|
+
|
|
46
|
+
let direction: PinchDirection | undefined = undefined;
|
|
47
|
+
if (currentDistance > lastDistance) {
|
|
48
|
+
direction = 'in';
|
|
49
|
+
} else if (currentDistance < lastDistance) {
|
|
50
|
+
direction = 'out';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (direction !== undefined) {
|
|
54
|
+
console.log(activeScale, direction);
|
|
55
|
+
lastDistance = currentDistance;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const touchend = (e: TouchEvent) => {
|
|
61
|
+
if (e.touches.length < 2) {
|
|
62
|
+
reset();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
window.addEventListener('touchstart', touchstart, { passive: true });
|
|
66
|
+
}
|