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; /** * 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; @Input() taxDeadlines: Array; // /** // * 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(); /** * 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 }; }