@obliczeniowo/elementary
Version:
Library made in Angular version 20
354 lines (347 loc) • 24.7 kB
JavaScript
import * as i0 from '@angular/core';
import { input, EventEmitter, effect, computed, HostBinding, HostListener, Output, Input, Component, LOCALE_ID, ContentChild, Inject, NgModule } from '@angular/core';
import { lastValueFrom, map } from 'rxjs';
import { keys } from '@obliczeniowo/elementary/objects';
import * as i1 from '@obliczeniowo/elementary/loading';
import { LoadingModule } from '@obliczeniowo/elementary/loading';
import { ElementaryMath } from '@obliczeniowo/elementary/math';
import { Dates } from '@obliczeniowo/elementary/classes';
import * as i1$1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i2 from '@obliczeniowo/elementary/year-picker';
import { YearPickerModule } from '@obliczeniowo/elementary/year-picker';
class DailyStatisticComponent {
loading = false;
selected = false;
/** day statistic day */
statistics = input.required();
/** */
settings = input.required();
get x() {
return this.statistics()?.x ?? 0;
}
get y() {
return this.statistics()?.y ?? 0;
}
clicked = new EventEmitter();
async onClick() {
if (this.loading) {
return;
}
const { date, count, data } = this.statistics();
const settings = this.settings();
if (!data && settings.getExtData) {
this.loading = true;
const data = await lastValueFrom(settings.getExtData(this.statistics()));
this.loading = false;
if (data) {
this.statistics().data = data;
}
}
this.clicked.emit({ date, count, data: data || this.statistics().data });
}
hovered = new EventEmitter();
hover() {
this.hovered.emit(this.statistics());
}
leave() {
this.hovered.emit(undefined);
}
constructor() {
effect(() => {
this.setCurrentColor();
});
}
setCurrentColor() {
if (this.settings() && this.statistics()) {
const { count } = this.statistics();
const { colors, colorsStats } = this.settings();
const key = (keys(colorsStats || {})).map(num => +num).sort((p, c) => p - c).find((item) => item >= count);
this.currentColor = count === 0 || !key ? colors[0] : colorsStats?.[key];
}
}
currentColor = 'rgba(200, 200, 200, 0.5)';
get width() {
return this.settings().size || 10;
}
get height() {
return this.settings().size || 10;
}
size = computed(() => this.settings().size || 10);
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyStatisticComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: DailyStatisticComponent, isStandalone: false, selector: "obl-daily-statistic", inputs: { selected: { classPropertyName: "selected", publicName: "selected", isSignal: false, isRequired: false, transformFunction: null }, statistics: { classPropertyName: "statistics", publicName: "statistics", isSignal: true, isRequired: true, transformFunction: null }, settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { clicked: "clicked", hovered: "hovered" }, host: { listeners: { "click": "onClick()", "mouseover": "hover()", "mouseleave": "leave()" }, properties: { "class.selected": "this.selected", "style.left.px": "this.x", "style.top.px": "this.y", "style.background-color": "this.currentColor", "style.width.px": "this.width", "style.height.px": "this.height" } }, ngImport: i0, template: "@if (loading) {\n <obl-spinner\n [style.width.px]=\"size() * 0.8\"\n [style.height.px]=\"size() * 0.8\"\n [style.margin.px]=\"size() * 0.2\"\n ></obl-spinner>\n}\n", styles: [":host{position:absolute;overflow:hidden;display:flex;justify-content:center;align-items:center;cursor:pointer;box-sizing:border-box}:host:hover{opacity:.8;border:var(--daily-statistic-border, 1px) solid var(--daily-statistic-border-hover-color, gray)}:host.selected{border:var(--daily-statistic-border, 1px) solid var(--daily-statistic-border-selected-color, gray)}obl-spinner{--obl-spinner-color: black}\n"], dependencies: [{ kind: "component", type: i1.SpinnerComponent, selector: "obl-spinner" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyStatisticComponent, decorators: [{
type: Component,
args: [{ selector: 'obl-daily-statistic', standalone: false, template: "@if (loading) {\n <obl-spinner\n [style.width.px]=\"size() * 0.8\"\n [style.height.px]=\"size() * 0.8\"\n [style.margin.px]=\"size() * 0.2\"\n ></obl-spinner>\n}\n", styles: [":host{position:absolute;overflow:hidden;display:flex;justify-content:center;align-items:center;cursor:pointer;box-sizing:border-box}:host:hover{opacity:.8;border:var(--daily-statistic-border, 1px) solid var(--daily-statistic-border-hover-color, gray)}:host.selected{border:var(--daily-statistic-border, 1px) solid var(--daily-statistic-border-selected-color, gray)}obl-spinner{--obl-spinner-color: black}\n"] }]
}], ctorParameters: () => [], propDecorators: { selected: [{
type: HostBinding,
args: ['class.selected']
}, {
type: Input
}], x: [{
type: HostBinding,
args: ['style.left.px']
}], y: [{
type: HostBinding,
args: ['style.top.px']
}], clicked: [{
type: Output
}], onClick: [{
type: HostListener,
args: ['click']
}], hovered: [{
type: Output
}], hover: [{
type: HostListener,
args: ['mouseover']
}], leave: [{
type: HostListener,
args: ['mouseleave']
}], currentColor: [{
type: HostBinding,
args: ['style.background-color']
}], width: [{
type: HostBinding,
args: ['style.width.px']
}], height: [{
type: HostBinding,
args: ['style.height.px']
}] } });
class DailyLegendComponent {
settings = input.required();
sort = (p, c) => (+p.key) - (+c.key);
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyLegendComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: DailyLegendComponent, isStandalone: false, selector: "obl-daily-legend", inputs: { settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<span>{{ settings().text?.less || 'less' }}</span>\n\n@for (item of (settings().colorsStats || {}) | keyvalue: sort; track item.key) {\n <div\n [style.background]=\"item.value\"\n [style.width.px]=\"settings().size\"\n [style.height.px]=\"settings().size\"\n [style.margin.px]=\"settings().margin\"\n ></div>\n}\n\n<span>{{ settings().text?.more || 'more' }}</span>\n", styles: [":host{display:flex;align-items:center;font-size:12px;color:var(--default-text-color)}:host span{margin:5px}\n"], dependencies: [{ kind: "pipe", type: i1$1.KeyValuePipe, name: "keyvalue" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyLegendComponent, decorators: [{
type: Component,
args: [{ selector: 'obl-daily-legend', standalone: false, template: "<span>{{ settings().text?.less || 'less' }}</span>\n\n@for (item of (settings().colorsStats || {}) | keyvalue: sort; track item.key) {\n <div\n [style.background]=\"item.value\"\n [style.width.px]=\"settings().size\"\n [style.height.px]=\"settings().size\"\n [style.margin.px]=\"settings().margin\"\n ></div>\n}\n\n<span>{{ settings().text?.more || 'more' }}</span>\n", styles: [":host{display:flex;align-items:center;font-size:12px;color:var(--default-text-color)}:host span{margin:5px}\n"] }]
}] });
class DailyStatisticsComponent {
locale;
statistics = [];
_settings;
max = 0;
daysHeaders = [];
monthHeaders = [];
selected;
hovered;
total;
selectedIndex;
default = {
min: new Date().getFullYear() - 20,
max: new Date().getFullYear()
};
totalRef;
selectedRef;
/** settings for all view components */
set settings(value) {
const dateChanged = !this._settings || !Dates.equalToDayLevel(this._settings.end, value.end) || !Dates.equalToDayLevel(this._settings.start, value.start);
this._settings = value;
const first = Dates.getWeekDate(value.start, 'first', this.weakDayOffset);
const date = new Date(first);
this.daysHeaders = [];
for (let i = 0; i < 7; i += 2) {
date.setDate(date.getDate() + (i ? 2 : 0));
this.daysHeaders.push(new Date(date));
}
if (dateChanged || !this.statistics) {
this.selected = undefined;
this.selectedIndex = undefined;
this.selectionChanged.emit(this.selected);
value.get(value.start, value.end).pipe(map((statistics) => {
const { start, end } = value;
this.total = statistics.map(v => v.count).reduce((p, c) => (p += c, p), 0);
const iterator = new Date(start);
this.setColors(statistics);
while (!Dates.equalToDayLevel(iterator, end)) {
if (statistics.findIndex(v => Dates.equalToDayLevel(v.date, iterator)) === -1) {
statistics.push({
date: new Date(iterator.getTime()),
count: 0,
});
}
iterator.setDate(iterator.getDate() + 1);
}
return statistics;
}), map((statistics) => {
this.max = 0;
this.monthHeaders = [];
return statistics.map(v => {
const days = Dates.countDays(first, v.date);
const x = Math.floor(days / 7) * (value.size + this.margin);
if (this.max < x) {
this.max = x;
}
if (v.date.getDate() === 1 && !this.monthHeaders.find(d => Dates.equalToDayLevel(d.date, v.date))) {
this.monthHeaders.push({
date: new Date(v.date),
x
});
}
return {
...v,
x,
y: (Math.floor(days) % 7) * (value.size + this.margin)
};
});
})).subscribe(v => this.statistics = v);
}
}
_weakDayOffset = 0;
/**
* Set weak day offset to for example start weak not from Sunday but from Monday you should set it to 6
*
* Do not use values less then 0
*/
set weakDayOffset(value) {
this._weakDayOffset = value;
if (this.statistics && this.settings) {
const { size, start } = this.settings;
const first = Dates.getWeekDate(start, 'first', value);
const date = new Date(first);
this.daysHeaders = [];
for (let i = 0; i < 7; i += 2) {
date.setDate(date.getDate() + (i ? 2 : 0));
this.daysHeaders.push(new Date(date));
}
this.max = 0;
this.monthHeaders = [];
this.statistics = this.statistics.map(v => {
const days = Dates.countDays(first, v.date);
const x = Math.floor(days / 7) * (size + this.margin);
if (this.max < x) {
this.max = x;
}
if (v.date.getDate() === 1) {
this.monthHeaders.push({
date: new Date(v.date),
x
});
}
return {
...v,
x,
y: (Math.floor(days) % 7) * (size + this.margin)
};
});
}
}
get weakDayOffset() {
return this._weakDayOffset;
}
// eslint-disable-next-line @typescript-eslint/adjacent-overload-signatures
get settings() {
return this._settings;
}
get margin() {
return this._settings?.margin ?? 5;
}
get height() {
return (this.size + this.margin) * 7;
}
get width() {
return this.max + this.fontSize + this.margin;
}
get size() {
return this._settings?.size ?? 10;
}
get fontSize() {
return this.size * 0.5;
}
clicked = new EventEmitter();
selectionChanged = new EventEmitter();
constructor(locale) {
this.locale = locale;
}
changed(year) {
this.settings = {
...this._settings,
start: new Date(year, 0, 1),
end: new Date(year + 1, 0, 1)
};
}
onClicked(statistic, index) {
this.selected = statistic;
this.selectedIndex = index;
this.clicked.emit(statistic);
this.selectionChanged.emit(statistic);
}
onHovered(statistics) {
this.hovered = statistics;
}
setColors(statistics) {
const elements = statistics.map(item => item.count).filter(v => v);
const { min, max } = elements.length && ElementaryMath.getMinMax(elements) || { min: 0, max: 0 };
const mid = (max - min) / ((this._settings.colors.length - 1) || 1);
const colorsStats = this._settings.colors.map((color, index) => {
return { [index === 0 ? 0 : mid * index + min]: color };
}).reduce((p, c) => ({ ...p, ...c }), {});
this._settings = {
...this._settings,
colorsStats
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyStatisticsComponent, deps: [{ token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: DailyStatisticsComponent, isStandalone: false, selector: "obl-daily-statistics", inputs: { settings: "settings", weakDayOffset: "weakDayOffset" }, outputs: { clicked: "clicked", selectionChanged: "selectionChanged" }, queries: [{ propertyName: "totalRef", first: true, predicate: ["totalRef"], descendants: true }, { propertyName: "selectedRef", first: true, predicate: ["selectedRef"], descendants: true }], ngImport: i0, template: "<div class=\"months-header\">\n @for (month of monthHeaders; track month) {\n <div\n class=\"month-header\"\n [style.left.px]=\"month.x\"\n >\n {{ month.date | date : 'LLL' : undefined : locale }}\n </div>\n }\n</div>\n\n<div class=\"days-section\">\n <div class=\"days-header\" [style.lineHeight.px]=\"size\">\n @for (date of daysHeaders; track date) {\n <div\n class=\"day\"\n [style.top.px]=\"$index * 2 * (settings.size + settings.margin)\"\n >\n {{ date | date : 'EEEE' : undefined : locale }}\n </div>\n }\n </div>\n <div class=\"days\" [style.width.px]=\"width\" [style.height.px]=\"height\">\n @for (statistic of statistics; track statistic) {\n <obl-daily-statistic\n [settings]=\"settings\"\n [statistics]=\"statistic\"\n (clicked)=\"onClicked($event, $index)\"\n (hovered)=\"onHovered($event)\"\n [selected]=\"selectedIndex === $index\"\n ></obl-daily-statistic>\n }\n </div>\n</div>\n\n<div class=\"legend\" [style.width.px]=\"width\">\n <div>\n @if (selected || hovered) {\n <ng-container\n *ngTemplateOutlet=\"\n selectedRef || defaultSelected;\n context: { $implicit: hovered || selected }\n \"\n >\n </ng-container>\n }\n @if (total) {\n <ng-container\n *ngTemplateOutlet=\"\n totalRef || defaultTotal;\n context: { $implicit: total }\n \"\n >\n </ng-container>\n }\n </div>\n <obl-daily-legend [settings]=\"settings\"></obl-daily-legend>\n</div>\n\n@if (settings.yearSwitcher) {\n <div class=\"year-picker\" [style.width.px]=\"width\">\n <obl-year-picker\n [display]=\"{ nextPrevious: false }\"\n [current]=\"settings.start.getFullYear()\"\n [start]=\"settings.minYear || default.min\"\n [end]=\"settings.maxYear || default.max\"\n [picker]=\"{\n className: 'obl-daily-picker'\n }\"\n (changed)=\"changed($event)\"\n ></obl-year-picker>\n </div>\n}\n\n<ng-template let-selected #defaultSelected>\n {{ selected.date | date : 'dd-MM-yyyy' : locale }} →\n {{ selected.count }}\n</ng-template>\n\n<ng-template let-total #defaultTotal> Total: {{ total }} </ng-template>\n", styles: [":host{width:fit-content;color:var(--default-text-color)}.days-section{display:flex;overflow:hidden}.months-header,.legend{margin-left:var(--days-space-size, 80px)}.days-header{position:relative;font-size:12px;width:var(--days-space-size, 80px);min-width:var(--days-space-size, 80px)}.days-header .day{position:absolute}.days{position:relative;display:block}.legend{display:flex;justify-content:space-between;align-items:center;min-width:fit-content;font-size:12px}.months-header{position:relative;font-size:12px;height:20px;overflow:hidden}.month-header{position:absolute}obl-year-picker{display:block;width:305px;max-width:100%;margin-left:auto}.year-picker{margin-left:var(--days-space-size, 80px);--obl-font-size: 14px}::ng-deep obl-values-set.obl-daily-picker{--obl-font-size: 14px}\n"], dependencies: [{ kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2.YearPickerComponent, selector: "obl-year-picker", inputs: ["current", "start", "end", "placeholder", "disabled", "display", "picker"], outputs: ["changed"] }, { kind: "component", type: DailyStatisticComponent, selector: "obl-daily-statistic", inputs: ["selected", "statistics", "settings"], outputs: ["clicked", "hovered"] }, { kind: "component", type: DailyLegendComponent, selector: "obl-daily-legend", inputs: ["settings"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyStatisticsComponent, decorators: [{
type: Component,
args: [{ selector: 'obl-daily-statistics', standalone: false, template: "<div class=\"months-header\">\n @for (month of monthHeaders; track month) {\n <div\n class=\"month-header\"\n [style.left.px]=\"month.x\"\n >\n {{ month.date | date : 'LLL' : undefined : locale }}\n </div>\n }\n</div>\n\n<div class=\"days-section\">\n <div class=\"days-header\" [style.lineHeight.px]=\"size\">\n @for (date of daysHeaders; track date) {\n <div\n class=\"day\"\n [style.top.px]=\"$index * 2 * (settings.size + settings.margin)\"\n >\n {{ date | date : 'EEEE' : undefined : locale }}\n </div>\n }\n </div>\n <div class=\"days\" [style.width.px]=\"width\" [style.height.px]=\"height\">\n @for (statistic of statistics; track statistic) {\n <obl-daily-statistic\n [settings]=\"settings\"\n [statistics]=\"statistic\"\n (clicked)=\"onClicked($event, $index)\"\n (hovered)=\"onHovered($event)\"\n [selected]=\"selectedIndex === $index\"\n ></obl-daily-statistic>\n }\n </div>\n</div>\n\n<div class=\"legend\" [style.width.px]=\"width\">\n <div>\n @if (selected || hovered) {\n <ng-container\n *ngTemplateOutlet=\"\n selectedRef || defaultSelected;\n context: { $implicit: hovered || selected }\n \"\n >\n </ng-container>\n }\n @if (total) {\n <ng-container\n *ngTemplateOutlet=\"\n totalRef || defaultTotal;\n context: { $implicit: total }\n \"\n >\n </ng-container>\n }\n </div>\n <obl-daily-legend [settings]=\"settings\"></obl-daily-legend>\n</div>\n\n@if (settings.yearSwitcher) {\n <div class=\"year-picker\" [style.width.px]=\"width\">\n <obl-year-picker\n [display]=\"{ nextPrevious: false }\"\n [current]=\"settings.start.getFullYear()\"\n [start]=\"settings.minYear || default.min\"\n [end]=\"settings.maxYear || default.max\"\n [picker]=\"{\n className: 'obl-daily-picker'\n }\"\n (changed)=\"changed($event)\"\n ></obl-year-picker>\n </div>\n}\n\n<ng-template let-selected #defaultSelected>\n {{ selected.date | date : 'dd-MM-yyyy' : locale }} →\n {{ selected.count }}\n</ng-template>\n\n<ng-template let-total #defaultTotal> Total: {{ total }} </ng-template>\n", styles: [":host{width:fit-content;color:var(--default-text-color)}.days-section{display:flex;overflow:hidden}.months-header,.legend{margin-left:var(--days-space-size, 80px)}.days-header{position:relative;font-size:12px;width:var(--days-space-size, 80px);min-width:var(--days-space-size, 80px)}.days-header .day{position:absolute}.days{position:relative;display:block}.legend{display:flex;justify-content:space-between;align-items:center;min-width:fit-content;font-size:12px}.months-header{position:relative;font-size:12px;height:20px;overflow:hidden}.month-header{position:absolute}obl-year-picker{display:block;width:305px;max-width:100%;margin-left:auto}.year-picker{margin-left:var(--days-space-size, 80px);--obl-font-size: 14px}::ng-deep obl-values-set.obl-daily-picker{--obl-font-size: 14px}\n"] }]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Inject,
args: [LOCALE_ID]
}] }], propDecorators: { totalRef: [{
type: ContentChild,
args: ['totalRef']
}], selectedRef: [{
type: ContentChild,
args: ['selectedRef']
}], settings: [{
type: Input
}], weakDayOffset: [{
type: Input
}], clicked: [{
type: Output
}], selectionChanged: [{
type: Output
}] } });
class DailyStatisticsModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyStatisticsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: DailyStatisticsModule, declarations: [DailyStatisticsComponent,
DailyStatisticComponent,
DailyLegendComponent], imports: [CommonModule,
LoadingModule,
YearPickerModule], exports: [DailyStatisticsComponent,
DailyStatisticComponent,
DailyLegendComponent] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyStatisticsModule, imports: [CommonModule,
LoadingModule,
YearPickerModule] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DailyStatisticsModule, decorators: [{
type: NgModule,
args: [{
declarations: [
DailyStatisticsComponent,
DailyStatisticComponent,
DailyLegendComponent
],
imports: [
CommonModule,
LoadingModule,
YearPickerModule
],
exports: [
DailyStatisticsComponent,
DailyStatisticComponent,
DailyLegendComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { DailyLegendComponent, DailyStatisticComponent, DailyStatisticsComponent, DailyStatisticsModule };
//# sourceMappingURL=obliczeniowo-elementary-daily-statistics.mjs.map