API reference
The default surface is tng-date-range-picker. When you need custom markup, use createDateRangePickerController(...), bindTngDateRangePicker(...), and the date range picker helper directives instead of wiring keyboard, focus, and ARIA behavior by hand.
The wrapper intentionally sets a few opinionated defaults on top of the controller, notably closeOnSelect, trapFocus, and autoCommitView.
tng-date-range-picker (component)
Wrapper attachment
html
<tng-date-range-picker
calendarLayout="dual"
[defaultValue]="{ start: '2024-04-22', end: '2024-04-26' }"
[minDate]="'2024-04-01'"
[maxDate]="'2026-03-31'"
ariaLabel="Invoice period"
></tng-date-range-picker>
Selection and value
| Property | Type | Default | Details |
|---|---|---|---|
defaultValue | TngDateRangePickerSelectionInput<TDate> | undefined | undefined | Sets the uncontrolled initial range or partial start date. |
value | TngDateRangePickerSelectionInput<TDate> | undefined | undefined | Controls the committed range from the outside. |
enableRangeSelection | boolean | true | Keeps the second click as the range end instead of treating each date as a new start. |
minDate / maxDate | TngDateInputValue<TDate> | undefined | undefined | Disables out-of-range days, months, and years. |
disableDate | ((date: TDate) => boolean) | null | null | Disables individual dates inside the otherwise valid range. |
today | TngDateInputValue<TDate> | undefined | undefined | Overrides which date is marked as today in the grid. |
Interaction and behavior
| Property | Type | Default | Details |
|---|---|---|---|
allowManualInput | boolean | true | Allows typing directly into the field and committing valid values. |
autoCommitView | boolean | false | Controls whether the wrapper should auto-commit when drilling between year, month, and day views. |
closeOnEscape | boolean | true | Closes the popup when Escape is pressed. |
enableTypeahead | boolean | true | Enables keyboard typeahead navigation in month and year grids. |
fixedWeeks | boolean | true | Always renders 6 weeks in the day grid to keep the overlay height stable across months. |
onPartialInputCommit | boolean | false | Allows partial manual input commits (start date only) before the end date is entered. |
skipDisabled | boolean | true | Controls whether keyboard arrow navigation jumps over disabled dates. |
closeOnOutsideClick | boolean | true | Dismisses the popup when pointer or focus moves outside. |
closeOnSelect | boolean | true | Closes after a committed range selection. |
closeOthersOnOpen | boolean | false | Asks other registered date range pickers to close when this one opens. |
restoreFocus | boolean | true | Restores focus to the trigger after the popup closes. |
showOutsideDays | boolean | true | Keeps adjacent-month days visible in day view. |
trapFocus | boolean | true | Keeps focus inside the popup while it is open. |
weekStartsOn | TngWeekdayIndex | 0 | Overrides the locale-derived start of week. |
Overlay and layout
| Property | Type | Default | Details |
|---|---|---|---|
calendarLayout | 'single' | 'dual' | 'responsive' | 'single' | Single keeps Material-style sequential range selection. Dual fixes the leading calendar to start and the trailing calendar to end; responsive only changes the number of visible panels. |
defaultOpen | boolean | false | Sets the uncontrolled initial open state. |
open | boolean | undefined | undefined | Controls the popup state from the outside. |
placement | 'auto' | 'bottom' | 'top' | 'auto' | Auto-flips the popup when needed. The popup uses logical end alignment against the complete input and trigger shell. |
scrollStrategy | 'block' | 'close' | 'reposition' | 'reposition' | Repositions with page scroll and closes if the field leaves view. Use block to lock scrolling while preserving the document scrollbar. |
overlayRuntime | TngOverlayRuntime | null | undefined | Internal runtime | Lets advanced apps share an overlay layer registry across surfaces. |
overlayMinSize | number | undefined | Layout-aware | Overrides the layout-aware minimum popup width before the available viewport width is applied. |
overlaySize | number | undefined | Layout-aware | Overrides the layout-aware maximum popup width. Between the bounds, the popup follows the input-shell width. |
yearPageSize | number | 24 | Controls how many years are shown per year page. |
direction | 'ltr' | 'rtl' | 'ltr' | Flips navigation semantics and keyboard movement for RTL flows. |
Accessibility and presentation
| Property | Type | Default | Details |
|---|---|---|---|
adapter | TngDateAdapter<TDate> | undefined | Default date adapter | Controls parsing, formatting, and visible month or period labels. |
ariaDescribedBy | string | null | null | Forwards an external description id to the host. |
ariaLabel | string | null | null | Sets a root accessible name when no visible label is present. |
ariaLabelledBy | string | null | null | Points the host at an external labeling element. |
disabled | boolean | false | Disables the field, trigger, and all calendar interaction. |
fullWidth | boolean | true | Makes the host fill the available inline size. |
id | string | null | null | Seeds the generated input id and overlay relationship ids. |
inputAriaLabel | string | 'Date range input' | Labels the editable text input itself. |
invalid | boolean | false | Forces invalid styling in addition to manual input validation state. |
locale | string | Angular LOCALE_ID | Drives weekday names, month labels, and adapter locale defaults. |
placeholder | string | 'MM-DD-YYYY - MM-DD-YYYY' | Changes the visible hint only. Parsing still comes from the adapter. |
readonly | boolean | false | Makes the text field read-only while still allowing popup selection. |
Outputs
| Output | Type | Details |
|---|---|---|
valueChange | TngDateRangePickerValue<TDate> | Emits after a click, keyboard commit, or successful manual input range commit. |
openChange | boolean | Emits whenever the popup opens or closes. |
closed | TngDateRangePickerCloseReason | Reports escape, outside, programmatic, or select close reasons. |
activeDateChange | TDate | Emits as keyboard focus moves through the calendar model. |
previewEndDateChange | TDate | Emits as the pointer hovers over candidate end dates during an in-progress range selection. |
viewChange | 'day' | 'month' | 'year' | Tracks the current visible panel. |
monthChange | TDate | Emits when the visible month block changes. |
yearChange | number | Emits when the visible year page anchor changes. |
Wrapper instance methods
| Method | Purpose |
|---|---|
clear() | Clears the current range selection and returns the wrapper to day view. |
close(reason?) | Programmatically closes the popup with an optional close reason. |
openDateRangePicker() | Programmatically opens the popup. |
showDaysPanel() / showMonthsPanel() / showYearsPanel() | Drives the visible panel explicitly when the default drill-down flow is not enough. |
toggleOpen() | Toggles the popup state. |
Headless binding layer
Controller + Angular binding
ts
import { bindTngDateRangePicker, createDateRangePickerController } from '@tailng-ui/primitives';
readonly controller = createDateRangePickerController<Date>({
ownerDocument: document,
value: { start: '2024-04-22', end: '2024-04-26' },
today: '2024-04-18',
minDate: '2024-04-01',
maxDate: '2026-03-31',
closeOnSelect: true,
calendarLayout: 'dual',
trapFocus: true,
});
readonly dateRangePicker = bindTngDateRangePicker(this.controller);
Field + overlay wiring
html
<section [tngDateRangePickerHost]="controller">
<div data-slot="date-range-picker-field">
<div #anchorShell>
<div
data-slot="date-range-picker-input-shell"
[attr.data-invalid]="dateRangePicker.outputs().validationError !== null ? 'true' : null"
[attr.data-open]="dateRangePicker.outputs().getTriggerAttributes()['data-open']"
>
<input [tngDateRangePickerInput]="controller" type="text" placeholder="MM-DD-YYYY - MM-DD-YYYY" />
<button [tngDateRangePickerTrigger]="controller" type="button">Open</button>
</div>
<section [tngDateRangePickerOverlay]="controller" [tngDateRangePickerOverlayAnchor]="anchorShell">
<button [tngDateRangePickerPrevButton]="controller" type="button">‹</button>
<button [tngDateRangePickerPeriodButton]="controller" type="button">
{{ dateRangePicker.periodLabel() }}
</button>
<button [tngDateRangePickerNextButton]="controller" type="button">›</button>
@for (calendar of dateRangePicker.outputs().calendars; track calendar.index) {
<div
[tngDateRangePickerDayGrid]="controller"
[tngDateRangePickerCalendarIndex]="calendar.index"
>
@for (cell of calendar.cells; track cell.id) {
<button [tngDateRangePickerDayCell]="cell" type="button">{{ cell.label }}</button>
}
</div>
}
</section>
</div>
</div>
</section>
| Helper | Purpose |
|---|---|
bindTngDateRangePicker(controller) | Returns signals for outputs() and periodLabel() so Angular templates can stay declarative. |
[tngDateRangePickerHost] | Applies the public root attributes such as data-open, data-view, and ARIA labels. |
[tngDateRangePickerInput] / [tngDateRangePickerTrigger] | Forward manual input editing, trigger registration, and wrapper-grade open and keyboard behavior. |
[tngDateRangePickerOverlay] | Ports the popup to document.body, syncs public overlay attributes, and keeps focus and positioning aligned. |
[tngDateRangePickerPrevButton] / [tngDateRangePickerNextButton] / [tngDateRangePickerPeriodButton] | Own the standard navigation and drill-down flow without per-view branching in your component. |
[tngDateRangePickerDayGrid] / [tngDateRangePickerDayCell] | Forward day-grid keyboarding, click handling, hover range behavior, and the public day-cell state hooks. |
[tngDateRangePickerMonthGrid] / [tngDateRangePickerMonthOption] / [tngDateRangePickerYearGrid] / [tngDateRangePickerYearOption] | Handle month and year picker keyboarding and selection while preserving the public slot contract. |
Advanced controller options
The wrapper covers the common surface. These options are available when you work with the controller directly.
| Option | Type | Default | Details |
|---|---|---|---|
initialView | 'day' | 'month' | 'year' | 'day' | Starts the controller on a different panel than the wrapper exposes by default. |
calendarLayout | 'single' | 'dual' | 'single' | Controls whether outputs.calendars contains one sequential range calendar or a consecutive start/end pair. |
overlayMode | 'overlay' | 'push' | 'side' | 'overlay' | Changes how the controller models overlay layout when you are fully headless. |
position | 'start' | 'center' | 'end' | 'start' | Changes overlay alignment relative to the field in headless layouts. |
focusStrategy | 'active-descendant' | 'roving' | 'roving' | Lets advanced compositions opt into a different grid focus model. |
onPartialInputCommit | boolean | false | Allows partial manual input commits in advanced headless flows. |
preserveViewOnOpenClose | boolean | true | Keeps the current panel when reopening instead of always returning to day view. |
skipDisabled | boolean | true | Controls whether keyboard movement jumps across disabled dates. |
Controller methods
| Method | Purpose |
|---|---|
getOutputs() / getState() | Read the live render model or the lower-level mutable state snapshot. |
open() / close() / toggleOpen() | Own popup visibility when you are not using the wrapper. |
setInputText(...) / commitInputText() / parseInputText(...) | Support manual editing with adapter validation and bound checks. |
selectDate(...) / clear() / setValue(...) | Own the committed selection state directly. |
showYearsPanel() / showMonthsPanel() / showDaysPanel() | Drive the visible panel explicitly. |
setConfig(...) | Reconfigures the controller after creation for advanced custom integrations. |
subscribe(...) | Exposes low-level controller events when the template bindings are not enough. |