mirror of
https://github.com/xuthus83/pigallery2.git
synced 2025-01-14 14:43:17 +08:00
improving statistic design #654
This commit is contained in:
parent
fdea2b570f
commit
5ff243310e
@ -5,10 +5,13 @@
|
|||||||
<div class="col-md-1-half col-12 d-table">
|
<div class="col-md-1-half col-12 d-table">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9 col-12 date-frequency" *ngIf="showStatistic">
|
<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"
|
<div class="d-inline-block date-frequency-column"
|
||||||
[style.width.%]="(1/length)*100"
|
[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=" 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 class="d-none d-lg-block" *ngIf="freq.count < freq.max/2">{{freq.count}}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -18,7 +21,7 @@
|
|||||||
[style.height.%]="(freq.count/freq.max)*100">
|
[style.height.%]="(freq.count/freq.max)*100">
|
||||||
<div class="d-none d-lg-block" *ngIf="freq.count >= freq.max/2">{{freq.count}}</div>
|
<div class="d-none d-lg-block" *ngIf="freq.count >= freq.max/2">{{freq.count}}</div>
|
||||||
</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}}
|
{{freq.date | date: freq.dateStr}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -28,8 +31,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="position-absolute">
|
<div class="position-absolute" *ngIf="filterService.statistic.length>1">
|
||||||
<div class="text-center">
|
<div class="text-md-center">
|
||||||
<span
|
<span
|
||||||
[class.oi-chevron-bottom]="showStatistic"
|
[class.oi-chevron-bottom]="showStatistic"
|
||||||
[class.oi-chevron-top]="!showStatistic"
|
[class.oi-chevron-top]="!showStatistic"
|
||||||
|
@ -54,87 +54,6 @@ export class GalleryFilterComponent implements OnInit, OnDestroy {
|
|||||||
return this.filterService.activeFilters.value;
|
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 {
|
ngOnDestroy(): void {
|
||||||
setTimeout(() => this.filterService.setShowingFilters(false));
|
setTimeout(() => this.filterService.setShowingFilters(false));
|
||||||
|
@ -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(
|
public applyFilters(
|
||||||
directoryContent: Observable<DirectoryContent>
|
directoryContent: Observable<DirectoryContent>
|
||||||
): Observable<DirectoryContent> {
|
): Observable<DirectoryContent> {
|
||||||
return directoryContent.pipe(
|
return directoryContent.pipe(
|
||||||
switchMap((dirContent: DirectoryContent) => {
|
switchMap((dirContent: DirectoryContent) => {
|
||||||
this.prefiltered = dirContent;
|
this.statistic = this.getStatistic(dirContent);
|
||||||
this.resetFilters(false);
|
this.resetFilters(false);
|
||||||
return this.activeFilters.pipe(
|
return this.activeFilters.pipe(
|
||||||
map((afilters) => {
|
map((afilters) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user