Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 6x 7x 6x 6x 6x 6x 6x 1x 1x | import { PictureLightboxService } from '../../../../services/pictures/picture-lightbox.service';
import { PictureRowComponent } from '../../components/picture-row/picture-row.component';
import { PictureSearchBarComponent } from '../../components/picture-search-bar/picture-search-bar.component';
import { PictureStateService } from '../../services/picture-state.service';
import { isPlatformBrowser } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
DestroyRef,
effect,
ElementRef,
inject,
OnInit,
PLATFORM_ID,
viewChild,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { LoggerService } from '@drevo-web/core';
import { Picture } from '@drevo-web/shared';
import {
MODAL_DATA,
ModalData,
SpinnerComponent,
VirtualScrollerComponent,
VirtualScrollerItemDirective,
} from '@drevo-web/ui';
import { debounceTime, Subject } from 'rxjs';
@Component({
selector: 'app-pictures-page',
imports: [
PictureSearchBarComponent,
PictureRowComponent,
SpinnerComponent,
VirtualScrollerComponent,
VirtualScrollerItemDirective,
],
providers: [PictureStateService],
templateUrl: './picture-page.component.html',
styleUrl: './picture-page.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PicturePageComponent implements OnInit {
private readonly state = inject(PictureStateService);
private readonly lightboxService = inject(PictureLightboxService);
private readonly destroyRef = inject(DestroyRef);
private readonly logger = inject(LoggerService).withContext('PicturesComponent');
private readonly modalData = inject<ModalData<undefined, string>>(MODAL_DATA, { optional: true });
private readonly platformId = inject(PLATFORM_ID);
private readonly resizeSubject = new Subject<number>();
private resizeObserver: ResizeObserver | undefined;
private readonly galleryContainer = viewChild<ElementRef<HTMLElement>>('galleryContainer');
readonly isSelectMode = !!this.modalData;
readonly isLoading = this.state.isLoading;
readonly isLoadingMore = this.state.isLoadingMore;
readonly rows = this.state.rows;
readonly totalRows = this.state.totalRows;
readonly hasResults = this.state.hasResults;
readonly showNoResults = this.state.showNoResults;
readonly trackByFn = this.state.trackByFn;
constructor() {
this.destroyRef.onDestroy(() => {
this.resizeObserver?.disconnect();
});
// React to gallery container appearing/disappearing in DOM (inside @if)
effect(() => {
const container = this.galleryContainer();
this.resizeObserver?.disconnect();
Iif (container) {
this.observeResize(container.nativeElement);
}
});
}
ngOnInit(): void {
this.resizeSubject
.pipe(debounceTime(150), takeUntilDestroyed(this.destroyRef))
.subscribe(width => this.state.onContainerResize(width));
this.state.init();
}
onSearchChange(value: string): void {
this.state.onSearchChange(value);
}
onLoadMore(): void {
this.state.loadMore();
}
onPictureClick(picture: Picture): void {
if (this.isSelectMode) {
this.logger.info('Picture selected', { id: picture.id });
this.modalData?.close(`@${picture.id}@`);
} else {
this.logger.info('Opening lightbox', { id: picture.id });
this.lightboxService.open(picture.id);
}
}
private observeResize(element: HTMLElement): void {
if (!isPlatformBrowser(this.platformId)) {
return;
}
// Observe the CDK viewport element — its contentRect.width excludes the scrollbar
const viewport = element.querySelector('cdk-virtual-scroll-viewport') ?? element;
this.resizeObserver = new ResizeObserver(entries => {
const entry = entries[0];
if (entry) {
this.resizeSubject.next(entry.contentRect.width);
}
});
this.resizeObserver.observe(viewport);
}
}
|