Date Range
Date range picker with trigger button, modal calendar overlay, quick filter buttons, and ControlValueAccessor support.
This component has both a default concrete implementation (<smart-date-range>) and abstract base classes for custom extensions.
Usage
<smart-date-range
variant="standard"
[ngModel]="range()"
(ngModelChange)="onRangeChange($event)"
/>
Default Component Usage
Selector
<smart-date-range>
Properties
| Property | Type | Default | Description |
|---|---|---|---|
ngModel | ModelSignal<IDateRange | undefined> | undefined | Date range value (start, end in YYYY-MM-DD) |
Features
- Trigger button with calendar icon
- Clear button (X) to reset
- Modal overlay with calendar grid
- Quick filters: Today, Yesterday, Last 7/30 days, This/Last Month
- Range selection with start/end highlighting
ControlValueAccessorfor forms- Dark mode
DateRangePresetComponent (<smart-date-range-preset>)
A fully styled, drop-in variation based on the Preline "single calendar range" datepicker (FRA-219). The Preline JS plugin is not installed, so the range calendar is rendered entirely with Angular signals (no external runtime): click the trigger to open an inline popover, pick a start and end day to highlight the range, switch months via prev/next buttons or the month/year selects, then Apply.
Extends DateRangeBaseComponent (reuses onClick, onModalApply, onModalDismiss, onClear), so it consumes the exact same API and ControlValueAccessor contract as the standard variant — no new interface options or tokens are required.
Selecting the variant
<smart-date-range> dispatches over the variant input (type DateRangeVariantName = 'standard' | 'preset') with an internal @switch. This component does not use the InjectionToken / NgComponentOutlet pattern:
Because the wrapper binds [class]="cssClass()" directly on the selector (not via NgComponentOutlet), the host class is applied natively — no cssClass input override is needed (unlike token-based components such as badge).
Base Classes API (for Extension)
DateRangeBaseComponent
Provides open/close state, calendar data management, clear/apply logic, and ControlValueAccessor.
| Member | Type | Description |
|---|---|---|
ngModel | ModelSignal<IDateRange | undefined> | Date range value |
value | IDateRange | undefined | Current value (synced via effect) |
isOpen | WritableSignal<boolean> | Controls modal visibility |
calendarData | CalendarState | Persisted calendar state between opens |
onClick() | method | Opens modal, restores previous selection |
onModalApply() | method | Applies selection and closes modal |
onModalDismiss() | method | Closes modal without applying |
onClear() | method | Resets value to undefined |
DateRangeModalBaseComponent
Provides calendar rendering, quick filter buttons, date selection, and range restriction.
| Member | Type | Description |
|---|---|---|
previousState | InputSignal<CalendarState> | State from parent component |
apply | OutputEmitter<CalendarState> | Emits on apply |
dismiss | OutputEmitter<void> | Emits on dismiss |
calendar | month[] | Generated calendar months |
dateForm | UntypedFormGroup | Form with dateFrom/dateTo fields |
selectToday() | method | Quick filter: today |
selectYesterday() | method | Quick filter: yesterday |
selectLastSevenDays() | method | Quick filter: last 7 days |
selectLastThirtyDays() | method | Quick filter: last 30 days |
selectThisMonth() | method | Quick filter: this month |
selectLastMonth() | method | Quick filter: last month |
isInRange(day) | method | Check if day is in selected range |
isSelectionStart(day) | method | Check if day is range start |
isSelectionEnd(day) | method | Check if day is range end |
applyDates() | method | Emits apply with current state |
dismissPage() | method | Emits dismiss and unsubscribes |
Extending the base class
import {
ChangeDetectionStrategy,
Component,
forwardRef,
signal,
ViewEncapsulation,
} from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { DateRangeBaseComponent } from '@smartsoft001/angular';
import { IDateRange } from '@smartsoft001/domain-core';
/**
* A custom range picker built on `DateRangeBaseComponent`.
*
* The base owns the value (`ngModel` / `value`), the open state (`isOpen`,
* `onClick`, `onModalDismiss`), the reset (`onClear`) and the
* `ControlValueAccessor` contract. Here the full calendar modal is replaced
* with a short list of preset ranges.
*/
@Component({
selector: 'docs-custom-date-range',
template: `
<div class="docs-date-range">
<button
type="button"
class="docs-date-range__trigger"
(click)="onClick()"
>
@if (value) {
{{ value.start }} - {{ value.end }}
} @else {
Pick a range
}
</button>
@if (value) {
<button
type="button"
class="docs-date-range__clear"
(click)="onClear()"
>
Clear
</button>
}
@if (isOpen()) {
<ul class="docs-date-range__panel">
<li>
<button
type="button"
(click)="apply({ start: '2026-04-01', end: '2026-04-07' })"
>
First week of April
</button>
</li>
<li>
<button
type="button"
(click)="apply({ start: '2026-04-01', end: '2026-04-30' })"
>
Whole of April
</button>
</li>
<li>
<button type="button" (click)="onModalDismiss()">Cancel</button>
</li>
</ul>
}
</div>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomDateRangeComponent),
multi: true,
},
],
})
export class CustomDateRangeComponent extends DateRangeBaseComponent {
/**
* The base also exposes `onModalApply()`, but that one expects a
* `CalendarState` carrying moment objects produced by the built-in calendar.
* A picker that does not use that calendar writes the value itself and keeps
* everything else the base provides.
*/
apply(range: IDateRange): void {
this.value = range;
this.ngModel.set(range);
this.propagateChange(range);
this.propagateTouched();
this.isOpen.set(false);
}
}
/**
* Date range has no injection token: `<smart-date-range>` picks between its
* `standard` and `preset` variations through the `variant` input, so a custom
* implementation is rendered directly rather than registered.
*/
@Component({
selector: 'docs-date-range-custom-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CustomDateRangeComponent],
template: `
<docs-custom-date-range
[ngModel]="range()"
(ngModelChange)="range.set($event)"
/>
`,
})
export class DateRangeCustomExampleComponent {
range = signal<IDateRange | undefined>({
start: '2026-04-01',
end: '2026-04-07',
});
}
Dependencies
@smartsoft001/domain-core—IDateRangeinterface@ngx-translate/core—TranslatePipefor i18nmoment— date operationsCalendarService— calendar month generation
Tailwind Classes
All classes use smart: prefix. Trigger: smart:inline-flex smart:rounded-md smart:border smart:border-gray-300. Modal backdrop: smart:fixed smart:inset-0 smart:bg-black/50. Calendar selection: smart:bg-indigo-600 smart:text-white.
Source
The component lives in packages/shared/angular/src/lib/components/date-range and is documented for Claude Code by the angular-components-date-range skill.