1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-01-12 04:23:09 +02:00

improving statistic design #654

This commit is contained in:
Patrik J. Braun 2023-04-13 18:29:27 +02:00
parent fdea2b570f
commit 5ff243310e
3 changed files with 105 additions and 88 deletions

View File

@ -5,10 +5,13 @@
<div class="col-md-1-half col-12 d-table">
</div>
<div class="col-md-9 col-12 date-frequency" *ngIf="showStatistic">
<ng-container *ngFor="let freq of MediaCountOverTime; count as length">
<ng-container *ngFor="let freq of filterService.statistic; count as length">
<div class="d-inline-block date-frequency-column"
[style.width.%]="(1/length)*100"
[title]="(freq.date | date: 'medium') + ' (' + freq.count+')'">
container="body"
triggers="mouseenter:mouseleave"
placement="bottom start"
[popover]="(freq.date | date: 'medium') + ' (' + freq.count+')'">
<div class=" text-center " [style.height.%]="100-(freq.count/freq.max)*100">
<div class="d-none d-lg-block" *ngIf="freq.count < freq.max/2">{{freq.count}}</div>
</div>
@ -18,7 +21,7 @@
[style.height.%]="(freq.count/freq.max)*100">
<div class="d-none d-lg-block" *ngIf="freq.count >= freq.max/2">{{freq.count}}</div>
</div>
<div class="text-center d-none d-md-block">
<div class="text-center d-none d-xl-block overflow-x-hidden text-nowrap ps-1 pe-1">
{{freq.date | date: freq.dateStr}}
</div>
</div>
@ -28,8 +31,8 @@
</div>
<div class="row">
<div class="position-absolute">
<div class="text-center">
<div class="position-absolute" *ngIf="filterService.statistic.length>1">
<div class="text-md-center">
<span
[class.oi-chevron-bottom]="showStatistic"
[class.oi-chevron-top]="!showStatistic"

View File

