feat(reader): improve cbx reader mobile header and slideshow indicator

This commit is contained in:
ACX
2026-02-10 22:38:15 -07:00
committed by acx10
parent 574bfdbe7a
commit aac8246bcf
6 changed files with 112 additions and 27 deletions

View File

@@ -33,9 +33,7 @@
}
@if (isSlideshowActive) {
<div class="slideshow-indicator">
<span class="slideshow-badge">Slideshow</span>
</div>
<div class="slideshow-progress" [class.above-footer]="isFooterVisible" [style.animation-duration.ms]="slideshowInterval"></div>
}
<div class="image-container"

View File

@@ -28,33 +28,26 @@
}
}
// Slideshow indicator
.slideshow-indicator {
// Slideshow progress bar
.slideshow-progress {
position: absolute;
top: 60px;
left: 50%;
transform: translateX(-50%);
z-index: 11;
bottom: 0;
left: 0;
height: 3px;
background: #4a90e2;
z-index: 1001;
pointer-events: none;
animation: slideshow-fill linear infinite;
transition: bottom 0.3s ease;
.slideshow-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: rgba(74, 144, 226, 0.9);
border-radius: 16px;
font-size: 12px;
font-weight: 500;
color: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
animation: pulse 2s ease-in-out infinite;
&.above-footer {
bottom: 52px;
}
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
@keyframes slideshow-fill {
from { width: 0; }
to { width: 100%; }
}
.bookmark-indicator {
@@ -550,6 +543,10 @@
@media (max-width: 768px) {
.comic-reader-container {
.slideshow-progress.above-footer {
bottom: 48px;
}
.image-container {
top: 0;
bottom: 0;

View File

@@ -94,6 +94,9 @@ export class CbxReaderComponent implements OnInit, OnDestroy {
noteDialogData: CbxNoteDialogData | null = null;
private editingNote: BookNoteV2 | null = null;
// Footer visibility (for slideshow progress bar positioning)
isFooterVisible = false;
// Fullscreen state
isFullscreen = false;
@@ -149,6 +152,10 @@ export class CbxReaderComponent implements OnInit, OnDestroy {
this.footerService.setForceVisible(state.footerVisible);
});
this.footerService.forceVisible$
.pipe(takeUntil(this.destroy$))
.subscribe(visible => this.isFooterVisible = visible);
this.subscribeToHeaderEvents();
this.subscribeToSidebarEvents();
this.subscribeToFooterEvents();

View File

@@ -15,15 +15,37 @@
</div>
<span class="book-title">{{ bookTitle }}</span>
<div class="header-right">
<button class="icon-btn" (click)="onToggleSlideshow()" [title]="state.isSlideshowActive ? 'Stop Slideshow (P)' : 'Start Slideshow (P)'" [class.active]="state.isSlideshowActive">
<button class="icon-btn desktop-only" (click)="onToggleSlideshow()" [title]="state.isSlideshowActive ? 'Stop Slideshow (P)' : 'Start Slideshow (P)'" [class.active]="state.isSlideshowActive">
<app-reader-icon [name]="state.isSlideshowActive ? 'pause' : 'play'" [size]="20" />
</button>
<button class="icon-btn" (click)="onToggleFullscreen()" [title]="state.isFullscreen ? 'Exit Fullscreen (F)' : 'Fullscreen (F)'">
<button class="icon-btn desktop-only" (click)="onToggleFullscreen()" [title]="state.isFullscreen ? 'Exit Fullscreen (F)' : 'Fullscreen (F)'">
<app-reader-icon [name]="state.isFullscreen ? 'fullscreen-exit' : 'fullscreen'" [size]="20" />
</button>
<button class="icon-btn" (click)="onShowShortcutsHelp()" title="Keyboard Shortcuts (?)">
<button class="icon-btn desktop-only" (click)="onShowShortcutsHelp()" title="Keyboard Shortcuts (?)">
<app-reader-icon name="help" [size]="20" />
</button>
<div class="overflow-menu mobile-only">
<button class="icon-btn" (click)="overflowOpen = !overflowOpen; $event.stopPropagation()" title="More">
<app-reader-icon name="dots-vertical" [size]="20" />
</button>
@if (overflowOpen) {
<div class="overflow-backdrop" (click)="overflowOpen = false"></div>
<div class="overflow-dropdown">
<button class="overflow-item" (click)="onToggleSlideshow(); overflowOpen = false" [class.active]="state.isSlideshowActive">
<app-reader-icon [name]="state.isSlideshowActive ? 'pause' : 'play'" [size]="18" />
<span>{{ state.isSlideshowActive ? 'Stop Slideshow' : 'Start Slideshow' }}</span>
</button>
<button class="overflow-item" (click)="onToggleFullscreen(); overflowOpen = false">
<app-reader-icon [name]="state.isFullscreen ? 'fullscreen-exit' : 'fullscreen'" [size]="18" />
<span>{{ state.isFullscreen ? 'Exit Fullscreen' : 'Fullscreen' }}</span>
</button>
<button class="overflow-item" (click)="onShowShortcutsHelp(); overflowOpen = false">
<app-reader-icon name="help" [size]="18" />
<span>Keyboard Shortcuts</span>
</button>
</div>
}
</div>
<button class="icon-btn" (click)="onOpenSettings()" title="Settings">
<app-reader-icon name="settings" [size]="20" />
</button>

View File

@@ -105,6 +105,58 @@
overflow: hidden;
text-overflow: ellipsis;
}
.mobile-only {
display: none;
}
.overflow-menu {
position: relative;
}
.overflow-backdrop {
position: fixed;
inset: 0;
z-index: 999;
}
.overflow-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 4px;
background: #2d2d2d;
border: 1px solid #404040;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
z-index: 1000;
min-width: 180px;
padding: 4px;
}
.overflow-item {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 8px 12px;
background: none;
border: none;
color: #ffffff;
font-size: 13px;
cursor: pointer;
border-radius: 6px;
white-space: nowrap;
&:hover {
background: rgba(255, 255, 255, 0.12);
}
&.active {
color: #ef4444;
background: rgba(239, 68, 68, 0.15);
}
}
}
@media (max-width: 600px) {
@@ -123,5 +175,13 @@
.book-title {
font-size: 12px;
}
.desktop-only {
display: none;
}
.mobile-only {
display: block;
}
}
}

View File

@@ -20,6 +20,7 @@ export class CbxHeaderComponent implements OnInit, OnDestroy {
@Input() currentPageHasNotes = false;
isVisible = true;
overflowOpen = false;
state: CbxHeaderState = {
isFullscreen: false,
isSlideshowActive: false