167 lines
3.1 KiB
TypeScript
167 lines
3.1 KiB
TypeScript
import {
|
|
Component,
|
|
EventEmitter,
|
|
Input,
|
|
Output,
|
|
ViewChild,
|
|
computed,
|
|
effect,
|
|
signal,
|
|
} from '@angular/core';
|
|
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormControl } from '@angular/forms';
|
|
|
|
import dayjs from 'dayjs';
|
|
import jalaliday from 'jalaliday';
|
|
|
|
import { InputText } from 'primeng/inputtext';
|
|
import { Popover } from 'primeng/popover';
|
|
|
|
import { Maybe } from '@/core';
|
|
import { formatJalali } from '@/utils';
|
|
|
|
import { UikitFieldComponent } from '../uikit-field.component';
|
|
|
|
import {
|
|
MonthInfo,
|
|
MonthlyWeekDate,
|
|
daysName,
|
|
prepareMonthInfo,
|
|
prepareWeekDayDates,
|
|
} from './monthly-calendar.helper.util';
|
|
|
|
dayjs.extend(jalaliday);
|
|
|
|
@Component({
|
|
selector: 'app-datepicker',
|
|
standalone: true,
|
|
templateUrl: './datepicker.component.html',
|
|
imports: [CommonModule, Popover, InputText, UikitFieldComponent],
|
|
})
|
|
export class DatepickerComponent {
|
|
@Input({ required: true })
|
|
control!: FormControl<Maybe<string>>;
|
|
|
|
@Input()
|
|
alignment: 'vertical' | 'horizontal' = 'vertical';
|
|
|
|
@Input()
|
|
name = 'date';
|
|
|
|
@Input()
|
|
label = 'تاریخ';
|
|
|
|
@Input()
|
|
closeOnSelect = true;
|
|
|
|
/**
|
|
* Gregorian YYYY-MM-DD
|
|
*/
|
|
@Input()
|
|
min?: string;
|
|
|
|
/**
|
|
* Gregorian YYYY-MM-DD
|
|
*/
|
|
@Input()
|
|
max?: string;
|
|
|
|
@Input()
|
|
closedDays: number[] = [];
|
|
|
|
@Output()
|
|
valueChange = new EventEmitter<string>();
|
|
|
|
@Output()
|
|
clickOnDayEvent = new EventEmitter<MonthlyWeekDate>();
|
|
|
|
@ViewChild('op')
|
|
op!: Popover;
|
|
|
|
readonly daysName = daysName;
|
|
|
|
/**
|
|
* Currently displayed month
|
|
*/
|
|
private readonly activeDateSignal = signal(dayjs().format('YYYY-MM-DD'));
|
|
|
|
monthInfo = computed<MonthInfo>(() => prepareMonthInfo(this.activeDateSignal()));
|
|
|
|
weekDaysDates = computed(() => prepareWeekDayDates(this.monthInfo(), this.min, this.max));
|
|
|
|
constructor() {
|
|
effect(() => {
|
|
const value = this.value;
|
|
|
|
if (value) {
|
|
this.activeDateSignal.set(value);
|
|
}
|
|
});
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
if (this.control?.value) {
|
|
this.activeDateSignal.set(this.control.value);
|
|
}
|
|
|
|
this.control.valueChanges.subscribe((value) => {
|
|
if (value) {
|
|
this.activeDateSignal.set(value);
|
|
}
|
|
});
|
|
}
|
|
|
|
get value(): Maybe<string> {
|
|
return this.control?.value ?? null;
|
|
}
|
|
|
|
get valueToShow(): Maybe<string> {
|
|
if (!this.value) {
|
|
return null;
|
|
}
|
|
|
|
return formatJalali(this.value);
|
|
}
|
|
|
|
get activeDate(): string {
|
|
return this.activeDateSignal();
|
|
}
|
|
|
|
set activeDate(date: string) {
|
|
this.activeDateSignal.set(date);
|
|
}
|
|
|
|
changeMonth(months: number): void {
|
|
this.activeDateSignal.set(
|
|
dayjs(this.activeDateSignal()).add(months, 'month').format('YYYY-MM-DD')
|
|
);
|
|
}
|
|
|
|
selectDate(day: MonthlyWeekDate): void {
|
|
if (day.isDisabled) {
|
|
return;
|
|
}
|
|
|
|
this.control.setValue(day.gregorianDate);
|
|
|
|
this.control.markAsDirty();
|
|
this.control.markAsTouched();
|
|
|
|
this.valueChange.emit(day.gregorianDate);
|
|
this.clickOnDayEvent.emit(day);
|
|
|
|
if (this.closeOnSelect) {
|
|
this.op.hide();
|
|
}
|
|
}
|
|
|
|
isSelected(day: MonthlyWeekDate): boolean {
|
|
return this.value === day.gregorianDate;
|
|
}
|
|
|
|
trackByIndex(index: number): number {
|
|
return index;
|
|
}
|
|
}
|