2025-05-07 18:09:33 +02:00

801 lines
20 KiB
TypeScript

import { Subscription } from "rxjs";
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Input,
OnChanges,
TemplateRef,
forwardRef,
OnInit,
SimpleChanges,
EventEmitter,
Output,
OnDestroy,
ElementRef,
ViewChild,
ViewChildren,
} from "@angular/core";
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from "@angular/forms";
import { NgbDatepickerRoutingViewNOA11Y } from "./datepicker-routing-view";
import {
DatepickerViewModel,
DayTemplateContextNOA11Y,
EnabledRangeService,
isChangedDate,
NavigationEvent,
NgbCalendarNOA11Y,
NgbDataTemplateStruct,
NgbDate,
NgbDatepickerConfigNOA11Y,
NgbDatepickerI18nNOA11Y,
NgbDatepickerKeyMapServiceNOA11Y,
NgbDatepickerServiceNOA11Y,
NgbDateStruct,
} from "@isp/xdce-widget";
import { toInteger } from "@isp/xdce-widget/user-input/isp-typeahead/ng-bootstrap/util";
const NGB_CALENDAR_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RplCalendarNOA11Y),
multi: true,
};
/**
* The payload of the datepicker navigation event
*/
export interface RplCalendarNavigateEventNOA11Y {
/**
* Currently displayed month
*/
current: { year: number; month: number };
/**
* Month we're navigating to
*/
next: { year: number; month: number };
}
export class CalendarItemNOA11Y {
name: string;
number: number;
focused: boolean;
disabled: boolean;
selected: boolean;
}
/**
* A lightweight and highly configurable datepicker directive
*/
@Component({
exportAs: "fidRplCalendarNOA11Y",
selector: "fid-rpl-calendar-noa11y",
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
class: "d-inline-block rounded",
tabindex: "0",
"[attr.tabindex]": 'model.disabled ? undefined : "0"',
"(blur)": "showFocus(false)",
"(focus)": "showFocus(true)",
"(keydown)": "onKeyDown($event)",
},
templateUrl: './datepicker.html',
styleUrls: ["./datepicker.scss"],
providers: [
NGB_CALENDAR_VALUE_ACCESSOR,
NgbDatepickerServiceNOA11Y,
NgbDatepickerKeyMapServiceNOA11Y,
],
})
export class RplCalendarNOA11Y
implements OnDestroy, OnChanges, OnInit, ControlValueAccessor
{
model: DatepickerViewModel;
@ViewChild(forwardRef(() => NgbDatepickerRoutingViewNOA11Y))
datepickerRoutingView: NgbDatepickerRoutingViewNOA11Y;
@ViewChild(forwardRef(() => NgbDatepickerRoutingViewNOA11Y))
datepickerHeader: NgbDatepickerRoutingViewNOA11Y;
private _subscription: Subscription;
/**
* Reference for the custom template for the day display
*/
@Input() dayTemplate: TemplateRef<DayTemplateContextNOA11Y>;
/**
* Number of months to display
*/
@Input() displayMonths: number;
/**
* First day of the week. With default calendar we use ISO 8601: 'weekday' is 1=Mon ... 7=Sun
*/
@Input() firstDayOfWeek: number;
/**
* Callback to mark a given date as disabled.
* 'Current' contains the month that will be displayed in the view
*/
@Input() markDisabled: (
date: NgbDateStruct,
current: { year: number; month: number }
) => boolean;
/**
* Min date for the navigation. If not provided will be 10 years before today or `startDate`
*/
_minDate: NgbDateStruct;
@Input()
get minDate() {
return this._minDate;
}
set minDate(minDate: NgbDateStruct) {
this._minDate = minDate;
this.startDateEnabled = minDate;
}
/**
* Max date for the navigation. If not provided will be 10 years from today or `startDate`
*/
_maxDate: NgbDateStruct;
@Input()
get maxDate() {
return this._maxDate;
}
set maxDate(maxDate: NgbDateStruct) {
this._maxDate = maxDate;
this.endDateEnabled = maxDate;
}
/**
* Navigation type: `select` (default with select boxes for month and year), `arrows`
* (without select boxes, only navigation arrows) or `none` (no navigation at all)
*/
@Input() navigation: "select" | "arrows" | "none";
/**
* The way to display days that don't belong to current month: `visible` (default),
* `hidden` (not displayed) or `collapsed` (not displayed with empty space collapsed)
*/
@Input() outsideDays: "visible" | "collapsed" | "hidden";
/**
* Whether to display days of the week
*/
@Input() showWeekdays: boolean;
/**
* Whether to display week numbers
*/
@Input() showWeekNumbers: boolean;
/**
* Date to open calendar with.
* With default calendar we use ISO 8601: 'month' is 1=Jan ... 12=Dec.
* If nothing or invalid date provided, calendar will open with current month.
* Use 'navigateTo(date)' as an alternative
*/
@Input() startDate: { year: number; month: number; day?: number };
@Input() startDateEnabled: any;
@Input() endDateEnabled: any;
@Input() disabledWeekend: boolean;
@Input() holidays: Array<NgbDateStruct>;
@Input() taxDeadlines: Array<NgbDateStruct>;
// /**
// * Start date Enabled
// */
// @Input() startDateEnabled: { year: number, month: number, day: number };
// /**
// * End date Enabled
// */
// @Input() endDateEnabled: { year: number, month: number, day: number };
/**
* In questa variabile sono memorizzati i dati iniziali che scenderanno l'albero dei template
*/
dataTemplate: NgbDataTemplateStruct = {
template: 0,
day: 0,
month: 0,
year: 0,
valid: false,
};
/**
* An event fired when navigation happens and currently displayed month changes.
* See NgbDatepickerNavigateEvent for the payload info.
*/
@Output() navigate = new EventEmitter<NgbDatepickerNavigateEventNOA11Y>();
/**
* Template da Adoperare
*/
template: number = 0;
mesi: CalendarItemNOA11Y[][] = [
[
{
name: "JAN",
number: 1,
disabled: false,
selected: false,
focused: false,
},
{
name: "FEB",
number: 2,
disabled: false,
selected: false,
focused: false,
},
{
name: "MAR",
number: 3,
disabled: false,
selected: false,
focused: false,
},
{
name: "APR",
number: 4,
disabled: false,
selected: false,
focused: false,
},
],
[
{
name: "MAY",
number: 5,
disabled: false,
selected: false,
focused: false,
},
{
name: "JUN",
number: 6,
disabled: false,
selected: false,
focused: false,
},
{
name: "JUL",
number: 7,
disabled: false,
selected: false,
focused: false,
},
{
name: "AUG",
number: 8,
disabled: false,
selected: false,
focused: false,
},
],
[
{
name: "SEP",
number: 9,
disabled: false,
selected: false,
focused: false,
},
{
name: "OCT",
number: 10,
disabled: false,
selected: false,
focused: false,
},
{
name: "NOV",
number: 11,
disabled: false,
selected: false,
focused: false,
},
{
name: "DEC",
number: 12,
disabled: false,
selected: false,
focused: false,
},
],
];
years: CalendarItemNOA11Y[][] = [
[
{
name: "2018",
number: 2018,
disabled: false,
selected: false,
focused: false,
},
{
name: "2019",
number: 2019,
disabled: false,
selected: false,
focused: false,
},
{
name: "2020",
number: 2020,
disabled: false,
selected: false,
focused: false,
},
{
name: "2021",
number: 2021,
disabled: false,
selected: false,
focused: false,
},
],
[
{
name: "2022",
number: 2022,
disabled: false,
selected: false,
focused: false,
},
{
name: "2023",
number: 2023,
disabled: false,
selected: false,
focused: false,
},
{
name: "2024",
number: 2024,
disabled: false,
selected: false,
focused: false,
},
{
name: "2025",
number: 2025,
disabled: false,
selected: false,
focused: false,
},
],
[
{
name: "2026",
number: 2026,
disabled: false,
selected: false,
focused: false,
},
{
name: "2027",
number: 2027,
disabled: false,
selected: false,
focused: false,
},
{
name: "2028",
number: 2028,
disabled: false,
selected: false,
focused: false,
},
{
name: "2029",
number: 2029,
disabled: false,
selected: false,
focused: false,
},
],
];
onChange = (_: any) => {};
onTouched = () => {};
// startDateEnabled: string;
// endDateEnabled: string;
constructor(
private enabledRange: EnabledRangeService,
private _keyMapService: NgbDatepickerKeyMapServiceNOA11Y,
public _service: NgbDatepickerServiceNOA11Y,
private _calendar: NgbCalendarNOA11Y,
public i18n: NgbDatepickerI18nNOA11Y,
config: NgbDatepickerConfigNOA11Y,
private _cd: ChangeDetectorRef,
private _elementRef: ElementRef
) {
this.dayTemplate = config.dayTemplate;
this.displayMonths = config.displayMonths;
this.firstDayOfWeek = config.firstDayOfWeek;
this.markDisabled = config.markDisabled;
this.minDate = config.minDate;
this.maxDate = config.maxDate;
this.navigation = config.navigation;
this.showWeekdays = config.showWeekdays;
this.showWeekNumbers = config.showWeekNumbers;
this.startDate = config.startDate;
this.disabledWeekend = config.disabledWeekend;
this.holidays = config.holidays;
this.taxDeadlines = config.taxDeadlines;
this.startDateEnabled = enabledRange.startDate;
this.endDateEnabled = enabledRange.endDate;
// alert("finale Start : " + this.startDateEnabled)
// alert("finale End : " + this.endDateEnabled)
this._subscription = _service.model$.subscribe((model) => {
const newDate = model.firstDate;
const oldDate = this.model ? this.model.firstDate : null;
const newSelectedDate = model.selectedDate;
const oldSelectedDate = this.model ? this.model.selectedDate : null;
this.model = model;
// handling selection change
if (isChangedDate(newSelectedDate, oldSelectedDate)) {
this.onTouched();
this.onChange(
newSelectedDate
? {
year: newSelectedDate.year,
month: newSelectedDate.month,
day: newSelectedDate.day,
}
: null
);
}
// emitting navigation event if the first month changes
if (!newDate.equals(oldDate)) {
this.navigate.emit({
current: oldDate
? { year: oldDate.year, month: oldDate.month }
: null,
next: { year: newDate.year, month: newDate.month },
});
}
_cd.markForCheck();
});
}
abilitaMesi(data) {
if (
this.startDateEnabled.month <= data &&
data <= this.endDateEnabled.month &&
this.startDateEnabled.year <= this.dataTemplate.year &&
this.dataTemplate.year <= this.endDateEnabled.year
) {
return true;
}
return false;
}
abilitaAnni(data) {
if (
this.startDateEnabled.year <= data &&
data <= this.endDateEnabled.year
) {
return true;
}
return false;
}
setNewTemplate(selectedTemplate: number) {
this.setMesi(this.dataTemplate.month, true);
this.updateYears(this.dataTemplate.year);
this.setYear(this.dataTemplate.year, true);
const prevTemplate = this.template;
this.template = selectedTemplate;
if (prevTemplate === 2 && this.template === 0) {
const model = this._service._model$.getValue();
if (model && model.selectedDate) {
this.navigateTo({
month: model.selectedDate.month,
year: model.selectedDate.year,
});
}
}
}
setMesi(position: number, selected: boolean) {
if (!this.abilitaMesi(position)) return;
let nRow = 0,
nCol = 0;
if (position == 4) nRow = 0;
else if (position == 8) nRow = 1;
else if (position == 12) nRow = 2;
else nRow = Math.trunc(position / 4);
nCol = position - nRow * 4 - 1;
console.info(
"dataTemplate",
this.dataTemplate,
"mese",
this.mesi[nRow][nCol],
"nRow",
nRow,
"nCol",
nCol
);
this.mesi[nRow][nCol].selected = selected;
}
updateYears(startYearValue: number) {
let yrs: CalendarItemNOA11Y[][] = [];
for (let j = 0; j < 3; j++) {
yrs[j] = [];
for (let i = 0; i < 4; i++) {
const yr = new CalendarItemNOA11Y();
yr.number = startYearValue + i + j * 4;
yr.name = yr.number + "";
yr.disabled = false;
yr.focused = false;
yr.selected = false;
yrs[j][i] = yr;
// this.years[j][i].number = startYearValue + i + j * 4
// this.years[j][i].name = this.years[j][i].number + "";
}
}
yrs.forEach((row) => {
row.forEach((y) => {
y.selected = this.dataTemplate.year === y.number;
});
});
this.years = yrs;
}
setYear(yearValue: number, selected: boolean) {
if (!this.abilitaAnni(yearValue)) return;
this.years.forEach((row) => {
row.forEach((year) => {
if (year.number == yearValue) year.selected = selected;
});
});
}
/**
* Manually focus the calendar
*/
focus() {
this._elementRef.nativeElement.focus();
}
getHeaderHeight() {
const h = this.showWeekdays ? 6.25 : 4.25;
return this.displayMonths === 1 || this.navigation !== "select" ? h - 2 : h;
}
getHeaderMargin() {
const m = this.showWeekdays ? 2 : 0;
return this.displayMonths !== 1 || this.navigation !== "select" ? m + 2 : m;
}
/**
* Navigates current view to provided date.
* With default calendar we use ISO 8601: 'month' is 1=Jan ... 12=Dec.
* If nothing or invalid date provided calendar will open current month.
* Use 'startDate' input as an alternative
*/
navigateTo(date?: { year: number; month: number }) {
this._service.open(
date ? new NgbDate(date.year, date.month, 1) : this._calendar.getToday()
);
}
ngOnDestroy() {
this._subscription.unsubscribe();
}
ngOnInit() {
if (this.model === undefined) {
this._service.displayMonths = toInteger(this.displayMonths);
this._service.markDisabled = this.markDisabled;
this._service.firstDayOfWeek = this.firstDayOfWeek;
this._service.disabledWeekend = this.disabledWeekend;
this._service.holidays = this.holidays;
this._service.taxDeadlines = this.taxDeadlines;
this._setDates();
// inixializzazione dataTemplate alla data selezionata
if (!this.startDate || !this.startDate.month || !this.startDate.year) {
this.startDate = { year: null, month: null, day: null };
let today = new Date();
let dd = today.getDate();
let mm = today.getMonth() + 1; //January is 0!
let yyyy = today.getFullYear();
this.startDate.day = dd;
this.startDate.month = mm;
this.startDate.year = yyyy;
}
this.dataTemplate.day = this.startDate.day;
this.dataTemplate.month = this.startDate.month;
this.dataTemplate.year = this.startDate.year;
this.dataTemplate.valid = false;
let elem: HTMLElement = this._elementRef.nativeElement;
elem.classList.contains("isp-corporate-mode-wrapper")
? (this.outsideDays = "hidden")
: (this.outsideDays = "visible");
}
}
ngOnChanges(changes: SimpleChanges) {
if (changes["displayMonths"]) {
this._service.displayMonths = toInteger(this.displayMonths);
}
if (changes["markDisabled"]) {
this._service.markDisabled = this.markDisabled;
}
if (changes["firstDayOfWeek"]) {
this._service.firstDayOfWeek = this.firstDayOfWeek;
}
if (changes["taxDeadlines"]) {
this._service.taxDeadlines = this.taxDeadlines;
}
}
onDateSelect(date: NgbDate) {
this._service.focus(date);
this.writeValue(date);
}
onKeyDown(event: KeyboardEvent) {
this._keyMapService.processKey(event);
}
onNavigateDateSelect(date: NgbDate) {
this._service.open(date);
}
onNavigateEvent(event: NavigationEvent) {
let bufferData: NgbDate;
this.model.firstDate.month = this.dataTemplate.month;
this.model.firstDate.year = this.dataTemplate.year;
if (event == NavigationEvent.PREV && this.template == 0) {
bufferData = this._calendar.getPrev(this.model.firstDate, "m", 1);
this._service.open(bufferData);
this.updateDataTemplate(bufferData);
} else if (NavigationEvent.NEXT && this.template == 0) {
bufferData = this._calendar.getNext(this.model.firstDate, "m", 1);
this._service.open(bufferData);
this.updateDataTemplate(bufferData);
} else if (event == NavigationEvent.PREV && this.template == 1) {
bufferData = this._calendar.getPrev(this.model.firstDate, "m", 12);
this._service.open(bufferData);
this.updateDataTemplate(bufferData);
} else if (event == NavigationEvent.NEXT && this.template == 1) {
bufferData = this._calendar.getNext(this.model.firstDate, "m", 12);
this._service.open(bufferData);
this.updateDataTemplate(bufferData);
} else if (event == NavigationEvent.PREV && this.template == 2) {
bufferData = this._calendar.getPrev(this.model.firstDate, "m", 144);
this._service.open(bufferData);
this.updateDataTemplate(bufferData);
this.updateYears(this.dataTemplate.year);
} else if (event == NavigationEvent.NEXT && this.template == 2) {
bufferData = this._calendar.getNext(this.model.firstDate, "m", 144);
this._service.open(bufferData);
this.updateDataTemplate(bufferData);
this.updateYears(this.dataTemplate.year);
}
}
onChangeTemplate(dataTemplate: NgbDataTemplateStruct) {
this.dataTemplate = dataTemplate;
this.template = dataTemplate.template;
this.navigateTo({ month: dataTemplate.month, year: dataTemplate.year });
}
updateDataTemplate(date: NgbDate) {
const newTemplate = { ...this.dataTemplate };
newTemplate.day = date.day;
newTemplate.month = date.month;
newTemplate.year = date.year;
this.dataTemplate = newTemplate;
this.navigateTo({
month: this.dataTemplate.month,
year: this.dataTemplate.year,
});
}
registerOnChange(fn: (value: any) => any): void {
this.onChange = fn;
}
registerOnTouched(fn: () => any): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean) {
this._service.disabled = isDisabled;
}
showFocus(focusVisible: boolean) {
this._service.focusVisible = focusVisible;
}
writeValue(value: any) {
this._service.select(value);
}
private _setDates() {
const startDate = this._service.toValidDate(
this.startDate,
this._calendar.getToday()
);
const minDate = this._service.toValidDate(
this.minDate,
this._calendar.getPrev(startDate, "y", 10)
);
const maxDate = this._service.toValidDate(
this.maxDate,
this._calendar.getPrev(this._calendar.getNext(startDate, "y", 11))
);
this.minDate = {
year: minDate.year,
month: minDate.month,
day: minDate.day,
};
this.maxDate = {
year: maxDate.year,
month: maxDate.month,
day: maxDate.day,
};
this._service.minDate = minDate;
this._service.maxDate = maxDate;
this.navigateTo(startDate);
}
public focusOutComponent(event: any) {
if (!this._elementRef.nativeElement.contains(event.target)) {
let elem: HTMLElement = this._elementRef.nativeElement;
if (elem.parentElement) {
elem.parentElement.removeChild(elem);
}
this._subscription.unsubscribe();
}
}
}
/**
* The payload of the calendar navigation event
*/
export interface NgbDatepickerNavigateEventNOA11Y {
/**
* Currently displayed month
*/
current: { year: number; month: number };
/**
* Month we're navigating to
*/
next: { year: number; month: number };
}