first commit
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { ChangeDetectionStrategy, Component, ElementRef, QueryList, ViewChild, ViewChildren, inject, signal, effect } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
|
||||
import { ThemesService } from '../../services/themes.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-themes-nav',
|
||||
standalone: true,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './themes-nav.component.html'
|
||||
})
|
||||
export class ThemesNavComponent {
|
||||
private router = inject(Router);
|
||||
private route = inject(ActivatedRoute);
|
||||
themesSvc = inject(ThemesService);
|
||||
|
||||
themes = () => this.themesSvc.themes();
|
||||
activeSlug = this.themesSvc.activeSlug;
|
||||
displayedThemes = () => this.themes();
|
||||
|
||||
@ViewChildren('pill', { read: ElementRef }) pills!: QueryList<ElementRef<HTMLButtonElement>>;
|
||||
@ViewChild('scroll', { static: false }) scrollRef?: ElementRef<HTMLDivElement>;
|
||||
|
||||
constructor() {
|
||||
// Reflect current URL into active slug
|
||||
this.router.events.subscribe((e) => {
|
||||
if (e instanceof NavigationEnd) this.syncFromUrl();
|
||||
});
|
||||
// Also on init
|
||||
setTimeout(() => this.syncFromUrl(), 0);
|
||||
}
|
||||
|
||||
private syncFromUrl() {
|
||||
const url = this.router.url || '';
|
||||
const m = url.match(/#?\/?(?:p\/[^/]+\/)?t\/([^/?#]+)/i);
|
||||
if (m && m[1]) this.activeSlug.set(m[1]);
|
||||
}
|
||||
|
||||
goToTheme(slug: string) {
|
||||
this.activeSlug.set(slug);
|
||||
this.router.navigate(['/t', slug]);
|
||||
}
|
||||
|
||||
onKeydown(ev: KeyboardEvent, index: number) {
|
||||
const list = this.pills?.toArray() || [];
|
||||
if (!list.length) return;
|
||||
if (ev.key === 'ArrowRight') {
|
||||
ev.preventDefault();
|
||||
const next = list[(index + 1) % list.length];
|
||||
next?.nativeElement?.focus();
|
||||
next?.nativeElement?.scrollIntoView({ inline: 'center', behavior: 'smooth', block: 'nearest' });
|
||||
} else if (ev.key === 'ArrowLeft') {
|
||||
ev.preventDefault();
|
||||
const prev = list[(index - 1 + list.length) % list.length];
|
||||
prev?.nativeElement?.focus();
|
||||
prev?.nativeElement?.scrollIntoView({ inline: 'center', behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
scrollBy(px: number) {
|
||||
const el = this.scrollRef?.nativeElement;
|
||||
if (!el) return;
|
||||
el.scrollBy({ left: px, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
onWheel(ev: WheelEvent) {
|
||||
const el = this.scrollRef?.nativeElement;
|
||||
if (!el) return;
|
||||
// Horizontal scroll with mouse wheel while hovering the bar
|
||||
if (Math.abs(ev.deltaY) > Math.abs(ev.deltaX)) {
|
||||
ev.preventDefault();
|
||||
el.scrollLeft += ev.deltaY;
|
||||
}
|
||||
}
|
||||
|
||||
// Theme helpers (read from document attribute/localStorage)
|
||||
private getCurrentTheme(): string {
|
||||
try {
|
||||
const fromAttr = document.documentElement.getAttribute('data-theme');
|
||||
if (fromAttr) return fromAttr;
|
||||
const fromStorage = localStorage.getItem('newtube.theme');
|
||||
const val = fromStorage || 'system';
|
||||
if (val === 'system') {
|
||||
// Resolve to light/dark using media query so styles can be deterministic
|
||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
return prefersDark ? 'dark' : 'light';
|
||||
}
|
||||
return val;
|
||||
} catch {
|
||||
// Safe fallback: prefer dark for safety in most UIs
|
||||
const prefersDark = typeof window !== 'undefined' && (window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false);
|
||||
return prefersDark ? 'dark' : 'light';
|
||||
}
|
||||
}
|
||||
|
||||
isLightTheme(): boolean {
|
||||
const t = this.getCurrentTheme();
|
||||
return t === 'light' || t === 'system';
|
||||
}
|
||||
isDarkTheme(): boolean { return this.getCurrentTheme() === 'dark'; }
|
||||
isBlackTheme(): boolean { return this.getCurrentTheme() === 'black'; }
|
||||
isBlueTheme(): boolean { return this.getCurrentTheme() === 'blue'; }
|
||||
}
|
||||
Reference in New Issue
Block a user