feat(dees-pdf-viewer): improve scrolling behavior and styles for better user experience

This commit is contained in:
2025-09-20 22:03:47 +00:00
parent a95d5a96a0
commit 352fe79791
2 changed files with 58 additions and 13 deletions

View File

@@ -528,10 +528,16 @@ export class DeesPdfViewer extends DeesElement {
const isBelow = thumbnailRect.bottom > sidebarRect.bottom; const isBelow = thumbnailRect.bottom > sidebarRect.bottom;
if (isAbove || isBelow) { if (isAbove || isBelow) {
// Scroll the thumbnail into view, centering it if possible // Calculate the scroll position to center the thumbnail
thumbnail.scrollIntoView({ const thumbnailOffset = thumbnail.offsetTop;
behavior: 'smooth', const thumbnailHeight = thumbnail.offsetHeight;
block: 'center' const sidebarHeight = sidebarContent.clientHeight;
const targetScrollTop = thumbnailOffset - (sidebarHeight / 2) + (thumbnailHeight / 2);
// Scroll the sidebar to center the thumbnail
sidebarContent.scrollTo({
top: Math.max(0, targetScrollTop),
behavior: 'smooth'
}); });
} }
} }
@@ -541,10 +547,23 @@ export class DeesPdfViewer extends DeesElement {
await this.updateComplete; await this.updateComplete;
const pageWrapper = this.shadowRoot?.querySelector(`.page-wrapper[data-page="${pageNum}"]`) as HTMLElement; const pageWrapper = this.shadowRoot?.querySelector(`.page-wrapper[data-page="${pageNum}"]`) as HTMLElement;
if (pageWrapper && this.viewerMain) { if (pageWrapper && this.viewerMain) {
pageWrapper.scrollIntoView({ // Calculate the offset of the page wrapper relative to the viewer
behavior: smooth ? 'smooth' : 'auto', const pageRect = pageWrapper.getBoundingClientRect();
block: 'start' const viewerRect = this.viewerMain.getBoundingClientRect();
}); const currentScrollTop = this.viewerMain.scrollTop;
// Calculate the target scroll position
const targetScrollTop = currentScrollTop + (pageRect.top - viewerRect.top) - this.viewerMain.clientTop;
// Scroll to the calculated position
if (smooth) {
this.viewerMain.scrollTo({
top: targetScrollTop,
behavior: 'smooth'
});
} else {
this.viewerMain.scrollTop = targetScrollTop;
}
// Update current page // Update current page
this.currentPage = pageNum; this.currentPage = pageNum;
@@ -661,11 +680,6 @@ export class DeesPdfViewer extends DeesElement {
} }
} }
private async goToPage(pageNum: number) {
if (pageNum >= 1 && pageNum <= this.totalPages) {
await this.scrollToPage(pageNum);
}
}
private handleThumbnailClick(e: Event) { private handleThumbnailClick(e: Event) {
const target = e.currentTarget as HTMLElement; const target = e.currentTarget as HTMLElement;
@@ -809,6 +823,28 @@ export class DeesPdfViewer extends DeesElement {
}); });
this.resizeObserver.observe(this.viewerMain); this.resizeObserver.observe(this.viewerMain);
this.measureViewportDimensions(); this.measureViewportDimensions();
// Prevent scroll propagation to parent when scrolling inside viewer
this.viewerMain.addEventListener('wheel', (e) => {
const element = e.currentTarget as HTMLElement;
const scrollTop = element.scrollTop;
const scrollHeight = element.scrollHeight;
const clientHeight = element.clientHeight;
const deltaY = e.deltaY;
// Check if we're at the boundaries
const isAtTop = scrollTop === 0;
const isAtBottom = Math.abs(scrollTop + clientHeight - scrollHeight) < 1;
// Prevent propagation if we're scrolling within bounds
if ((deltaY < 0 && !isAtTop) || (deltaY > 0 && !isAtBottom)) {
e.stopPropagation();
} else if ((deltaY < 0 && isAtTop) || (deltaY > 0 && isAtBottom)) {
// Prevent default and propagation when at boundaries
e.preventDefault();
e.stopPropagation();
}
}, { passive: false });
} }
} }

View File

@@ -9,6 +9,7 @@ export const viewerStyles = [
height: 600px; height: 600px;
position: relative; position: relative;
font-family: 'Geist Sans', sans-serif; font-family: 'Geist Sans', sans-serif;
contain: layout style;
} }
.pdf-viewer { .pdf-viewer {
@@ -17,6 +18,8 @@ export const viewerStyles = [
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(215 20% 10%)')}; background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(215 20% 10%)')};
position: relative;
overflow: hidden;
} }
.toolbar { .toolbar {
@@ -109,6 +112,7 @@ export const viewerStyles = [
display: flex; display: flex;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
min-height: 0;
} }
.sidebar { .sidebar {
@@ -160,6 +164,8 @@ export const viewerStyles = [
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
overscroll-behavior: contain;
min-height: 0;
} }
.thumbnail { .thumbnail {
@@ -210,6 +216,9 @@ export const viewerStyles = [
overflow-x: hidden; overflow-x: hidden;
padding: 20px; padding: 20px;
scroll-behavior: smooth; scroll-behavior: smooth;
overscroll-behavior: contain;
min-height: 0;
position: relative;
} }
.loading-container { .loading-container {