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 {
BrnCalendarImports,
BrnCalendarRange,
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-range',
imports: [BrnCalendarImports, NgIcon, HlmSelectImports, NgTemplateOutlet, HlmButtonImports],
viewProviders: [provideIcons({ lucideChevronLeft, lucideChevronRight })],
changeDetection: ChangeDetectionStrategy.OnPush,
hostDirectives: [
{
directive: BrnCalendarRange,
inputs: [
'min',
'max',
'disabled',
'startDate',
'endDate',
'dateDisabled',
'weekStartsOn',
'highlightDays',
'defaultFocusedDate',
],
outputs: ['endDateChange', 'startDateChange'],
},
],
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 }}
}
}
|
{{ _i18n.config().formatWeekdayName(weekday) }}
|
@for (date of week; track _dateAdapter.getTime(date)) {
|
|
}
`,
})
export class HlmCalendarRange {
/** Show dropdowns to navigate between months or years. */
public readonly captionLayout = input<
'dropdown' | 'label' | 'dropdown-months' | 'dropdown-years'
>('label');
/** Access the calendar i18n */
protected readonly _i18n = injectBrnCalendarI18n();
/** Access the date time adapter */
protected readonly _dateAdapter = injectDateAdapter();
/** Access the calendar directive */
private readonly _calendar = inject(BrnCalendarRange);
/** 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',
);
}
}