igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1 lines • 253 kB
Source Map (JSON)
{"version":3,"file":"igniteui-angular-calendar.mjs","sources":["../../../projects/igniteui-angular/calendar/src/calendar/calendar.directives.ts","../../../projects/igniteui-angular/calendar/src/calendar/calendar.ts","../../../projects/igniteui-angular/calendar/src/calendar/common/calendar-view.directive.ts","../../../projects/igniteui-angular/calendar/src/calendar/months-view/months-view.component.ts","../../../projects/igniteui-angular/calendar/src/calendar/months-view/months-view.component.html","../../../projects/igniteui-angular/calendar/src/calendar/years-view/years-view.component.ts","../../../projects/igniteui-angular/calendar/src/calendar/years-view/years-view.component.html","../../../projects/igniteui-angular/calendar/src/calendar/days-view/day-item.component.ts","../../../projects/igniteui-angular/calendar/src/calendar/days-view/day-item.component.html","../../../projects/igniteui-angular/calendar/src/calendar/calendar.services.ts","../../../projects/igniteui-angular/calendar/src/calendar/calendar-base.ts","../../../projects/igniteui-angular/calendar/src/calendar/day-digit.pipe.ts","../../../projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.ts","../../../projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.html","../../../projects/igniteui-angular/calendar/src/calendar/months-view.pipe.ts","../../../projects/igniteui-angular/calendar/src/calendar/calendar.component.ts","../../../projects/igniteui-angular/calendar/src/calendar/calendar.component.html","../../../projects/igniteui-angular/calendar/src/calendar/month-picker/month-picker.component.ts","../../../projects/igniteui-angular/calendar/src/calendar/month-picker/month-picker.component.html","../../../projects/igniteui-angular/calendar/src/calendar/public_api.ts","../../../projects/igniteui-angular/calendar/src/calendar/calendar.module.ts","../../../projects/igniteui-angular/calendar/src/igniteui-angular-calendar.ts"],"sourcesContent":["/**\n * This file contains all the directives used by the @link IgxCalendarComponent.\n * Except for the directives which are used for templating the calendar itself\n * you should generally not use them directly.\n *\n * @preferred\n */\nimport { Directive, EventEmitter, HostBinding, HostListener, Input, InjectionToken, Output, TemplateRef, ElementRef, AfterViewInit, OnDestroy, NgZone, inject } from '@angular/core';\nimport { fromEvent, Subject, interval } from 'rxjs';\nimport { takeUntil, debounce, tap } from 'rxjs/operators';\nimport { CalendarDay, PlatformUtil } from 'igniteui-angular/core';\n\nexport const IGX_CALENDAR_VIEW_ITEM =\n new InjectionToken<IgxCalendarMonthDirective | IgxCalendarYearDirective>('IgxCalendarViewItem');\n\n@Directive()\nexport abstract class IgxCalendarViewBaseDirective {\n public elementRef = inject(ElementRef);\n\n @Input()\n public value: Date;\n\n @Input()\n public date: Date;\n\n @Input()\n public showActive = false;\n\n @Output()\n public itemSelection = new EventEmitter<Date>();\n\n public get nativeElement() {\n return this.elementRef.nativeElement;\n }\n\n @HostListener('mousedown', ['$event'])\n public onMouseDown(event: MouseEvent) {\n event.preventDefault();\n this.itemSelection.emit(this.value);\n }\n\n public abstract get isCurrent(): boolean;\n public abstract get isSelected(): boolean;\n public abstract get isActive(): boolean;\n}\n\n/**\n * @hidden\n */\n@Directive({\n selector: '[igxCalendarYear]',\n providers: [\n { provide: IGX_CALENDAR_VIEW_ITEM, useExisting: IgxCalendarYearDirective }\n ],\n exportAs: 'igxCalendarYear',\n standalone: true\n})\nexport class IgxCalendarYearDirective extends IgxCalendarViewBaseDirective {\n @HostBinding('class.igx-calendar-view__item--current')\n public get isCurrent(): boolean {\n return CalendarDay.today.year === this.value.getFullYear();\n }\n\n @HostBinding('class.igx-calendar-view__item--selected')\n public get isSelected(): boolean {\n return this.value.getFullYear() === this.date.getFullYear();\n }\n\n @HostBinding('class.igx-calendar-view__item--active')\n public get isActive(): boolean {\n return this.isSelected && this.showActive;\n }\n}\n\n@Directive({\n selector: '[igxCalendarMonth]',\n providers: [\n { provide: IGX_CALENDAR_VIEW_ITEM, useExisting: IgxCalendarMonthDirective }\n ],\n exportAs: 'igxCalendarMonth',\n standalone: true\n})\nexport class IgxCalendarMonthDirective extends IgxCalendarViewBaseDirective {\n @HostBinding('class.igx-calendar-view__item--current')\n public get isCurrent(): boolean {\n const today = CalendarDay.today;\n const date = CalendarDay.from(this.value);\n return date.year === today.year && date.month === today.month;\n }\n\n @HostBinding('class.igx-calendar-view__item--selected')\n public get isSelected(): boolean {\n return (this.value.getFullYear() === this.date.getFullYear() &&\n this.value.getMonth() === this.date.getMonth()\n );\n }\n\n @HostBinding('class.igx-calendar-view__item--active')\n public get isActive(): boolean {\n return this.isSelected && this.showActive;\n }\n}\n\n/**\n * @hidden\n */\n@Directive({\n selector: '[igxCalendarHeaderTitle]',\n standalone: true\n})\nexport class IgxCalendarHeaderTitleTemplateDirective {\n public template = inject<TemplateRef<any>>(TemplateRef);\n}\n\n/**\n * @hidden\n */\n@Directive({\n selector: '[igxCalendarHeader]',\n standalone: true\n})\nexport class IgxCalendarHeaderTemplateDirective {\n public template = inject<TemplateRef<any>>(TemplateRef);\n}\n\n/**\n * @hidden\n */\n@Directive({\n selector: '[igxCalendarSubheader]',\n standalone: true\n})\nexport class IgxCalendarSubheaderTemplateDirective {\n public template = inject<TemplateRef<any>>(TemplateRef);\n}\n\n/**\n * @hidden\n */\n@Directive({\n selector: '[igxCalendarScrollPage]',\n standalone: true\n})\nexport class IgxCalendarScrollPageDirective implements AfterViewInit, OnDestroy {\n private element = inject(ElementRef);\n private zone = inject(NgZone);\n protected platform = inject(PlatformUtil);\n\n /**\n * A callback function to be invoked when increment/decrement page is triggered.\n *\n * @hidden\n */\n @Input()\n public startScroll: (keydown?: boolean) => void;\n\n /**\n * A callback function to be invoked when increment/decrement page stops.\n *\n * @hidden\n */\n @Input()\n public stopScroll: (event: any) => void;\n\n /**\n * @hidden\n */\n private destroy$ = new Subject<boolean>();\n\n /**\n * @hidden\n */\n @HostListener('mousedown', ['$event'])\n public onMouseDown(event: MouseEvent) {\n event.preventDefault();\n this.startScroll();\n }\n\n /**\n * @hidden\n */\n @HostListener('mouseup', ['$event'])\n public onMouseUp(event: MouseEvent) {\n this.stopScroll(event);\n }\n\n /**\n * @hidden\n */\n public ngAfterViewInit() {\n fromEvent(this.element.nativeElement, 'keyup').pipe(\n debounce(() => interval(100)),\n takeUntil(this.destroy$)\n ).subscribe((event: KeyboardEvent) => {\n this.stopScroll(event);\n });\n\n this.zone.runOutsideAngular(() => {\n fromEvent(this.element.nativeElement, 'keydown').pipe(\n tap((event: KeyboardEvent) => {\n if (this.platform.isActivationKey(event)) {\n event.preventDefault();\n event.stopPropagation();\n }\n }),\n debounce(() => interval(100)),\n takeUntil(this.destroy$)\n ).subscribe((event: KeyboardEvent) => {\n if (this.platform.isActivationKey(event)) {\n this.zone.run(() => this.startScroll(true));\n }\n });\n });\n }\n\n /**\n * @hidden\n */\n public ngOnDestroy() {\n this.destroy$.next(true);\n this.destroy$.complete();\n }\n}\n","\n/**\n * Sets the selection type - single, multi or range.\n */\nexport const CalendarSelection = {\n SINGLE: 'single',\n MULTI: 'multi',\n RANGE: 'range'\n} as const;\nexport type CalendarSelection = (typeof CalendarSelection)[keyof typeof CalendarSelection];\n\nexport const enum ScrollDirection {\n PREV = 'prev',\n NEXT = 'next',\n NONE = 'none'\n}\n\nexport interface IViewDateChangeEventArgs {\n previousValue: Date;\n currentValue: Date;\n}\n\nexport const IgxCalendarView = {\n Month: 'month',\n Year: 'year',\n Decade: 'decade'\n} as const;\n\n/**\n * Determines the Calendar active view - days, months or years.\n */\nexport type IgxCalendarView = (typeof IgxCalendarView)[keyof typeof IgxCalendarView];\n\nconst MDAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nconst FEBRUARY = 1;\n\nexport const range = (start = 0, stop: number, step = 1) => {\n const res = [];\n const cur = (stop === undefined) ? 0 : start;\n const max = (stop === undefined) ? start : stop;\n for (let i = cur; step < 0 ? i > max : i < max; i += step) {\n res.push(i);\n }\n return res;\n};\n\n/**\n * Returns true for leap years, false for non-leap years.\n *\n * @export\n * @param year\n * @returns\n */\nexport const isLeap = (year: number): boolean => (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0));\n\nexport const weekDay = (year: number, month: number, day: number): number => new Date(year, month, day).getDay();\n\n/**\n * Return weekday and number of days for year, month.\n *\n * @export\n * @param year\n * @param month\n * @returns\n */\nexport const monthRange = (year: number, month: number): number[] => {\n if ((month < 0) || (month > 11)) {\n throw new Error('Invalid month specified');\n }\n const day = weekDay(year, month, 1);\n let nDays = MDAYS[month];\n if ((month === FEBRUARY) && (isLeap(year))) {\n nDays++;\n }\n return [day, nDays];\n};\n\nexport interface IFormattedParts {\n value: string;\n literal?: string;\n combined: string;\n}\n\nexport interface IFormattingOptions {\n day?: 'numeric' | '2-digit';\n month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';\n weekday?: 'long' | 'short' | 'narrow';\n year?: 'numeric' | '2-digit';\n}\n\nexport interface IFormattingViews {\n day?: boolean;\n month?: boolean;\n year?: boolean;\n}\n\nexport class Calendar {\n public timedelta(date: Date, interval: string, units: number): Date {\n const ret = new Date(date);\n\n const checkRollover = () => {\n if (ret.getDate() !== date.getDate()) {\n ret.setDate(0);\n }\n };\n\n switch (interval.toLowerCase()) {\n case 'year':\n ret.setFullYear(ret.getFullYear() + units);\n checkRollover();\n break;\n case 'quarter':\n ret.setMonth(ret.getMonth() + 3 * units);\n checkRollover();\n break;\n case 'month':\n ret.setMonth(ret.getMonth() + units);\n checkRollover();\n break;\n case 'week':\n ret.setDate(ret.getDate() + 7 * units);\n break;\n case 'day':\n ret.setDate(ret.getDate() + units);\n break;\n case 'hour':\n ret.setTime(ret.getTime() + units * 3600000);\n break;\n case 'minute':\n ret.setTime(ret.getTime() + units * 60000);\n break;\n case 'second':\n ret.setTime(ret.getTime() + units * 1000);\n break;\n default:\n throw new Error('Invalid interval specifier');\n }\n\n return ret;\n }\n}\n","import {\n Output,\n EventEmitter,\n Input,\n HostListener,\n ViewChildren,\n QueryList,\n booleanAttribute,\n Directive,\n HostBinding,\n InjectionToken,\n inject,\n} from \"@angular/core\";\nimport { noop } from \"rxjs\";\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from \"@angular/forms\";\nimport {\n IGX_CALENDAR_VIEW_ITEM,\n IgxCalendarMonthDirective,\n IgxCalendarYearDirective,\n} from \"../calendar.directives\";\nimport { CalendarDay, DateRangeType, DayInterval, getNextActiveDate, isDate, isDateInRanges } from 'igniteui-angular/core';\n\n\nexport enum IgxCalendarNavDirection {\n NEXT = 1,\n PREV = -1,\n}\n\nexport const DAY_INTERVAL_TOKEN = new InjectionToken<DayInterval>(\n \"DAY_INTERVAL\",\n);\n\n@Directive({\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: IgxCalendarViewDirective,\n multi: true,\n },\n ],\n standalone: true,\n})\nexport abstract class IgxCalendarViewDirective implements ControlValueAccessor {\n protected dayInterval = inject<DayInterval>(DAY_INTERVAL_TOKEN);\n \n @HostBinding(\"attr.role\")\n @Input()\n public role = 'grid';\n\n @HostBinding(\"attr.tabIndex\")\n @Input()\n public tabIndex = 0;\n\n @HostBinding('attr.aria-activeDescendant')\n protected get activeDescendant() {\n if (this.tabIndex === -1) return;\n\n return this.date.getTime();\n }\n\n /**\n * Gets/sets whether the view should be rendered\n * according to the locale and format, if any.\n */\n @Input({ transform: booleanAttribute })\n public formatView: boolean;\n\n /**\n * Applies styles to the active item on view focus.\n */\n @Input({ transform: booleanAttribute })\n public showActive = false;\n\n /**\n * Emits an event when a selection is made in the view.\n * Provides reference the `date` property in the component.\n * @memberof IgxCalendarViewDirective\n */\n @Output()\n public selected = new EventEmitter<Date>();\n\n /**\n * Emits an event when a page changes in the view.\n * Provides reference the `date` property in the component.\n * @memberof IgxCalendarViewDirective\n * @hidden @internal\n */\n @Output()\n public pageChanged = new EventEmitter<Date>();\n\n /**\n * Emits an event when the active date has changed.\n * @memberof IgxCalendarViewDirective\n * @hidden @internal\n */\n @Output()\n public activeDateChanged = new EventEmitter<Date>();\n\n /**\n * @hidden\n * @internal\n */\n @ViewChildren(IGX_CALENDAR_VIEW_ITEM, { read: IGX_CALENDAR_VIEW_ITEM })\n public viewItems: QueryList<\n IgxCalendarMonthDirective | IgxCalendarYearDirective\n >;\n\n /**\n * @hidden\n */\n protected _formatter: Intl.DateTimeFormat;\n\n /**\n * @hidden\n */\n protected _locale = \"en\";\n\n /**\n * @hidden\n * @internal\n */\n private _date = new Date();\n\n /**\n * @hidden\n */\n protected _onTouchedCallback: () => void = noop;\n\n /**\n * @hidden\n */\n protected _onChangeCallback: (_: Date) => void = noop;\n\n /**\n * Gets/sets the selected date of the view.\n * By default it's the current date.\n * ```typescript\n * let date = this.view.date;\n * ```\n *\n * @memberof IgxYearsViewComponent\n */\n @Input()\n public set date(value: Date) {\n if (!isDate(value)) return;\n\n this._date = value;\n }\n\n public get date() {\n return this._date;\n }\n\n /**\n * Gets the `locale` of the view.\n * Default value is `\"en\"`.\n * ```typescript\n * let locale = this.view.locale;\n * ```\n *\n * @memberof IgxCalendarViewDirective\n */\n @Input()\n public get locale(): string {\n return this._locale;\n }\n\n /**\n * Sets the `locale` of the view.\n * Expects a valid BCP 47 language tag.\n * Default value is `\"en\"`.\n *\n * @memberof IgxCalendarViewDirective\n */\n public set locale(value: string) {\n this._locale = value;\n this.initFormatter();\n }\n\n constructor() {\n this.initFormatter();\n }\n\n /**\n * @hidden\n */\n @HostListener(\"keydown.arrowdown\", [\"$event\"])\n public onKeydownArrowDown(event: KeyboardEvent) {\n this.navigateTo(event, IgxCalendarNavDirection.NEXT, 3);\n }\n\n /**\n * @hidden\n */\n @HostListener(\"keydown.arrowup\", [\"$event\"])\n public onKeydownArrowUp(event: KeyboardEvent) {\n this.navigateTo(event, IgxCalendarNavDirection.PREV, 3);\n }\n\n /**\n * @hidden\n */\n @HostListener(\"keydown.arrowright\", [\"$event\"])\n public onKeydownArrowRight(event: KeyboardEvent) {\n this.navigateTo(event, IgxCalendarNavDirection.NEXT, 1);\n }\n\n /**\n * @hidden\n */\n @HostListener(\"keydown.arrowleft\", [\"$event\"])\n public onKeydownArrowLeft(event: KeyboardEvent) {\n this.navigateTo(event, IgxCalendarNavDirection.PREV, 1);\n }\n\n /**\n * @hidden\n */\n @HostListener(\"keydown.home\", [\"$event\"])\n public onKeydownHome(event: KeyboardEvent) {\n event.preventDefault();\n event.stopPropagation();\n\n this.date = this.range.at(0);\n this.activeDateChanged.emit(this.date);\n }\n\n /**\n * @hidden\n */\n @HostListener(\"keydown.end\", [\"$event\"])\n public onKeydownEnd(event: KeyboardEvent) {\n event.preventDefault();\n event.stopPropagation();\n\n this.date = this.range.at(-1);\n this.activeDateChanged.emit(this.date);\n }\n\n /**\n * @hidden\n */\n @HostListener(\"keydown.enter\", [\"$event\"])\n public onKeydownEnter(event: KeyboardEvent) {\n event.stopPropagation();\n\n this.selected.emit(this.date);\n this._onChangeCallback(this.date);\n }\n\n /**\n * @hidden\n */\n @HostListener(\"focus\")\n protected handleFocus() {\n this.showActive = true;\n }\n\n /**\n * @hidden\n */\n @HostListener(\"blur\")\n protected handleBlur() {\n this.showActive = false;\n }\n\n /**\n * @hidden\n */\n public selectDate(value: Date) {\n this.date = value;\n\n this.selected.emit(this.date);\n this._onChangeCallback(this.date);\n }\n\n /**\n * @hidden\n */\n public registerOnChange(fn: (v: Date) => void) {\n this._onChangeCallback = fn;\n }\n\n /**\n * @hidden\n */\n public registerOnTouched(fn: () => void) {\n this._onTouchedCallback = fn;\n }\n\n /**\n * @hidden\n */\n public writeValue(value: Date) {\n if (value) {\n this.date = value;\n }\n }\n\n /**\n * @hidden\n */\n protected navigateTo(\n event: KeyboardEvent,\n direction: IgxCalendarNavDirection,\n delta: number,\n ) {\n event.preventDefault();\n event.stopPropagation();\n\n const date = getNextActiveDate(\n CalendarDay.from(this.date).add(this.dayInterval, direction * delta),\n [],\n );\n\n const outOfRange = !isDateInRanges(date, [\n {\n type: DateRangeType.Between,\n dateRange: [this.range.at(0), this.range.at(-1)],\n },\n ]);\n\n if (outOfRange) {\n this.pageChanged.emit(date.native);\n }\n\n this.date = date.native;\n this.activeDateChanged.emit(this.date);\n }\n\n /**\n * @hidden\n */\n protected abstract initFormatter(): void;\n\n /**\n * @hidden\n */\n protected abstract get range(): Date[];\n}\n","import {\n Component,\n Input,\n HostBinding,\n ElementRef,\n booleanAttribute,\n inject,\n} from \"@angular/core\";\nimport { IgxCalendarMonthDirective } from \"../calendar.directives\";\nimport { TitleCasePipe } from \"@angular/common\";\nimport {\n IgxCalendarViewDirective,\n DAY_INTERVAL_TOKEN,\n} from \"../common/calendar-view.directive\";\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from \"@angular/forms\";\nimport { CalendarDay, calendarRange, PlatformUtil } from 'igniteui-angular/core';\n\nlet NEXT_ID = 0;\n\n@Component({\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: IgxMonthsViewComponent,\n multi: true,\n },\n {\n provide: DAY_INTERVAL_TOKEN,\n useValue: \"month\",\n },\n ],\n selector: \"igx-months-view\",\n templateUrl: \"months-view.component.html\",\n imports: [IgxCalendarMonthDirective, TitleCasePipe]\n})\nexport class IgxMonthsViewComponent extends IgxCalendarViewDirective implements ControlValueAccessor {\n public el = inject(ElementRef);\n\n #standalone = true;\n private platform = inject(PlatformUtil);\n\n /**\n * Sets/gets the `id` of the months view.\n * If not set, the `id` will have value `\"igx-months-view-0\"`.\n * ```html\n * <igx-months-view id=\"my-months-view\"></igx-months-view>\n * ```\n * ```typescript\n * let monthsViewId = this.monthsView.id;\n * ```\n *\n * @memberof IgxMonthsViewComponent\n */\n @HostBinding(\"attr.id\")\n @Input()\n public id = `igx-months-view-${NEXT_ID++}`;\n\n /**\n * The default css class applied to the component.\n *\n * @hidden\n */\n @HostBinding(\"class.igx-calendar-view\")\n public readonly viewClass = true;\n\n /**\n * @hidden @internal\n */\n @Input()\n @HostBinding(\"class.igx-calendar-view--standalone\")\n public get standalone() {\n return this.#standalone;\n }\n\n public set standalone(value: boolean) {\n this.#standalone = value;\n }\n\n /**\n * Gets the month format option of the months view.\n * ```typescript\n * let monthFormat = this.monthsView.monthFormat.\n * ```\n */\n @Input()\n public get monthFormat(): any {\n return this._monthFormat;\n }\n\n /**\n * Sets the month format option of the months view.\n * ```html\n * <igx-months-view> [monthFormat]=\"short'\"</igx-months-view>\n * ```\n *\n * @memberof IgxMonthsViewComponent\n */\n public set monthFormat(value: any) {\n this._monthFormat = value;\n this.initFormatter();\n }\n\n /**\n * Gets/sets whether the view should be rendered\n * according to the locale and format, if any.\n */\n @Input({ transform: booleanAttribute })\n public override formatView = true;\n\n /**\n * Returns an array of date objects which are then used to\n * properly render the month names.\n *\n * Used in the template of the component\n *\n * @hidden @internal\n */\n public get range(): Date[] {\n const start = CalendarDay.from(this.date).set({ date: 1, month: 0 });\n const end = start.add(this.dayInterval, 12);\n\n return Array.from(\n calendarRange({ start, end, unit: this.dayInterval }),\n ).map((m) => m.native);\n }\n\n /**\n * @hidden\n */\n private _monthFormat = \"short\";\n\n /**\n * @hidden\n */\n protected onMouseDown() {\n if (this.tabIndex !== -1 && this.platform.isBrowser && this.el?.nativeElement) {\n this.el.nativeElement.focus();\n }\n }\n\n /**\n * Returns the locale representation of the month in the months view.\n *\n * @hidden\n */\n public formattedMonth(value: Date): { long: string; formatted: string } {\n const rawFormatter = new Intl.DateTimeFormat(this.locale, {\n month: \"long\",\n year: \"numeric\",\n });\n\n if (this.formatView) {\n return {\n long: rawFormatter.format(value),\n formatted: this._formatter.format(value),\n };\n }\n\n return {\n long: rawFormatter.format(value),\n formatted: `${value.getMonth()}`,\n };\n }\n\n /**\n * @hidden\n */\n public monthTracker(_: number, item: Date): string {\n return `${item.getMonth()}}`;\n }\n\n /**\n * @hidden\n */\n protected initFormatter() {\n this._formatter = new Intl.DateTimeFormat(this._locale, {\n month: this.monthFormat,\n });\n }\n}\n","<div class=\"igx-calendar-view__items\" role=\"row\">\n @for (month of range; track monthTracker($index, month)) {\n <span\n igxCalendarMonth\n #item=\"igxCalendarMonth\"\n class=\"igx-calendar-view__item\"\n role=\"gridcell\"\n [attr.id]=\"month.getTime()\"\n [attr.aria-label]=\"formattedMonth(month).long\"\n [attr.aria-selected]=\"item.isSelected\"\n [value]=\"month\"\n [date]=\"date\"\n [showActive]=\"showActive\"\n (itemSelection)=\"selectDate($event)\"\n (mousedown)=\"onMouseDown()\"\n >\n <span class=\"igx-calendar-view__item-inner\" aria-hidden=\"true\">\n {{ formattedMonth(month).formatted | titlecase }}\n </span>\n </span>\n }\n</div>\n","import {\n Component,\n Input,\n HostBinding,\n ElementRef,\n inject,\n} from \"@angular/core\";\nimport { IgxCalendarYearDirective } from \"../calendar.directives\";\nimport {\n IgxCalendarViewDirective,\n DAY_INTERVAL_TOKEN,\n} from \"../common/calendar-view.directive\";\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from \"@angular/forms\";\nimport { CalendarDay, calendarRange, PlatformUtil } from 'igniteui-angular/core';\n\n@Component({\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: IgxYearsViewComponent,\n multi: true,\n },\n {\n provide: DAY_INTERVAL_TOKEN,\n useValue: \"year\",\n },\n ],\n selector: \"igx-years-view\",\n templateUrl: \"years-view.component.html\",\n imports: [IgxCalendarYearDirective]\n})\nexport class IgxYearsViewComponent extends IgxCalendarViewDirective implements ControlValueAccessor {\n public el = inject(ElementRef);\n\n #standalone = true;\n private platform = inject(PlatformUtil);\n\n /**\n * The default css class applied to the component.\n *\n * @hidden\n */\n @HostBinding(\"class.igx-calendar-view\")\n public readonly viewClass = true;\n\n /**\n * @hidden @internal\n */\n @Input()\n\t@HostBinding('class.igx-calendar-view--standalone')\n\tpublic get standalone() {\n return this.#standalone;\n }\n\n\tpublic set standalone(value: boolean) {\n this.#standalone = value;\n }\n\n /**\n * @hidden\n */\n private _yearFormat = \"numeric\";\n\n /**\n * @hidden\n */\n private _yearsPerPage = 15;\n\n /**\n * Gets the year format option of the years view.\n * ```typescript\n * let yearFormat = this.yearsView.yearFormat.\n * ```\n */\n @Input()\n public get yearFormat(): any {\n return this._yearFormat;\n }\n\n /**\n * Sets the year format option of the years view.\n * ```html\n * <igx-years-view [yearFormat]=\"numeric\"></igx-years-view>\n * ```\n *\n * @memberof IgxYearsViewComponent\n */\n public set yearFormat(value: any) {\n this._yearFormat = value;\n this.initFormatter();\n }\n\n /**\n * Returns an array of date objects which are then used to properly\n * render the years.\n *\n * Used in the template of the component.\n *\n * @hidden @internal\n */\n public get range(): Date[] {\n const year = this.date.getFullYear();\n const start = new CalendarDay({\n year: Math.floor(year / this._yearsPerPage) * this._yearsPerPage,\n month: this.date.getMonth(),\n });\n const end = start.add(this.dayInterval, this._yearsPerPage);\n\n return Array.from(calendarRange({ start, end, unit: this.dayInterval })).map(\n (m) => m.native,\n );\n }\n\n /**\n * Returns the locale representation of the year in the years view.\n *\n * @hidden\n */\n public formattedYear(value: Date): {long: string, formatted: string} {\n const rawFormatter = new Intl.DateTimeFormat(this.locale, { year: 'numeric' });\n\n if (this.formatView) {\n return {\n long: rawFormatter.format(value),\n formatted: this._formatter.format(value)\n }\n }\n\n return {\n long: rawFormatter.format(value),\n formatted: `${value.getFullYear()}`\n }\n }\n\n /**\n * @hidden\n */\n public yearTracker(_: number, item: Date): string {\n return `${item.getFullYear()}}`;\n }\n\n /**\n * @hidden\n */\n protected initFormatter() {\n this._formatter = new Intl.DateTimeFormat(this._locale, {\n year: this.yearFormat,\n });\n }\n\n /**\n * @hidden\n */\n protected onMouseDown() {\n if (this.tabIndex !== -1 && this.platform.isBrowser && this.el?.nativeElement) {\n this.el.nativeElement.focus();\n }\n }\n}\n","<div class=\"igx-calendar-view__items\" role=\"row\">\n @for (year of range; track yearTracker($index, year)) {\n <span\n igxCalendarYear\n #item=\"igxCalendarYear\"\n class=\"igx-calendar-view__item\"\n role=\"gridcell\"\n [attr.id]=\"year.getTime()\"\n [attr.aria-label]=\"formattedYear(year).long\"\n [attr.aria-selected]=\"item.isSelected\"\n [attr.aria-current]=\"item.isCurrent\"\n [value]=\"year\"\n [date]=\"date\"\n [showActive]=\"showActive\"\n (itemSelection)=\"selectDate($event)\"\n (mousedown)=\"onMouseDown()\"\n >\n <span class=\"igx-calendar-view__item-inner\" aria-hidden=\"true\">\n {{ formattedYear(year).formatted }}\n </span>\n </span>\n }\n</div>\n","import { Component, Input, Output, EventEmitter, HostBinding, ElementRef, booleanAttribute, ChangeDetectionStrategy, inject } from '@angular/core';\nimport { CalendarSelection } from '../calendar';\nimport { areSameMonth, CalendarDay, DateRangeDescriptor, isDateInRanges, isNextMonth, isPreviousMonth } from 'igniteui-angular/core';\n\n/**\n * @hidden\n */\n@Component({\n selector: 'igx-day-item',\n templateUrl: 'day-item.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true\n})\nexport class IgxDayItemComponent {\n private elementRef = inject(ElementRef);\n\n @Input()\n public date: CalendarDay;\n\n @Input()\n public viewDate: Date;\n\n @Input()\n public selection: string;\n\n /**\n * Returns boolean indicating if the day is selected\n *\n */\n @Input()\n public get selected(): any {\n return this._selected;\n }\n\n /**\n * Selects the day\n */\n public set selected(value: any) {\n this._selected = value;\n }\n\n @Input()\n public disabledDates: DateRangeDescriptor[];\n\n @Input()\n public specialDates: DateRangeDescriptor[];\n\n @Input({ transform: booleanAttribute })\n public hideOutsideDays = false;\n\n @Input({ transform: booleanAttribute })\n @HostBinding('class.igx-days-view__date--last')\n public isLastInRange = false;\n\n @Input({ transform: booleanAttribute })\n @HostBinding('class.igx-days-view__date--first')\n public isFirstInRange = false;\n\n @Input({ transform: booleanAttribute })\n public isWithinRange = false;\n\n @Input({ transform: booleanAttribute })\n public isWithinPreviewRange = false;\n\n @Input({ transform: booleanAttribute })\n public hideLeadingDays = false;\n\n @Input({ transform: booleanAttribute })\n public hideTrailingDays = false;\n\n private get hideLeading() {\n return this.hideLeadingDays && this.isPreviousMonth;\n }\n\n private get hideTrailing() {\n return this.hideTrailingDays && this.isNextMonth;\n }\n\n @Output()\n public dateSelection = new EventEmitter<CalendarDay>();\n\n @Output()\n public mouseEnter = new EventEmitter<void>();\n\n @Output()\n public mouseLeave = new EventEmitter<void>();\n\n @Output()\n public mouseDown = new EventEmitter<void>();\n\n public get isCurrentMonth(): boolean {\n return areSameMonth(this.date, this.viewDate);\n }\n\n public get isPreviousMonth(): boolean {\n return isPreviousMonth(this.date, this.viewDate);\n }\n\n public get isNextMonth(): boolean {\n return isNextMonth(this.date, this.viewDate);\n }\n\n public get nativeElement() {\n return this.elementRef.nativeElement;\n }\n\n @Input({ transform: booleanAttribute })\n @HostBinding('class.igx-days-view__date--active')\n public isActive = false;\n\n @HostBinding('class.igx-days-view__date--selected')\n public get isSelectedCSS(): boolean {\n const selectable =\n !this.isInactive || this.isWithinPreviewRange ||\n (this.isWithinRange && this.selection === \"range\");\n return !this.isDisabled && selectable && this.selected;\n }\n\n @HostBinding('class.igx-days-view__date--inactive')\n public get isInactive(): boolean {\n return !this.isCurrentMonth;\n }\n\n @HostBinding('class.igx-days-view__date--hidden')\n public get isHidden(): boolean {\n return (this.hideLeading || this.hideTrailing) && this.isInactive;\n }\n\n @HostBinding('class.igx-days-view__date--current')\n public get isToday(): boolean {\n return !this.isInactive && this.date.equalTo(CalendarDay.today);\n }\n\n @HostBinding('class.igx-days-view__date--weekend')\n public get isWeekend(): boolean {\n return this.date.weekend;\n }\n\n public get isDisabled(): boolean {\n if (!this.disabledDates) {\n return false;\n }\n\n return isDateInRanges(this.date, this.disabledDates);\n }\n\n public get isFocusable(): boolean {\n return this.isCurrentMonth && !this.isHidden && !this.isDisabled;\n }\n\n protected onMouseEnter() {\n this.mouseEnter.emit();\n }\n\n protected onMouseLeave() {\n this.mouseLeave.emit();\n }\n\n protected onMouseDown(event: MouseEvent) {\n event.preventDefault();\n this.mouseDown.emit();\n }\n\n @HostBinding('class.igx-days-view__date--range')\n public get isWithinRangeCSS(): boolean {\n return !this.isSingleSelection && this.isWithinRange;\n }\n\n @HostBinding('class.igx-days-view__date--range-preview')\n public get isWithinPreviewRangeCSS(): boolean {\n return !this.isSingleSelection && this.isWithinPreviewRange;\n }\n\n @HostBinding('class.igx-days-view__date--special')\n public get isSpecial(): boolean {\n if (!this.specialDates) {\n return false;\n }\n\n return !this.isInactive && isDateInRanges(this.date, this.specialDates);\n }\n\n @HostBinding('class.igx-days-view__date--disabled')\n public get isDisabledCSS(): boolean {\n return this.isHidden || this.isDisabled;\n }\n\n @HostBinding('class.igx-days-view__date--single')\n public get isSingleSelection(): boolean {\n return this.selection !== CalendarSelection.RANGE;\n }\n\n private _selected = false;\n}\n","<span\n aria-hidden=\"true\"\n class=\"igx-days-view__date-inner\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"\n (mousedown)=\"onMouseDown($event)\"\n>\n <ng-content></ng-content>\n</span>\n","import { Injectable, ElementRef, NgZone, inject } from \"@angular/core\";\nimport { EventManager } from \"@angular/platform-browser\";\nimport { PlatformUtil } from 'igniteui-angular/core';\n\n@Injectable()\nexport class KeyboardNavigationService {\n private eventManager = inject(EventManager);\n private ngZone = inject(NgZone);\n\n private keyHandlers = new Map<string, (event: KeyboardEvent) => void>();\n private eventUnsubscribeFn: Function | null = null;\n private platform = inject(PlatformUtil);\n\n public attachKeyboardHandlers(elementRef: ElementRef, context: any) {\n if (!this.platform.isBrowser) {\n return this;\n }\n\n this.detachKeyboardHandlers(); // Clean up any existing listeners\n\n this.ngZone.runOutsideAngular(() => {\n this.eventUnsubscribeFn = this.eventManager.addEventListener(\n elementRef.nativeElement,\n 'keydown',\n (event: KeyboardEvent) => {\n const handler = this.keyHandlers.get(event.key);\n\n if (handler) {\n this.ngZone.run(handler.bind(context, event));\n }\n }\n );\n });\n\n return this;\n }\n\n public detachKeyboardHandlers() {\n if (this.eventUnsubscribeFn) {\n this.eventUnsubscribeFn();\n this.eventUnsubscribeFn = null;\n }\n\n this.keyHandlers.clear();\n }\n\n public set(key : string, handler: (event: KeyboardEvent) => void) {\n this.keyHandlers.set(key, handler);\n return this;\n }\n\n public unset(key: string) {\n this.keyHandlers.delete(key);\n return this;\n }\n}\n","import { Input, Output, EventEmitter, Directive, LOCALE_ID, HostListener, booleanAttribute, ViewChildren, QueryList, ElementRef, ChangeDetectorRef, inject } from '@angular/core';\nimport { IFormattingOptions, IFormattingViews, IViewDateChangeEventArgs, ScrollDirection, IgxCalendarView, CalendarSelection } from './calendar';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { noop, Subject } from 'rxjs';\nimport {\n isDate,\n isEqual,\n PlatformUtil,\n DateRangeDescriptor,\n DateTimeUtil,\n CalendarResourceStringsEN,\n ICalendarResourceStrings,\n getCurrentResourceStrings,\n CalendarDay,\n getYearRange,\n isDateInRanges,\n WEEKDAYS\n} from 'igniteui-angular/core';\nimport { getLocaleFirstDayOfWeek } from \"@angular/common\";\nimport { KeyboardNavigationService } from './calendar.services';\n\n/** @hidden @internal */\n@Directive({\n selector: '[igxCalendarBase]',\n standalone: true,\n providers: [KeyboardNavigationService]\n})\nexport class IgxCalendarBaseDirective implements ControlValueAccessor {\n protected platform = inject(PlatformUtil);\n protected _localeId = inject(LOCALE_ID);\n protected keyboardNavigation? = inject(KeyboardNavigationService);\n protected cdr? = inject(ChangeDetectorRef);\n\n /**\n * Holds month view index we are operating on.\n */\n protected activeViewIdx = 0;\n\n /**\n * @hidden\n */\n private _activeView: IgxCalendarView = IgxCalendarView.Month;\n\n /**\n * @hidden\n */\n private activeViewSubject = new Subject<IgxCalendarView>();\n\n /**\n * @hidden\n */\n protected activeView$ = this.activeViewSubject.asObservable();\n\n /**\n * Sets/gets whether the outside dates (dates that are out of the current month) will be hidden.\n * Default value is `false`.\n * ```html\n * <igx-calendar [hideOutsideDays]=\"true\"></igx-calendar>\n * ```\n * ```typescript\n * let hideOutsideDays = this.calendar.hideOutsideDays;\n * ```\n */\n\n @Input({ transform: booleanAttribute })\n public hideOutsideDays = false;\n\n /**\n * Emits an event when a date is selected.\n * Provides reference the `selectedDates` property.\n */\n @Output()\n public selected = new EventEmitter<Date | Date[]>();\n\n /**\n * Emits an event when the month in view is changed.\n * ```html\n * <igx-calendar (viewDateChanged)=\"viewDateChanged($event)\"></igx-calendar>\n * ```\n * ```typescript\n * public viewDateChanged(event: IViewDateChangeEventArgs) {\n * let viewDate = event.currentValue;\n * }\n * ```\n */\n @Output()\n public viewDateChanged = new EventEmitter<IViewDateChangeEventArgs>();\n\n /**\n * Emits an event when the active view is changed.\n * ```html\n * <igx-calendar (activeViewChanged)=\"activeViewChanged($event)\"></igx-calendar>\n * ```\n * ```typescript\n * public activeViewChanged(event: CalendarView) {\n * let activeView = event;\n * }\n * ```\n */\n @Output()\n public activeViewChanged = new EventEmitter<IgxCalendarView>();\n\n /**\n * @hidden\n */\n public rangeStarted = false;\n\n /**\n * @hidden\n */\n public pageScrollDirection = ScrollDirection.NONE;\n\n /**\n * @hidden\n */\n public scrollPage$ = new Subject<void>();\n\n /**\n * @hidden\n */\n public stopPageScroll$ = new Subject<boolean>();\n\n /**\n * @hidden\n */\n public startPageScroll$ = new Subject<void>();\n\n /**\n * @hidden\n */\n public selectedDates: Date[];\n\n /**\n * @hidden\n */\n public shiftKey = false;\n\n /**\n * @hidden\n */\n public lastSelectedDate: Date;\n\n /**\n * @hidden\n */\n protected formatterWeekday: Intl.DateTimeFormat;\n\n /**\n * @hidden\n */\n protected formatterDay: Intl.DateTimeFormat;\n\n /**\n * @hidden\n */\n protected formatterMonth: Intl.DateTimeFormat;\n\n /**\n * @hidden\n */\n protected formatterYear: Intl.DateTimeFormat;\n\n /**\n * @hidden\n */\n protected formatterMonthday: Intl.DateTimeFormat;\n\n /**\n * @hidden\n */\n protected formatterRangeday: Intl.DateTimeFormat;\n\n /**\n * @hidden\n */\n protected _onTouchedCallback: () => void = noop;\n /**\n * @hidden\n */\n protected _onChangeCallback: (_: Date | Date[]) => void = noop;\n\n /**\n * @hidden\n */\n protected _deselectDate: boolean;\n\n /**\n * @hidden\n */\n private initialSelection: Date | Date[];\n\n /**\n * @hidden\n */\n private _locale: string;\n\n /**\n * @hidden\n */\n private _weekStart: WEEKDAYS | number;\n\n /**\n * @hidden\n */\n private _viewDate: Date;\n\n /**\n * @hidden\n */\n private _startDate: Date;\n\n /**\n * @hidden\n */\n private _endDate: Date;\n\n /**\n * @hidden\n */\n private _disabledDates: DateRangeDescriptor[] = [];\n\n /**\n * @hidden\n */\n private _specialDates: DateRangeDescriptor[] = [];\n\n /**\n * @hidden\n */\n private _selection: CalendarSelection | string = CalendarSelection.SINGLE;\n\n /** @hidden @internal */\n private _resourceStrings = getCurrentResourceStrings(CalendarResourceStringsEN);\n\n /**\n * @hidden\n */\n private _formatOptions: IFormattingOptions = {\n day: 'numeric',\n month: 'long',\n weekday: 'narrow',\n year: 'numeric'\n };\n\n /**\n * @hidden\n */\n private _formatViews: IFormattingViews = {\n day: false,\n month: true,\n year: false\n };\n\n /**\n * An accessor that sets the resource strings.\n * By default it uses EN resources.\n */\n @Input()\n public set resourceStrings(value: ICalendarResourceStrings) {\n this._resourceStrings = Object.assign({}, this._resourceStrings, value);\n }\n\n /**\n * An accessor that returns the resource strings.\n */\n public get resourceStrings(): ICalendarResourceStrings {\n return this._resourceStrings;\n }\n\n /**\n * Gets the start day of the week.\n * Can return a numeric or an enum representation of the week day.\n * If not set, defaults to the first day of the week for the application locale.\n */\n @Input()\n public get weekStart(): WEEKDAYS | number {\n return this._weekStart;\n }\n\n /**\n * Sets the start day of the week.\n * Can be assigned to a numeric value or to `WEEKDAYS` enum value.\n */\n public set weekStart(value: WEEKDAYS | number) {\n this._weekStart = value;\n }\n\n /**\n * Gets the `locale` of the calendar.\n * If not set, defaults to application's locale.\n */\n @Input()\n public get locale(): string {\n return this._locale;\n }\n\n /**\n * Sets the `locale` of the calendar.\n * Expects a valid BCP 47 language tag.\n */\n public set locale(value: string) {\n this._locale = value;\n\n // if value is not a valid BCP 47 tag, set it back to _localeId\n try {\n getLocaleFirstDayOfWeek(this._locale);\n } catch (e) {\n this._locale = this._localeId;\n }\n\n // changing locale runtime needs to update the `weekStart` too, if `weekStart` is not explicitly set\n if (!this.weekStart) {\n this.weekStart = getLocaleFirstDayOfWeek(this._locale);\n }\n\n this.initFormatters();\n }\n\n /**\n * Gets the date format options of the views.\n */\n @Input()\n public get formatOptions(): IFormattingOptions {\n return this._formatOptions;\n }\n\n /**\n * Sets the date format options of the views.\n * Default is { day: 'numeric', month: 'short', weekday: 'short', year: 'numeric' }\n */\n public set formatOptions(formatOptions: IFormattingOptions) {\n this._formatOptions = {...this._formatOptions, ...formatOptions};\n this.initFormatters();\n }\n\n /**\n * Gets whether the `day`, `month` and `year` should be rendered\n * according to the locale and formatOptions, if any.\n */\n @Input()\n public get formatViews(): IFormattingViews {\n return this._formatViews;\n }\n\n /**\n * Sets whether the `day`, `month` and `year` should be rendered\n * according to the locale and formatOptions, if any.\n */\n public set formatViews(formatViews: IFormattingViews) {\n this._formatViews = Object.assign(this._formatViews, formatViews);\n }\n\n /**\n * Gets the current active view.\n * ```typescript\n * this.activeView = calendar.activeView;\n * ```\n */\n @Input()\n public get activeView(): IgxCalendarView {\n return this._activeView;\n }\n\n /**\n * Sets the current active view.\n * ```html\n * <igx-calendar [activeView]=\"year\" #calendar></igx-calendar>\n * ```\n * ```typescript\n * calendar.activeView = IgxCalendarView.YEAR;\n * ```\n */\n public set activeView(val: IgxCalendarView) {\n this._activeView = val;\n this.activeViewSubject.next(val);\n }\n\n /**\n * @hidden\n */\n public get isDefaultView(): boolean {\n return this._activeView === IgxCalendarView.Month;\n }\n\n /**\n * @hidden\n */\n public get isDecadeView(): boolean {\n return this._activeView === IgxCalendarView.Decade;\n }\n\n /**\n * @hidden\n */\n public activeViewDecade(activeViewIdx = 0): void {\n this.activeView = IgxCalendarView.Decade;\n this.activeViewIdx = activeViewIdx;\n }\n\n /**\n * @hidden\n */\n public activeViewDecadeKB(event: KeyboardEvent, activeViewIdx = 0) {\n event.stopPropagation();\n\n if (this.platform.isActivationKey(event)) {\n event.preventDefault();\n this.activeViewDecade(activeViewIdx);\n }\n }\n\n /**\n * @hidden\n */\n @ViewChildren('yearsBtn')\n public yearsBtns: QueryList<ElementRef>;\n\n /**\n * @hidden @internal\n */\n public previousViewDate: Date;\n\n /**\n * @hidden\n */\n public changeYear(date: Date) {\n this.previousViewDate = this.viewDate;\n this.viewDate = CalendarDay.from(date).add('month', -this.activeViewIdx).native;\n this.activeView = IgxCalendarView.Month;\n }\n\n /**\n * Returns the locale representation of the year in the year view if enabled,\n * otherwise returns the default `Date.getFullYear()` value.\n *\n * @hidden\n */\n public formattedYear(value: Date | Date[]): string {\n\t\tif (Array.isArray(value)) {\n\t\t\treturn;\n\t\t}\n\n if (this.formatViews.year) {\n return this.formatterYear.format(value);\n }\n\n\t return `${value.getFullYear()}`;\n }\n\n\tpublic formattedYears(value: Date) {\n\t\tconst dates = value as unknown as Date[];\n\t\treturn dates.map(date => this.formattedYear(date)).join(' - ');\n\t}\n\n protected prevNavLabel(detail?: string): string {\n switch (this.activeView) {\n case 'month':\n return `${this.resourceStrings.igx_calendar_previous_month}, ${detail}`\n case 'year':\n return this.resourceStrings.igx_calendar_previous_year;\n case 'decade':\n return this.resourceStrings.igx_calendar_previous_years.replace('{0}', '15');\n }\n }\n\n protected nextNavLabel(detail?: string): string {\n switch (this.activeView) {\n case 'month':\n return `${this.resourceStrings.igx_calendar_next_month}, ${detail}`\n case 'year':\n return this.resourceStrings.igx_ca