@ -54,87 +54,6 @@ export class GalleryFilterComponent implements OnInit, OnDestroy {
return this.filterService.activeFilters.value;
}
get MediaCountOverTime(): { date: Date, endDate: Date, dateStr: string, count: number, max: number }[] {
if (!this.filterService.prefiltered ||
!this.filterService.prefiltered.media ||
this.filterService.prefiltered.media.length === 0) {
return [];
}
const ret: { date: Date, endDate: Date, dateStr: string, count: number, max: number }[] = [];
const diff = (this.ActiveFilters.dateFilter.maxDate - this.ActiveFilters.dateFilter.minDate) / 1000;
const H = 60 * 60;
const D = H * 24;
const M = D * 30;
const Y = D * 365;
const Dec = Y * 10;
const Sen = Y * 100;
const divs = [H, D, M, Y, Dec, Sen];
const startMediaTime = this.filterService.prefiltered.media.reduce((p, c) => p.metadata.creationDate < c.metadata.creationDate ? p : c).metadata.creationDate;
// finding the resolution
let usedDiv = H;
for (let i = 0; i < divs.length; ++i) {
if (diff / divs[i] < 15) {
usedDiv = divs[i];
break;
}
}
// getting the first date (truncated to the resolution)
let startMediaDate = new Date(startMediaTime);
if (usedDiv >= Y) {
const fy = (new Date(startMediaTime).getFullYear());
startMediaDate = new Date(fy - fy % (usedDiv / Y), 0, 1);
} else if (usedDiv === M) {
startMediaDate = new Date(startMediaDate.getFullYear(), startMediaDate.getMonth(), 1);
} else {
startMediaDate = new Date(startMediaTime - startMediaTime % usedDiv);
}
this.filterService.prefiltered.media.forEach(m => {
const key = Math.floor((m.metadata.creationDate - startMediaTime) / 1000 / usedDiv);
const getDate = (index: number) => {
let d: Date;
if (usedDiv >= Y) {
d = new Date(startMediaDate.getFullYear() + (index * (usedDiv / Y)), 0, 1);
} else if (usedDiv === M) {
d = new Date(startMediaDate.getFullYear(), startMediaDate.getMonth() + index, 1);
} else if (usedDiv === D) {
d = new Date(startMediaDate.getFullYear(), startMediaDate.getMonth(), startMediaDate.getDate() + index, 1);
} else {
d = (new Date(startMediaDate.getTime() + (index * usedDiv * 1000)));
}
return d;
};
// extending the array
while (ret.length <= key) {
let dStr: string;
// getting date range start for entry and also UI date pattern
if (usedDiv >= Y) {
dStr = 'yyyy';
} else if (usedDiv === M) {
dStr = 'MMM';
} else if (usedDiv === D) {
dStr = 'EEE';
} else {
dStr = 'HH';
}
ret.push({date: getDate(ret.length), endDate: getDate(ret.length + 1), dateStr: dStr, count: 0, max: 0});
}
ret[key].count++;
});
// don't show if there is only one column
if (ret.length <= 1) {
return [];
}
const max = ret.reduce((p, c) => Math.max(p, c.count), 0);
ret.forEach(v => v.max = max);
return ret;
}
ngOnDestroy(): void {
setTimeout(() => this.filterService.setShowingFilters(false));

View File

@ -120,15 +120,110 @@ export class FilterService {
},
],
});
public prefiltered: DirectoryContent;
public statistic: { date: Date; endDate: Date; dateStr: string; count: number; max: number; }[] = [];
private getStatistic(prefiltered: DirectoryContent): { date: Date, endDate: Date, dateStr: string, count: number, max: number }[] {
if (!prefiltered ||
!prefiltered.media ||
prefiltered.media.length === 0) {
return [];
}
const ret: { date: Date, endDate: Date, dateStr: string, count: number, max: number }[] = [];
const minDate = prefiltered.media.reduce(
(p, curr) => Math.min(p, curr.metadata.creationDate),
Number.MAX_VALUE - 1
);
const maxDate = prefiltered.media.reduce(
(p, curr) => Math.max(p, curr.metadata.creationDate),
Number.MIN_VALUE + 1
);
const diff = (maxDate - minDate) / 1000;
const H = 60 * 60;
const D = H * 24;
const M = D * 30;
const Y = D * 365;
const Y2 = Y * 2;
const Y5 = Y * 5;
const Dec = Y * 10;
const Sen = Y * 100;
const divs = [H, D, M, Y, Y2, Y5, Dec, Sen];
// finding the resolution
let usedDiv = H;
for (let i = 0; i < divs.length; ++i) {
if (diff / divs[i] < 26) {
usedDiv = divs[i];
break;
}
}
// getting the first date (truncated to the resolution)
const floorDate = (ts: number): number => {
let d = new Date(ts);
if (usedDiv >= Y) {
const fy = (d.getFullYear());
d = new Date(fy - fy % (usedDiv / Y), 0, 1);
} else if (usedDiv === M) {
d = new Date(d.getFullYear(), d.getMonth(), 1);
} else {
d = new Date(ts - ts % usedDiv);
}
return d.getTime();
};
const startMediaDate = new Date(floorDate(minDate));
prefiltered.media.forEach(m => {
const key = Math.floor((floorDate(m.metadata.creationDate) - startMediaDate.getTime()) / 1000 / usedDiv);
const getDate = (index: number) => {
let d: Date;
if (usedDiv >= Y) {
d = new Date(startMediaDate.getFullYear() + (index * (usedDiv / Y)), 0, 1);
} else if (usedDiv === M) {
d = new Date(startMediaDate.getFullYear(), startMediaDate.getMonth() + index, 1);
} else if (usedDiv === D) {
d = new Date(startMediaDate.getFullYear(), startMediaDate.getMonth(), startMediaDate.getDate() + index, 1);
} else {
d = (new Date(startMediaDate.getTime() + (index * usedDiv * 1000)));
}
return d;
};
// extending the array
while (ret.length <= key) {
let dStr: string;
// getting date range start for entry and also UI date pattern
if (usedDiv >= Y) {
dStr = 'y';
} else if (usedDiv === M) {
dStr = 'y MMM';
} else if (usedDiv === D) {
dStr = 'EEE';
} else {
dStr = 'HH';
}
ret.push({date: getDate(ret.length), endDate: getDate(ret.length + 1), dateStr: dStr, count: 0, max: 0});
}
ret[key].count++;
});
// don't show if there is only one column
if (ret.length <= 1) {
return [];
}
const max = ret.reduce((p, c) => Math.max(p, c.count), 0);
ret.forEach(v => v.max = max);
return ret;
}
public applyFilters(
directoryContent: Observable<DirectoryContent>
): Observable<DirectoryContent> {
return directoryContent.pipe(
switchMap((dirContent: DirectoryContent) => {
this.prefiltered = dirContent;
this.statistic = this.getStatistic(dirContent);
this.resetFilters(false);
return this.activeFilters.pipe(
map((afilters) => {