feat(web): responsive date group header height (#17944)

* feat: responsive date group header height

* update tests

* feat(web): improve perf when changing mobile orientation (#17945)

fix: improve perf when changing mobile orientation
This commit is contained in:
Min Idzelis 2025-04-29 13:59:06 -04:00 committed by GitHub
parent 07290580a6
commit 0e4cf9ac57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 55 additions and 20 deletions

View File

@ -131,7 +131,7 @@
>
<!-- Date group title -->
<div
class="flex z-[100] pt-[calc(1.75rem+1px)] pb-5 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm"
class="flex z-[100] pt-7 pb-5 max-md:pt-5 max-md:pb-3 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm"
style:width={dateGroup.width + 'px'}
>
{#if !singleSelect && ((hoveredDateGroup === dateGroup.groupTitle && isMouseOverGroup) || assetInteraction.selectedGroup.has(dateGroup.groupTitle))}

View File

@ -88,7 +88,16 @@
const usingMobileDevice = $derived(mobileDevice.pointerCoarse);
$effect(() => {
assetStore.rowHeight = maxMd ? 100 : 235;
const layoutOptions = maxMd
? {
rowHeight: 100,
headerHeight: 32,
}
: {
rowHeight: 235,
headerHeight: 48,
};
assetStore.setLayoutOptions(layoutOptions);
});
const scrollTo = (top: number) => {

View File

@ -9,7 +9,7 @@
<div class="overflow-clip" style:height={height + 'px'}>
<div
class="flex z-[100] pt-[calc(1.75rem+1px)] pb-5 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm"
class="flex z-[100] pt-7 pb-5 h-6 place-items-center text-xs font-medium text-immich-fg bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg md:text-sm"
>
{title}
</div>

View File

@ -48,15 +48,15 @@ describe('AssetStore', () => {
expect(plainBuckets).toEqual(
expect.arrayContaining([
expect.objectContaining({ bucketDate: '2024-03-01T00:00:00.000Z', bucketHeight: 304 }),
expect.objectContaining({ bucketDate: '2024-02-01T00:00:00.000Z', bucketHeight: 4515.333_333_333_333 }),
expect.objectContaining({ bucketDate: '2024-03-01T00:00:00.000Z', bucketHeight: 303 }),
expect.objectContaining({ bucketDate: '2024-02-01T00:00:00.000Z', bucketHeight: 4514.333_333_333_333 }),
expect.objectContaining({ bucketDate: '2024-01-01T00:00:00.000Z', bucketHeight: 286 }),
]),
);
});
it('calculates timeline height', () => {
expect(assetStore.timelineHeight).toBe(5105.333_333_333_333);
expect(assetStore.timelineHeight).toBe(5103.333_333_333_333);
});
});

View File

@ -35,9 +35,7 @@ export type AssetStoreOptions = Omit<AssetApiGetTimeBucketsRequest, 'size'> & {
timelineAlbumId?: string;
deferInit?: boolean;
};
export type AssetStoreLayoutOptions = {
rowHeight: number;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function updateObject(target: any, source: any): boolean {
if (!target) {
@ -110,7 +108,6 @@ export class AssetDateGroup {
readonly date: DateTime;
readonly dayOfMonth: number;
intersetingAssets: IntersectingAsset[] = $state([]);
dodo: IntersectingAsset[] = $state([]);
height = $state(0);
width = $state(0);
@ -121,6 +118,7 @@ export class AssetDateGroup {
left: number = $state(0);
row = $state(0);
col = $state(0);
deferredLayout = false;
constructor(bucket: AssetBucket, index: number, date: DateTime, dayOfMonth: number) {
this.index = index;
@ -195,6 +193,10 @@ export class AssetDateGroup {
}
layout(options: CommonLayoutOptions) {
if (!this.bucket.intersecting) {
this.deferredLayout = true;
return;
}
const assets = this.intersetingAssets.map((intersetingAsset) => intersetingAsset.asset!);
const geometry = getJustifiedLayoutFromAssets(assets, options);
this.width = geometry.containerWidth;
@ -547,6 +549,11 @@ export type LiteBucket = {
bucketDateFormattted: string;
};
type AssetStoreLayoutOptions = {
rowHeight?: number;
headerHeight?: number;
gap?: number;
};
export class AssetStore {
// --- public ----
isInitialized = $state(false);
@ -596,7 +603,7 @@ export class AssetStore {
#unsubscribers: Unsubscriber[] = [];
#rowHeight = $state(235);
#headerHeight = $state(49);
#headerHeight = $state(48);
#gap = $state(12);
#options: AssetStoreOptions = AssetStore.#INIT_OPTIONS;
@ -608,36 +615,46 @@ export class AssetStore {
constructor() {}
set headerHeight(value) {
setLayoutOptions({ headerHeight = 48, rowHeight = 235, gap = 12 }: AssetStoreLayoutOptions) {
let changed = false;
changed ||= this.#setHeaderHeight(headerHeight);
changed ||= this.#setGap(gap);
changed ||= this.#setRowHeight(rowHeight);
if (changed) {
this.refreshLayout();
}
}
#setHeaderHeight(value: number) {
if (this.#headerHeight == value) {
return;
return false;
}
this.#headerHeight = value;
this.refreshLayout();
return true;
}
get headerHeight() {
return this.#headerHeight;
}
set gap(value) {
#setGap(value: number) {
if (this.#gap == value) {
return;
return false;
}
this.#gap = value;
this.refreshLayout();
return true;
}
get gap() {
return this.#gap;
}
set rowHeight(value) {
#setRowHeight(value: number) {
if (this.#rowHeight == value) {
return;
return false;
}
this.#rowHeight = value;
this.refreshLayout();
return true;
}
get rowHeight() {
@ -815,6 +832,15 @@ export class AssetStore {
}
bucket.intersecting = actuallyIntersecting || preIntersecting;
bucket.actuallyIntersecting = actuallyIntersecting;
if (preIntersecting || actuallyIntersecting) {
const hasDeferred = bucket.dateGroups.some((group) => group.deferredLayout);
if (hasDeferred) {
this.#updateGeometry(bucket, true);
for (const group of bucket.dateGroups) {
group.deferredLayout = false;
}
}
}
}
#processPendingChanges = throttle(() => {