import { NgTemplateOutlet } from '@angular/common'; import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core'; import { NgIcon, provideIcons } from '@ng-icons/core'; import { lucideChevronLeft, lucideChevronRight } from '@ng-icons/lucide'; import { BrnCalendar, BrnCalendarImports, injectBrnCalendarI18n } from '@spartan-ng/brain/calendar'; import { injectDateAdapter } from '@spartan-ng/brain/date-time'; import { buttonVariants, HlmButtonImports } from '@spartan-ng/styles/button'; import { HlmSelectImports } from '@spartan-ng/styles/select'; import { classes, hlm } from '@spartan-ng/styles/utils'; @Component({ selector: 'hlm-calendar', imports: [BrnCalendarImports, NgIcon, HlmSelectImports, NgTemplateOutlet, HlmButtonImports], viewProviders: [provideIcons({ lucideChevronLeft, lucideChevronRight })], changeDetection: ChangeDetectionStrategy.OnPush, hostDirectives: [ { directive: BrnCalendar, inputs: [ 'min', 'max', 'disabled', 'date', 'dateDisabled', 'weekStartsOn', 'highlightDays', 'defaultFocusedDate', ], outputs: ['dateChange'], }, ], host: { 'data-slot': 'calendar' }, template: `
@for (month of _i18n.config().months(); track month) { {{ month }} } @for (year of _i18n.config().years(); track year) { {{ year }} } @let heading = _heading(); @switch (captionLayout()) { @case ('dropdown') { } @case ('dropdown-months') {
{{ heading.year }}
} @case ('dropdown-years') {
{{ heading.month }}
} @case ('label') {
{{ heading.header }}
} }
@for (date of week; track _dateAdapter.getTime(date)) { }
`, }) export class HlmCalendar { /** Access the calendar i18n */ protected readonly _i18n = injectBrnCalendarI18n(); /** Access the date time adapter */ protected readonly _dateAdapter = injectDateAdapter(); /** Show dropdowns to navigate between months or years. */ public readonly captionLayout = input< 'dropdown' | 'label' | 'dropdown-months' | 'dropdown-years' >('label'); /** Access the calendar directive */ private readonly _calendar = inject(BrnCalendar); /** Get the heading for the current month and year */ protected readonly _heading = computed(() => { const config = this._i18n.config(); const date = this._calendar.focusedDate(); return { header: config.formatHeader( this._dateAdapter.getMonth(date), this._dateAdapter.getYear(date), ), month: config.formatMonth(this._dateAdapter.getMonth(date)), year: config.formatYear(this._dateAdapter.getYear(date)), }; }); protected readonly _btnClass = hlm( buttonVariants({ variant: 'ghost', size: 'icon' }), 'data-[today=true]:bg-muted group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:bg-muted/50 dark:hover:text-foreground relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-e-(--cell-radius) data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-s-(--cell-radius) [&>span]:text-xs [&>span]:opacity-70', 'data-[outside=true]:opacity-50', "data-[highlighted]:before:content-['']", 'data-[highlighted]:before:absolute', 'data-[highlighted]:before:bottom-1', 'data-[highlighted]:before:start-1/2', 'data-[highlighted]:before:h-1', 'data-[highlighted]:before:w-1', 'data-[highlighted]:before:-translate-x-1/2', 'data-[highlighted]:before:rounded-full', 'data-[highlighted]:before:bg-destructive', ); protected readonly _selectClass = 'gap-0 px-1.5 py-2 [&>ng-icon]:ms-1'; constructor() { classes( () => 'p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] group/calendar bg-background block in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent', ); } }