added angular calendar from html page component
This commit is contained in:
parent
c9497e3afc
commit
28c94936d6
@ -40,6 +40,8 @@ export { WidgetFideuramShowcaseComponent } from './widgetfideuram/components/wid
|
||||
|
||||
import { AgGridModule, AngularFrameworkComponentWrapper, AngularFrameworkOverrides } from 'ag-grid-angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NbpFidCalendarGenericComponent } from './widgetfideuram/components/nbp-fid-calendar-generic/nbp-fid-calendar-generic.component';
|
||||
export { NbpFidCalendarGenericComponent } from './widgetfideuram/components/nbp-fid-calendar-generic/nbp-fid-calendar-generic.component';
|
||||
export { AgGridModule } from 'ag-grid-angular';
|
||||
|
||||
export { DATE_STRING_FORMAT, formatDate, formatNumber, setSpinnerMessage, clearSpinnerMessage } from './widgetfideuram/Utils';
|
||||
@ -70,6 +72,7 @@ export { DATE_STRING_FORMAT, formatDate, formatNumber, setSpinnerMessage, clearS
|
||||
ShowcaseComponent,
|
||||
Showcase1Component,
|
||||
WidgetFideuramShowcaseComponent,
|
||||
NbpFidCalendarGenericComponent
|
||||
],
|
||||
exports: [
|
||||
NbpBreadCrumbsComponent,
|
||||
@ -89,7 +92,8 @@ export { DATE_STRING_FORMAT, formatDate, formatNumber, setSpinnerMessage, clearS
|
||||
ShowcaseComponent,
|
||||
Showcase1Component,
|
||||
WidgetFideuramShowcaseComponent,
|
||||
AgGridModule
|
||||
AgGridModule,
|
||||
NbpFidCalendarGenericComponent
|
||||
],
|
||||
providers: [
|
||||
AngularFrameworkOverrides,
|
||||
|
@ -0,0 +1,91 @@
|
||||
<div class="datepicker-wrapper">
|
||||
<input #dateInput
|
||||
class="datepicker-input"
|
||||
type="text"
|
||||
placeholder="DD/MM/YYYY"
|
||||
[value]="formattedSelectedDate"
|
||||
(input)="onDateInputChange($event)" />
|
||||
<i class="datepicker-icon fa fa-calendar" (click)="toggleCalendar()"></i>
|
||||
|
||||
<div #calendarPopup
|
||||
class="calendar calendar-popup"
|
||||
[class.view-days]="view === 'days'"
|
||||
[class.view-months]="view === 'months'"
|
||||
[class.view-years]="view === 'years'"
|
||||
[style.display]="calendarVisible ? 'block' : 'none'">
|
||||
|
||||
<!-- Calendar Header -->
|
||||
<div class="calendar-header">
|
||||
<button class="calendar-cell fa fa-chevron-left" (click)="prev()"></button>
|
||||
<div class="calendar-cell" id="monthYear" (click)="changeView()">{{ monthYearHeader }}</div>
|
||||
<button class="calendar-cell fa fa-chevron-right" (click)="next()"></button>
|
||||
</div>
|
||||
|
||||
<!-- Calendar Grid -->
|
||||
<div class="calendar-grid" [class.calendar-grid-7]="view === 'days'" [class.calendar-grid-5]="view === 'years'">
|
||||
|
||||
<!-- Days View -->
|
||||
<ng-container *ngIf="view === 'days'">
|
||||
<!-- Days of Week -->
|
||||
<div *ngFor="let day of daysOfWeek" class="day">{{ day }}</div>
|
||||
|
||||
<!-- Previous Month Days -->
|
||||
<div *ngFor="let day of getPreviousMonthDays()"
|
||||
class="calendar-cell other-month"
|
||||
[class.disabled]="day.disabled"
|
||||
(click)="onPrevMonthDayClick(day)">
|
||||
{{ pad(day.day) }}
|
||||
</div>
|
||||
|
||||
<!-- Current Month Days -->
|
||||
<div *ngFor="let day of getCurrentMonthDays()"
|
||||
class="calendar-cell"
|
||||
[class.today]="day.isToday"
|
||||
[class.selected]="day.isSelected"
|
||||
[class.disabled]="day.disabled"
|
||||
(click)="onCurrentMonthDayClick(day)">
|
||||
{{ pad(day.day) }}
|
||||
</div>
|
||||
|
||||
<!-- Next Month Days -->
|
||||
<div *ngFor="let day of getNextMonthDays()"
|
||||
class="calendar-cell other-month"
|
||||
[class.disabled]="day.disabled"
|
||||
(click)="onNextMonthDayClick(day)">
|
||||
{{ pad(day.day) }}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- Months View -->
|
||||
<ng-container *ngIf="view === 'months'">
|
||||
<div *ngFor="let month of getVisibleMonths()"
|
||||
class="calendar-cell"
|
||||
[class.today]="month.isCurrentMonth"
|
||||
[class.selected]="month.isSelected"
|
||||
[class.disabled]="month.disabled"
|
||||
(click)="onMonthClick(month)">
|
||||
{{ month.name }}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- Years View -->
|
||||
<ng-container *ngIf="view === 'years'">
|
||||
<div *ngFor="let yearObj of getVisibleYears()"
|
||||
class="calendar-cell"
|
||||
[class.today]="yearObj.isCurrentYear"
|
||||
[class.selected]="yearObj.isSelected"
|
||||
[class.disabled]="yearObj.disabled"
|
||||
(click)="onYearClick(yearObj)">
|
||||
{{ yearObj.year }}
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<!-- Calendar Actions -->
|
||||
<div class="calendar-actions">
|
||||
<button id="todayBtn" (click)="goToday()">Oggi</button>
|
||||
<button id="clearBtn" (click)="clearSelection()">Cancella</button>
|
||||
<button id="confirmBtn" (click)="confirmDate()">Conferma</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,201 @@
|
||||
// Stili del calendario
|
||||
.calendar {
|
||||
font-size: 12px;
|
||||
padding: 15px;
|
||||
border: 1px solid #e6e6e6;
|
||||
color: #333333;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.calendar-popup {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
display: none;
|
||||
min-width: 160px;
|
||||
padding: 5px 0;
|
||||
margin: 2px 0 0;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.view-days .calendar-header {
|
||||
grid-template-columns: 1fr 5fr 1fr;
|
||||
}
|
||||
|
||||
.view-years .calendar-header {
|
||||
grid-template-columns: 1fr 3fr 1fr;
|
||||
}
|
||||
|
||||
.view-months .calendar-cell,
|
||||
.view-years .calendar-cell {
|
||||
padding: 6px 20px;
|
||||
}
|
||||
|
||||
.calendar-cell {
|
||||
padding: 5px 10px;
|
||||
line-height: 1.5;
|
||||
font-size: 12px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
-webkit-transition: all 0.1s;
|
||||
-o-transition: all 0.1s;
|
||||
transition: all 0.1s;
|
||||
}
|
||||
|
||||
.calendar-cell:not(.disabled):not(.selected):hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
#monthYear {
|
||||
font-weight: bold;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar-grid-7 {
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
}
|
||||
|
||||
.calendar-grid-5 {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
|
||||
.calendar-grid .day {
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.calendar-grid .selected {
|
||||
background-color: #1797be;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.calendar-grid .selected:hover {
|
||||
background-color: #137d9f;
|
||||
border-color: #0e5d76;
|
||||
}
|
||||
|
||||
.calendar-grid .other-month {
|
||||
color: #909fa7;
|
||||
}
|
||||
|
||||
.calendar-grid .today {
|
||||
color: #23b7e5;
|
||||
}
|
||||
|
||||
.calendar-grid:not(:has(.selected)) .today {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.calendar-grid .disabled {
|
||||
color: #989898;
|
||||
background-color: #edf1f2;
|
||||
border-color: #dde6e9;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.calendar-actions {
|
||||
padding: 10px 9px 2px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.calendar-actions button {
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
line-height: 1.5;
|
||||
background-color: #eb690b;
|
||||
color: #fff;
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.calendar-actions button#todayBtn {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.calendar-actions button#clearBtn {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-left-width: 0;
|
||||
background-color: #fff;
|
||||
color: #eb690b;
|
||||
}
|
||||
|
||||
.calendar-actions button#confirmBtn {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
// Stili del datepicker
|
||||
.datepicker-wrapper {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.datepicker-input {
|
||||
padding: 5px;
|
||||
height: 22px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
border-right-width: 0;
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.datepicker-input::placeholder {
|
||||
color: #909fa7;
|
||||
}
|
||||
|
||||
.datepicker-input:focus {
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.datepicker-input.input-error {
|
||||
border-color: #f05050;
|
||||
background-color: #fff0f0;
|
||||
}
|
||||
|
||||
.datepicker-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 13.5px;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
height: 22px;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom-right-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker-icon:active,
|
||||
.datepicker-icon:hover,
|
||||
.datepicker-icon:focus {
|
||||
background-color: #e6e6e6;
|
||||
border-color: #cbcbcb;
|
||||
}
|
||||
|
||||
.datepicker-icon:hover:active {
|
||||
background-color: #d4d4d4;
|
||||
border-color: #aaaaaa;
|
||||
}
|
@ -0,0 +1,503 @@
|
||||
import { Component, OnInit, Input, Output, EventEmitter, ElementRef, ViewChild, HostListener, Renderer2 } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'nbp-fid-calendar-generic',
|
||||
templateUrl: './nbp-fid-calendar-generic.component.html',
|
||||
styleUrls: ['./nbp-fid-calendar-generic.component.scss']
|
||||
})
|
||||
export class NbpFidCalendarGenericComponent implements OnInit {
|
||||
@Input() dateFormat: string = 'dd/MM/yyyy';
|
||||
@Input() disabledWeekdays: number[] = []; // 0=domenica, 6=sabato
|
||||
@Input() disabledDateRanges: { start: Date, end: Date }[] = [];
|
||||
@Input() minDate: Date;
|
||||
@Input() maxDate: Date;
|
||||
@Output() dateChange = new EventEmitter<Date>();
|
||||
|
||||
@ViewChild('dateInput') dateInput: ElementRef;
|
||||
|
||||
// Cache per evitare ricalcoli continui e problemi di rendering
|
||||
private _previousMonthDaysCache: { day: number, disabled: boolean }[] = [];
|
||||
private _currentMonthDaysCache: { day: number, isToday: boolean, isSelected: boolean, disabled: boolean }[] = [];
|
||||
private _nextMonthDaysCache: { day: number, disabled: boolean }[] = [];
|
||||
private _visibleMonthsCache: { monthIndex: number, name: string, isCurrentMonth: boolean, isSelected: boolean, disabled: boolean }[] = [];
|
||||
private _visibleYearsCache: { year: number, isCurrentYear: boolean, isSelected: boolean, disabled: boolean }[] = [];
|
||||
@ViewChild('calendarPopup') calendarPopup: ElementRef;
|
||||
|
||||
currentDate: Date = new Date();
|
||||
selectedDate: Date = null;
|
||||
view: 'days' | 'months' | 'years' = 'days';
|
||||
months: string[] = ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'];
|
||||
daysOfWeek: string[] = ['lun', 'mar', 'mer', 'gio', 'ven', 'sab', 'dom'];
|
||||
formattedSelectedDate: string = '';
|
||||
calendarVisible: boolean = false;
|
||||
|
||||
constructor(private renderer: Renderer2, private elementRef: ElementRef) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
// Inizializza la data selezionata se presente
|
||||
if (this.selectedDate) {
|
||||
this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
|
||||
}
|
||||
|
||||
// Inizializza le cache
|
||||
this.updateAllCaches();
|
||||
}
|
||||
|
||||
// --- UTILITY ---
|
||||
private pad(n: number): string {
|
||||
return n < 10 ? '0' + n : n.toString();
|
||||
}
|
||||
|
||||
formatDate(date: Date, format: string): string {
|
||||
if (!date) return '';
|
||||
return format
|
||||
.replace('dd', this.pad(date.getDate()))
|
||||
.replace('MM', this.pad(date.getMonth() + 1))
|
||||
.replace('yyyy', date.getFullYear().toString())
|
||||
.replace('yy', String(date.getFullYear()).slice(-2));
|
||||
}
|
||||
|
||||
sameDate(d1: Date, d2: Date): boolean {
|
||||
return d1.getFullYear() === d2.getFullYear() &&
|
||||
d1.getMonth() === d2.getMonth() &&
|
||||
d1.getDate() === d2.getDate();
|
||||
}
|
||||
|
||||
// --- GESTIONE POPUP ---
|
||||
positionCalendarPopup(): void {
|
||||
if (!this.calendarPopup || !this.dateInput) return;
|
||||
|
||||
const input = this.dateInput.nativeElement;
|
||||
const popup = this.calendarPopup.nativeElement;
|
||||
|
||||
// Sposta il popup in fondo al body (portal)
|
||||
if (popup.parentNode !== document.body) {
|
||||
document.body.appendChild(popup);
|
||||
}
|
||||
|
||||
popup.style.display = 'block';
|
||||
popup.style.position = 'absolute';
|
||||
popup.style.zIndex = '9999';
|
||||
|
||||
const rect = input.getBoundingClientRect();
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
||||
|
||||
popup.style.top = (rect.bottom + scrollTop) + 'px';
|
||||
popup.style.left = (rect.left + scrollLeft) + 'px';
|
||||
}
|
||||
|
||||
toggleCalendar(): void {
|
||||
this.calendarVisible = !this.calendarVisible;
|
||||
if (this.calendarVisible) {
|
||||
this.showCalendar();
|
||||
this.updateAllCaches();
|
||||
} else {
|
||||
this.hideCalendar();
|
||||
}
|
||||
}
|
||||
|
||||
showCalendar(): void {
|
||||
this.calendarVisible = true;
|
||||
setTimeout(() => {
|
||||
this.positionCalendarPopup();
|
||||
});
|
||||
}
|
||||
|
||||
hideCalendar(): void {
|
||||
this.calendarVisible = false;
|
||||
if (this.calendarPopup) {
|
||||
this.calendarPopup.nativeElement.style.display = 'none';
|
||||
}
|
||||
|
||||
if (this.selectedDate) {
|
||||
this.currentDate = new Date(this.selectedDate);
|
||||
} else {
|
||||
this.currentDate = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('document:mousedown', ['$event'])
|
||||
onClickOutside(event: MouseEvent): void {
|
||||
if (this.calendarVisible &&
|
||||
!this.elementRef.nativeElement.contains(event.target) &&
|
||||
this.calendarPopup &&
|
||||
!this.calendarPopup.nativeElement.contains(event.target)) {
|
||||
this.hideCalendar();
|
||||
}
|
||||
}
|
||||
|
||||
// --- LOGICA DATE ---
|
||||
isDateDisabled(date: Date): boolean {
|
||||
// Disabilita date precedenti alla minDate
|
||||
if (this.minDate && date < this.minDate) return true;
|
||||
|
||||
// Disabilita date precedenti alla maxDate
|
||||
if (this.maxDate && date > this.maxDate) return true;
|
||||
|
||||
// Disabilita per giorno della settimana
|
||||
if (this.disabledWeekdays.indexOf(date.getDay()) !== -1) return true;
|
||||
|
||||
// Disabilita per range
|
||||
for (const range of this.disabledDateRanges) {
|
||||
if (date >= range.start && date <= range.end) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- NAVIGATION ---
|
||||
prev(): void {
|
||||
if (this.view === 'days') {
|
||||
this.currentDate.setMonth(this.currentDate.getMonth() - 1);
|
||||
} else if (this.view === 'months') {
|
||||
this.currentDate.setFullYear(this.currentDate.getFullYear() - 1);
|
||||
} else {
|
||||
this.currentDate.setFullYear(this.currentDate.getFullYear() - 20);
|
||||
}
|
||||
this.currentDate = new Date(this.currentDate); // Forza refresh
|
||||
this.updateAllCaches();
|
||||
}
|
||||
|
||||
next(): void {
|
||||
if (this.view === 'days') {
|
||||
this.currentDate.setMonth(this.currentDate.getMonth() + 1);
|
||||
} else if (this.view === 'months') {
|
||||
this.currentDate.setFullYear(this.currentDate.getFullYear() + 1);
|
||||
} else {
|
||||
this.currentDate.setFullYear(this.currentDate.getFullYear() + 20);
|
||||
}
|
||||
this.currentDate = new Date(this.currentDate); // Forza refresh
|
||||
this.updateAllCaches();
|
||||
}
|
||||
|
||||
goToday(): void {
|
||||
this.currentDate = new Date();
|
||||
this.selectedDate = new Date();
|
||||
this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
|
||||
this.view = 'days';
|
||||
this.dateChange.emit(this.selectedDate);
|
||||
this.updateAllCaches();
|
||||
}
|
||||
|
||||
clearSelection(): void {
|
||||
this.selectedDate = null;
|
||||
this.formattedSelectedDate = '';
|
||||
this.dateChange.emit(null);
|
||||
}
|
||||
|
||||
confirmDate(): void {
|
||||
// Metodo usato per confermare esplicitamente la data corrente
|
||||
this.dateChange.emit(this.selectedDate);
|
||||
this.hideCalendar();
|
||||
}
|
||||
|
||||
changeView(): void {
|
||||
if (this.view === 'days') {
|
||||
this.view = 'months';
|
||||
} else if (this.view === 'months') {
|
||||
this.view = 'years';
|
||||
}
|
||||
this.updateAllCaches();
|
||||
}
|
||||
|
||||
// --- SELEZIONE DATE ---
|
||||
selectDate(date: Date): void {
|
||||
if (this.isDateDisabled(date)) return;
|
||||
|
||||
this.selectedDate = date;
|
||||
this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
|
||||
// Non emettiamo qui l'evento dateChange per evitare doppie emissioni
|
||||
this.updateAllCaches();
|
||||
}
|
||||
|
||||
selectMonth(monthIndex: number): void {
|
||||
if (this.isMonthDisabled(monthIndex)) return;
|
||||
|
||||
this.currentDate.setMonth(monthIndex);
|
||||
this.currentDate = new Date(this.currentDate); // Forza refresh
|
||||
this.view = 'days';
|
||||
this.updateAllCaches();
|
||||
}
|
||||
|
||||
selectYear(year: number): void {
|
||||
if (this.isYearDisabled(year)) return;
|
||||
|
||||
this.currentDate.setFullYear(year);
|
||||
this.currentDate = new Date(this.currentDate); // Forza refresh
|
||||
this.view = 'months';
|
||||
this.updateAllCaches();
|
||||
}
|
||||
|
||||
selectDayFromPrevMonth(day: number): void {
|
||||
const year = this.currentDate.getFullYear();
|
||||
const month = this.currentDate.getMonth();
|
||||
const prevMonthDate = new Date(year, month - 1, day);
|
||||
|
||||
if (this.isDateDisabled(prevMonthDate)) return;
|
||||
|
||||
this.currentDate.setMonth(month - 1);
|
||||
this.currentDate = new Date(this.currentDate); // Forza refresh
|
||||
this.selectedDate = prevMonthDate;
|
||||
this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
|
||||
this.dateChange.emit(this.selectedDate);
|
||||
this.updateAllCaches();
|
||||
this.hideCalendar(); // Chiudi il calendario dopo la selezione
|
||||
}
|
||||
|
||||
selectDayFromNextMonth(day: number): void {
|
||||
const year = this.currentDate.getFullYear();
|
||||
const month = this.currentDate.getMonth();
|
||||
const nextMonthDate = new Date(year, month + 1, day);
|
||||
|
||||
if (this.isDateDisabled(nextMonthDate)) return;
|
||||
|
||||
this.currentDate.setMonth(month + 1);
|
||||
this.currentDate = new Date(this.currentDate); // Forza refresh
|
||||
this.selectedDate = nextMonthDate;
|
||||
this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
|
||||
this.dateChange.emit(this.selectedDate);
|
||||
this.updateAllCaches();
|
||||
this.hideCalendar(); // Chiudi il calendario dopo la selezione
|
||||
}
|
||||
|
||||
// --- UTILITY PER MESI E ANNI ---
|
||||
isMonthDisabled(monthIndex: number): boolean {
|
||||
// Se non ci sono restrizioni, il mese è abilitato
|
||||
if (!this.minDate && !this.maxDate && (!this.disabledDateRanges || this.disabledDateRanges.length === 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const year = this.currentDate.getFullYear();
|
||||
const firstDayOfMonth = new Date(year, monthIndex, 1, 0, 0, 0, 0);
|
||||
const lastDayOfMonth = new Date(year, monthIndex + 1, 0, 23, 59, 59, 999);
|
||||
|
||||
// Disabilita mese se completamente fuori dai limiti
|
||||
if (this.minDate && lastDayOfMonth < this.minDate) return true;
|
||||
if (this.maxDate && firstDayOfMonth > this.maxDate) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isYearDisabled(year: number): boolean {
|
||||
// Se non ci sono restrizioni, l'anno è abilitato
|
||||
if (!this.minDate && !this.maxDate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const firstDayOfYear = new Date(year, 0, 1, 0, 0, 0, 0);
|
||||
const lastDayOfYear = new Date(year, 11, 31, 23, 59, 59, 999);
|
||||
|
||||
// Disabilita anno se completamente fuori dai limiti
|
||||
if (this.minDate && lastDayOfYear < this.minDate) return true;
|
||||
if (this.maxDate && firstDayOfYear > this.maxDate) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- INPUT HANDLING ---
|
||||
onDateInputChange(event: Event): void {
|
||||
const value = (event.target as HTMLInputElement).value;
|
||||
const datePattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
|
||||
|
||||
if (datePattern.test(value)) {
|
||||
const [, dd, mm, yyyy] = value.match(datePattern);
|
||||
const date = new Date(parseInt(yyyy), parseInt(mm) - 1, parseInt(dd));
|
||||
|
||||
// Controlla che sia una data valida
|
||||
if (date &&
|
||||
date.getDate() === parseInt(dd) &&
|
||||
(date.getMonth() + 1) === parseInt(mm) &&
|
||||
date.getFullYear() === parseInt(yyyy)) {
|
||||
|
||||
if (!this.isDateDisabled(date)) {
|
||||
this.selectedDate = date;
|
||||
this.currentDate = new Date(date);
|
||||
this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
|
||||
this.dateChange.emit(this.selectedDate);
|
||||
this.updateAllCaches();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- CALENDAR DATA ---
|
||||
getDaysInMonth(year: number, month: number): number {
|
||||
return new Date(year, month + 1, 0).getDate();
|
||||
}
|
||||
|
||||
// Metodo per aggiornare tutte le cache quando cambiano i dati
|
||||
private updateAllCaches(): void {
|
||||
this.updatePreviousMonthDaysCache();
|
||||
this.updateCurrentMonthDaysCache();
|
||||
this.updateNextMonthDaysCache();
|
||||
this.updateVisibleMonthsCache();
|
||||
this.updateVisibleYearsCache();
|
||||
}
|
||||
|
||||
// Metodi per aggiornare le singole cache
|
||||
private updatePreviousMonthDaysCache(): void {
|
||||
const year = this.currentDate.getFullYear();
|
||||
const month = this.currentDate.getMonth();
|
||||
const startDay = this.getFirstDayOfMonth(year, month);
|
||||
const daysInPrevMonth = this.getDaysInMonth(year, month - 1);
|
||||
|
||||
const days = [];
|
||||
for (let i = startDay; i > 0; i--) {
|
||||
const day = daysInPrevMonth - i + 1;
|
||||
const prevMonthDate = new Date(year, month - 1, day);
|
||||
days.push({
|
||||
day,
|
||||
disabled: this.isDateDisabled(prevMonthDate)
|
||||
});
|
||||
}
|
||||
|
||||
this._previousMonthDaysCache = days;
|
||||
}
|
||||
|
||||
private updateCurrentMonthDaysCache(): void {
|
||||
const year = this.currentDate.getFullYear();
|
||||
const month = this.currentDate.getMonth();
|
||||
const daysInMonth = this.getDaysInMonth(year, month);
|
||||
const today = new Date();
|
||||
|
||||
const days = [];
|
||||
for (let i = 1; i <= daysInMonth; i++) {
|
||||
const date = new Date(year, month, i);
|
||||
days.push({
|
||||
day: i,
|
||||
isToday: this.sameDate(today, date),
|
||||
isSelected: this.selectedDate ? this.sameDate(this.selectedDate, date) : false,
|
||||
disabled: this.isDateDisabled(date)
|
||||
});
|
||||
}
|
||||
|
||||
this._currentMonthDaysCache = days;
|
||||
}
|
||||
|
||||
private updateNextMonthDaysCache(): void {
|
||||
const year = this.currentDate.getFullYear();
|
||||
const month = this.currentDate.getMonth();
|
||||
const startDay = this.getFirstDayOfMonth(year, month);
|
||||
const daysInMonth = this.getDaysInMonth(year, month);
|
||||
|
||||
const totalCells = 42; // 7 giorni x 6 settimane
|
||||
const nextMonthDays = totalCells - (startDay + daysInMonth);
|
||||
|
||||
const days = [];
|
||||
for (let i = 1; i <= nextMonthDays; i++) {
|
||||
const nextMonthDate = new Date(year, month + 1, i);
|
||||
days.push({
|
||||
day: i,
|
||||
disabled: this.isDateDisabled(nextMonthDate)
|
||||
});
|
||||
}
|
||||
|
||||
this._nextMonthDaysCache = days;
|
||||
}
|
||||
|
||||
private updateVisibleMonthsCache(): void {
|
||||
const currentYear = this.currentDate.getFullYear();
|
||||
const today = new Date();
|
||||
|
||||
const months = this.months.map((name, idx) => ({
|
||||
monthIndex: idx,
|
||||
name,
|
||||
isCurrentMonth: currentYear === today.getFullYear() && idx === today.getMonth(),
|
||||
isSelected: this.selectedDate ?
|
||||
currentYear === this.selectedDate.getFullYear() &&
|
||||
idx === this.selectedDate.getMonth() : false,
|
||||
disabled: this.isMonthDisabled(idx)
|
||||
}));
|
||||
|
||||
this._visibleMonthsCache = months;
|
||||
}
|
||||
|
||||
private updateVisibleYearsCache(): void {
|
||||
const base = Math.floor(this.currentDate.getFullYear() / 20) * 20;
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const years = [];
|
||||
for (let y = base; y < base + 20; y++) {
|
||||
years.push({
|
||||
year: y,
|
||||
isCurrentYear: y === currentYear,
|
||||
isSelected: this.selectedDate ? y === this.selectedDate.getFullYear() : false,
|
||||
disabled: this.isYearDisabled(y)
|
||||
});
|
||||
}
|
||||
|
||||
this._visibleYearsCache = years;
|
||||
}
|
||||
|
||||
getFirstDayOfMonth(year: number, month: number): number {
|
||||
return (new Date(year, month, 1).getDay() + 6) % 7; // Lunedì=0, Domenica=6
|
||||
}
|
||||
|
||||
getPreviousMonthDays(): { day: number, disabled: boolean }[] {
|
||||
return this._previousMonthDaysCache;
|
||||
}
|
||||
|
||||
getCurrentMonthDays(): { day: number, isToday: boolean, isSelected: boolean, disabled: boolean }[] {
|
||||
return this._currentMonthDaysCache;
|
||||
}
|
||||
|
||||
getNextMonthDays(): { day: number, disabled: boolean }[] {
|
||||
return this._nextMonthDaysCache;
|
||||
}
|
||||
|
||||
getVisibleYears(): { year: number, isCurrentYear: boolean, isSelected: boolean, disabled: boolean }[] {
|
||||
return this._visibleYearsCache;
|
||||
}
|
||||
|
||||
getVisibleMonths(): { monthIndex: number, name: string, isCurrentMonth: boolean, isSelected: boolean, disabled: boolean }[] {
|
||||
return this._visibleMonthsCache;
|
||||
}
|
||||
|
||||
get yearRange(): string {
|
||||
if (this.view === 'years') {
|
||||
const base = Math.floor(this.currentDate.getFullYear() / 20) * 20;
|
||||
return `${base} - ${base + 19}`;
|
||||
}
|
||||
return this.currentDate.getFullYear().toString();
|
||||
}
|
||||
|
||||
get monthYearHeader(): string {
|
||||
if (this.view === 'days') {
|
||||
return `${this.months[this.currentDate.getMonth()]} ${this.currentDate.getFullYear()}`;
|
||||
}
|
||||
return this.yearRange;
|
||||
}
|
||||
|
||||
// --- AZIONI SUI GIORNI, MESI E ANNI ---
|
||||
onPrevMonthDayClick(day: { day: number, disabled: boolean }): void {
|
||||
if (day.disabled) return;
|
||||
this.selectDayFromPrevMonth(day.day);
|
||||
// Non serve chiamare hideCalendar() qui perché lo fa già selectDayFromPrevMonth
|
||||
}
|
||||
|
||||
onCurrentMonthDayClick(day: { day: number, isToday: boolean, isSelected: boolean, disabled: boolean }): void {
|
||||
if (day.disabled) return;
|
||||
const date = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth(), day.day);
|
||||
this.selectDate(date);
|
||||
this.dateChange.emit(this.selectedDate);
|
||||
this.hideCalendar();
|
||||
}
|
||||
|
||||
onNextMonthDayClick(day: { day: number, disabled: boolean }): void {
|
||||
if (day.disabled) return;
|
||||
this.selectDayFromNextMonth(day.day);
|
||||
// Non serve chiamare hideCalendar() qui perché lo fa già selectDayFromNextMonth
|
||||
}
|
||||
|
||||
onMonthClick(month: { monthIndex: number, name: string, isCurrentMonth: boolean, isSelected: boolean, disabled: boolean }): void {
|
||||
if (month.disabled) return;
|
||||
this.selectMonth(month.monthIndex);
|
||||
}
|
||||
|
||||
onYearClick(yearObj: { year: number, isCurrentYear: boolean, isSelected: boolean, disabled: boolean }): void {
|
||||
if (yearObj.disabled) return;
|
||||
this.selectYear(yearObj.year);
|
||||
}
|
||||
}
|
@ -78,6 +78,17 @@
|
||||
</nbp-input-container>
|
||||
</div>
|
||||
|
||||
<p class="new-part">Fideuram Calendar generic (nbp-input-container + bp-fid-calendar-generic)</p>
|
||||
<div>
|
||||
<nbp-input-container [nbpStyle]="_nbpStyle.DEFAULT" [nbpLabel]="'Default'">
|
||||
<nbp-fid-calendar-generic [id]="'calendarId'" [name]="'calendarName'" [dateFormat]="'dd/MM/yyyy'"
|
||||
[minDate]="fidMinDate" [maxDate]="fidMaxDate" [disabled]="false"
|
||||
[(ngModel)]="fidCalendarModel" (dateChange)="onDateChange($event)">
|
||||
</nbp-fid-calendar-generic>
|
||||
</nbp-input-container>
|
||||
</div>
|
||||
|
||||
|
||||
<p class="new-part">Combo (nbp-input-container + nbp-combo)</p>
|
||||
<div>
|
||||
<nbp-input-container [nbpStyle]="_nbpStyle.DEFAULT" [nbpLabel]="'Default'">
|
||||
@ -87,7 +98,8 @@
|
||||
</nbp-input-container>
|
||||
<nbp-input-container [nbpStyle]="_nbpStyle.DEFAULT" [nbpLabel]="'Default'">
|
||||
<nbp-combo [id]="'comboDefault'" [name]="'comboDefault'" [nbpStyle]="_nbpStyle.DEFAULT"
|
||||
[nbpDataSource]="comboDatasource" [(ngModel)]="comboSelectedValue" [nbpShowEmptyValue]="true" [disabled]="true">
|
||||
[nbpDataSource]="comboDatasource" [(ngModel)]="comboSelectedValue" [nbpShowEmptyValue]="true"
|
||||
[disabled]="true">
|
||||
</nbp-combo>
|
||||
</nbp-input-container>
|
||||
</div>
|
||||
@ -149,8 +161,7 @@
|
||||
[nbpDataSource]="tableDs" [nbpAutoBind]="true" [nbpSelectionType]="tableSelectionType"
|
||||
[nbpLayoutAuto]="true">
|
||||
|
||||
<nbp-table-column nbpId='headerField' nbpTitle='Header field' nbpField='headerField'
|
||||
[nbpVisible]='true'>
|
||||
<nbp-table-column nbpId='headerField' nbpTitle='Header field' nbpField='headerField' [nbpVisible]='true'>
|
||||
</nbp-table-column>
|
||||
|
||||
<nbp-table-column nbpId='headerField2' nbpTitle='Header field 2' nbpField='headerField2'
|
||||
@ -171,12 +182,12 @@
|
||||
[nbpDataSource]="campiStandardData" ariaLabel="multi selection table" [nbpAutoBind]="true"
|
||||
[nbpSelectionType]="multi">
|
||||
|
||||
<nbp-table-column nbpId='headerField' nbpTitle='Header field' nbpField='headerField'
|
||||
[nbpSortable]='true' [nbpVisible]='true'>
|
||||
<nbp-table-column nbpId='headerField' nbpTitle='Header field' nbpField='headerField' [nbpSortable]='true'
|
||||
[nbpVisible]='true'>
|
||||
</nbp-table-column>
|
||||
|
||||
<nbp-table-column nbpId='headerField2' nbpTitle='Header field 2' nbpField='headerField2'
|
||||
[nbpSortable]='true' [nbpVisible]='true'>
|
||||
<nbp-table-column nbpId='headerField2' nbpTitle='Header field 2' nbpField='headerField2' [nbpSortable]='true'
|
||||
[nbpVisible]='true'>
|
||||
</nbp-table-column>
|
||||
|
||||
</nbp-table>
|
||||
|
@ -43,6 +43,19 @@ export class Showcase1Component extends NbpBaseComponent {
|
||||
|
||||
startDate: NgbDateStruct = { year: 2023, month: 3, day: 15 };
|
||||
endDate: NgbDateStruct = { year: 2023, month: 8, day: 15 };
|
||||
|
||||
// FIDEURAM CALENDAR GENERIC
|
||||
fidCalendarModel: Date = null;
|
||||
|
||||
// Date di limite per l'esempio min/max
|
||||
fidMinDate: Date = new Date(2025, 4, 1); // 1 maggio 2025
|
||||
fidMaxDate: Date = new Date(2025, 5, 30); // 30 giugno 2025
|
||||
|
||||
// Range di date disabilitate per l'esempio
|
||||
fidDisabledDateRanges: { start: Date, end: Date }[] = [
|
||||
{ start: new Date(2025, 4, 10), end: new Date(2025, 4, 15) }, // 10-15 maggio 2025
|
||||
{ start: new Date(2025, 4, 25), end: new Date(2025, 4, 28) } // 25-28 maggio 2025
|
||||
];
|
||||
//
|
||||
|
||||
//
|
||||
@ -192,6 +205,12 @@ export class Showcase1Component extends NbpBaseComponent {
|
||||
array = array.map(convertPageToRowModel);
|
||||
this.table2Data.setData(array, pageNumber, sortField, pageSize);
|
||||
}
|
||||
|
||||
// Metodo per gestire il cambio di data nel calendario Fideuram
|
||||
onDateChange(date: Date): void {
|
||||
console.log('Data selezionata:', date);
|
||||
// Puoi aggiungere qui la logica per gestire il cambio di data
|
||||
}
|
||||
}
|
||||
|
||||
export class RowExpanderTreeExpandedTest implements RowDataExpander {
|
||||
|
Loading…
x
Reference in New Issue
Block a user