@ngneers/controls 0.0.1-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/ngneers-controls-accordion.mjs +234 -0
- package/fesm2022/ngneers-controls-accordion.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api-ng.mjs +959 -0
- package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api-resize.mjs +712 -0
- package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -0
- package/fesm2022/ngneers-controls-api.mjs +347 -0
- package/fesm2022/ngneers-controls-api.mjs.map +1 -0
- package/fesm2022/ngneers-controls-avatar.mjs +77 -0
- package/fesm2022/ngneers-controls-avatar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-base.mjs +620 -0
- package/fesm2022/ngneers-controls-base.mjs.map +1 -0
- package/fesm2022/ngneers-controls-breadcrumb.mjs +100 -0
- package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -0
- package/fesm2022/ngneers-controls-button-group.mjs +110 -0
- package/fesm2022/ngneers-controls-button-group.mjs.map +1 -0
- package/fesm2022/ngneers-controls-button.mjs +83 -0
- package/fesm2022/ngneers-controls-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs +74 -0
- package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs.map +1 -0
- package/fesm2022/ngneers-controls-calendar.mjs +801 -0
- package/fesm2022/ngneers-controls-calendar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-checkbox.mjs +63 -0
- package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -0
- package/fesm2022/ngneers-controls-chip.mjs +55 -0
- package/fesm2022/ngneers-controls-chip.mjs.map +1 -0
- package/fesm2022/ngneers-controls-default-icons.mjs +82 -0
- package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
- package/fesm2022/ngneers-controls-defer.mjs +72 -0
- package/fesm2022/ngneers-controls-defer.mjs.map +1 -0
- package/fesm2022/ngneers-controls-dialog.mjs +407 -0
- package/fesm2022/ngneers-controls-dialog.mjs.map +1 -0
- package/fesm2022/ngneers-controls-directives.mjs +498 -0
- package/fesm2022/ngneers-controls-directives.mjs.map +1 -0
- package/fesm2022/ngneers-controls-drawer.mjs +271 -0
- package/fesm2022/ngneers-controls-drawer.mjs.map +1 -0
- package/fesm2022/ngneers-controls-edit-inplace.mjs +127 -0
- package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -0
- package/fesm2022/ngneers-controls-errors.mjs +329 -0
- package/fesm2022/ngneers-controls-errors.mjs.map +1 -0
- package/fesm2022/ngneers-controls-filter.mjs +708 -0
- package/fesm2022/ngneers-controls-filter.mjs.map +1 -0
- package/fesm2022/ngneers-controls-hint.mjs +153 -0
- package/fesm2022/ngneers-controls-hint.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n-translations-de.mjs +170 -0
- package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n-translations-en.mjs +170 -0
- package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -0
- package/fesm2022/ngneers-controls-i18n.mjs +57 -0
- package/fesm2022/ngneers-controls-i18n.mjs.map +1 -0
- package/fesm2022/ngneers-controls-icon.mjs +195 -0
- package/fesm2022/ngneers-controls-icon.mjs.map +1 -0
- package/fesm2022/ngneers-controls-inplace.mjs +150 -0
- package/fesm2022/ngneers-controls-inplace.mjs.map +1 -0
- package/fesm2022/ngneers-controls-input-field.mjs +165 -0
- package/fesm2022/ngneers-controls-input-field.mjs.map +1 -0
- package/fesm2022/ngneers-controls-input.mjs +72 -0
- package/fesm2022/ngneers-controls-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-item-view.mjs +473 -0
- package/fesm2022/ngneers-controls-item-view.mjs.map +1 -0
- package/fesm2022/ngneers-controls-list-box.mjs +281 -0
- package/fesm2022/ngneers-controls-list-box.mjs.map +1 -0
- package/fesm2022/ngneers-controls-mask-input.mjs +888 -0
- package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-menu.mjs +371 -0
- package/fesm2022/ngneers-controls-menu.mjs.map +1 -0
- package/fesm2022/ngneers-controls-message.mjs +30 -0
- package/fesm2022/ngneers-controls-message.mjs.map +1 -0
- package/fesm2022/ngneers-controls-number-input.mjs +366 -0
- package/fesm2022/ngneers-controls-number-input.mjs.map +1 -0
- package/fesm2022/ngneers-controls-paginator.mjs +129 -0
- package/fesm2022/ngneers-controls-paginator.mjs.map +1 -0
- package/fesm2022/ngneers-controls-popover.mjs +215 -0
- package/fesm2022/ngneers-controls-popover.mjs.map +1 -0
- package/fesm2022/ngneers-controls-progress.mjs +98 -0
- package/fesm2022/ngneers-controls-progress.mjs.map +1 -0
- package/fesm2022/ngneers-controls-radio.mjs +152 -0
- package/fesm2022/ngneers-controls-radio.mjs.map +1 -0
- package/fesm2022/ngneers-controls-roving-focus.mjs +323 -0
- package/fesm2022/ngneers-controls-roving-focus.mjs.map +1 -0
- package/fesm2022/ngneers-controls-scroll-shadow.mjs +65 -0
- package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
- package/fesm2022/ngneers-controls-scroller.mjs +308 -0
- package/fesm2022/ngneers-controls-scroller.mjs.map +1 -0
- package/fesm2022/ngneers-controls-select-button.mjs +58 -0
- package/fesm2022/ngneers-controls-select-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-select.mjs +417 -0
- package/fesm2022/ngneers-controls-select.mjs.map +1 -0
- package/fesm2022/ngneers-controls-slider.mjs +167 -0
- package/fesm2022/ngneers-controls-slider.mjs.map +1 -0
- package/fesm2022/ngneers-controls-snackbar.mjs +414 -0
- package/fesm2022/ngneers-controls-snackbar.mjs.map +1 -0
- package/fesm2022/ngneers-controls-spin-buttons.mjs +126 -0
- package/fesm2022/ngneers-controls-spin-buttons.mjs.map +1 -0
- package/fesm2022/ngneers-controls-spinner.mjs +184 -0
- package/fesm2022/ngneers-controls-spinner.mjs.map +1 -0
- package/fesm2022/ngneers-controls-splitter.mjs +604 -0
- package/fesm2022/ngneers-controls-splitter.mjs.map +1 -0
- package/fesm2022/ngneers-controls-state.mjs +185 -0
- package/fesm2022/ngneers-controls-state.mjs.map +1 -0
- package/fesm2022/ngneers-controls-switch.mjs +39 -0
- package/fesm2022/ngneers-controls-switch.mjs.map +1 -0
- package/fesm2022/ngneers-controls-table.mjs +2634 -0
- package/fesm2022/ngneers-controls-table.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tabs.mjs +336 -0
- package/fesm2022/ngneers-controls-tabs.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tag.mjs +30 -0
- package/fesm2022/ngneers-controls-tag.mjs.map +1 -0
- package/fesm2022/ngneers-controls-toast.mjs +350 -0
- package/fesm2022/ngneers-controls-toast.mjs.map +1 -0
- package/fesm2022/ngneers-controls-toggle-button.mjs +70 -0
- package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tooltip.mjs +506 -0
- package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -0
- package/fesm2022/ngneers-controls-tree.mjs +599 -0
- package/fesm2022/ngneers-controls-tree.mjs.map +1 -0
- package/fesm2022/ngneers-controls-upload.mjs +377 -0
- package/fesm2022/ngneers-controls-upload.mjs.map +1 -0
- package/fesm2022/ngneers-controls-utils-ng.mjs +411 -0
- package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -0
- package/fesm2022/ngneers-controls-utils.mjs +261 -0
- package/fesm2022/ngneers-controls-utils.mjs.map +1 -0
- package/fesm2022/ngneers-controls.mjs +6 -0
- package/fesm2022/ngneers-controls.mjs.map +1 -0
- package/package.json +302 -0
- package/types/ngneers-controls-accordion.d.ts +139 -0
- package/types/ngneers-controls-api-ng.d.ts +364 -0
- package/types/ngneers-controls-api-resize.d.ts +318 -0
- package/types/ngneers-controls-api.d.ts +145 -0
- package/types/ngneers-controls-avatar.d.ts +54 -0
- package/types/ngneers-controls-base.d.ts +277 -0
- package/types/ngneers-controls-breadcrumb.d.ts +272 -0
- package/types/ngneers-controls-button-group.d.ts +46 -0
- package/types/ngneers-controls-button.d.ts +48 -0
- package/types/ngneers-controls-calendar.d.ts +466 -0
- package/types/ngneers-controls-checkbox.d.ts +40 -0
- package/types/ngneers-controls-chip.d.ts +202 -0
- package/types/ngneers-controls-default-icons.d.ts +5 -0
- package/types/ngneers-controls-defer.d.ts +48 -0
- package/types/ngneers-controls-dialog.d.ts +399 -0
- package/types/ngneers-controls-directives.d.ts +171 -0
- package/types/ngneers-controls-drawer.d.ts +308 -0
- package/types/ngneers-controls-edit-inplace.d.ts +263 -0
- package/types/ngneers-controls-errors.d.ts +155 -0
- package/types/ngneers-controls-filter.d.ts +609 -0
- package/types/ngneers-controls-hint.d.ts +88 -0
- package/types/ngneers-controls-i18n-translations-de.d.ts +164 -0
- package/types/ngneers-controls-i18n-translations-en.d.ts +164 -0
- package/types/ngneers-controls-i18n.d.ts +186 -0
- package/types/ngneers-controls-icon.d.ts +68 -0
- package/types/ngneers-controls-inplace.d.ts +98 -0
- package/types/ngneers-controls-input-field.d.ts +248 -0
- package/types/ngneers-controls-input.d.ts +30 -0
- package/types/ngneers-controls-item-view.d.ts +215 -0
- package/types/ngneers-controls-list-box.d.ts +334 -0
- package/types/ngneers-controls-mask-input.d.ts +434 -0
- package/types/ngneers-controls-menu.d.ts +192 -0
- package/types/ngneers-controls-message.d.ts +21 -0
- package/types/ngneers-controls-number-input.d.ts +146 -0
- package/types/ngneers-controls-paginator.d.ts +255 -0
- package/types/ngneers-controls-popover.d.ts +116 -0
- package/types/ngneers-controls-progress.d.ts +49 -0
- package/types/ngneers-controls-radio.d.ts +91 -0
- package/types/ngneers-controls-roving-focus.d.ts +107 -0
- package/types/ngneers-controls-scroll-shadow.d.ts +25 -0
- package/types/ngneers-controls-scroller.d.ts +151 -0
- package/types/ngneers-controls-select-button.d.ts +51 -0
- package/types/ngneers-controls-select.d.ts +409 -0
- package/types/ngneers-controls-slider.d.ts +57 -0
- package/types/ngneers-controls-snackbar.d.ts +330 -0
- package/types/ngneers-controls-spin-buttons.d.ts +69 -0
- package/types/ngneers-controls-spinner.d.ts +117 -0
- package/types/ngneers-controls-splitter.d.ts +465 -0
- package/types/ngneers-controls-state.d.ts +102 -0
- package/types/ngneers-controls-switch.d.ts +18 -0
- package/types/ngneers-controls-table.d.ts +3127 -0
- package/types/ngneers-controls-tabs.d.ts +118 -0
- package/types/ngneers-controls-tag.d.ts +21 -0
- package/types/ngneers-controls-toast.d.ts +298 -0
- package/types/ngneers-controls-toggle-button.d.ts +48 -0
- package/types/ngneers-controls-tooltip.d.ts +189 -0
- package/types/ngneers-controls-tree.d.ts +409 -0
- package/types/ngneers-controls-upload.d.ts +347 -0
- package/types/ngneers-controls-utils-ng.d.ts +129 -0
- package/types/ngneers-controls-utils.d.ts +130 -0
- package/types/ngneers-controls.d.ts +2 -0
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { viewChild, contentChild, input, computed, Directive, output, inject, viewChildren, Component, booleanAttribute, linkedSignal, signal, effect } from '@angular/core';
|
|
4
|
+
import { templateTypesFn, Platform, NgnTemplate } from '@ngneers/controls/api/ng';
|
|
5
|
+
import { ValueControlBase, NgnPt, provideSelf } from '@ngneers/controls/base';
|
|
6
|
+
import { I18n } from '@ngneers/controls/i18n';
|
|
7
|
+
import { NgnIcon } from '@ngneers/controls/icon';
|
|
8
|
+
import { NgnMaskInput, getDateOrTimeMask } from '@ngneers/controls/mask-input';
|
|
9
|
+
import { NgnPopover } from '@ngneers/controls/popover';
|
|
10
|
+
import { throwExp, NgnError } from '@ngneers/controls/utils';
|
|
11
|
+
import { calendarControlTemplate } from '@ngneers/controls-themes/templates/calendar';
|
|
12
|
+
import { NgnButton } from '@ngneers/controls/button';
|
|
13
|
+
|
|
14
|
+
class CalendarTemplates extends ValueControlBase {
|
|
15
|
+
// Day template
|
|
16
|
+
_defaultDayTemplate = viewChild.required('defaultDayTemplate');
|
|
17
|
+
_userDayTemplate = contentChild('day', /* @ts-ignore */
|
|
18
|
+
...(ngDevMode ? [{ debugName: "_userDayTemplate" }] : /* istanbul ignore next */ []));
|
|
19
|
+
/**
|
|
20
|
+
* Set a custom template for a day.
|
|
21
|
+
* Can also be set using an `<ng-template>` element with `#day` template reference variable.
|
|
22
|
+
*/
|
|
23
|
+
templateDay = input(null, /* @ts-ignore */
|
|
24
|
+
...(ngDevMode ? [{ debugName: "templateDay" }] : /* istanbul ignore next */ []));
|
|
25
|
+
dayTemplate = computed(() => this._userDayTemplate() ?? this.templateDay() ?? this._defaultDayTemplate(), /* @ts-ignore */
|
|
26
|
+
...(ngDevMode ? [{ debugName: "dayTemplate" }] : /* istanbul ignore next */ []));
|
|
27
|
+
// Week day template
|
|
28
|
+
_defaultWeekDayTemplate = viewChild.required('defaultWeekDayTemplate');
|
|
29
|
+
_userWeekDayTemplate = contentChild('weekDay', /* @ts-ignore */
|
|
30
|
+
...(ngDevMode ? [{ debugName: "_userWeekDayTemplate" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/**
|
|
32
|
+
* Set a custom template for a week day.
|
|
33
|
+
* Can also be set using an `<ng-template>` element with `#weekDay` template reference variable.
|
|
34
|
+
*/
|
|
35
|
+
templateWeekDay = input(null, /* @ts-ignore */
|
|
36
|
+
...(ngDevMode ? [{ debugName: "templateWeekDay" }] : /* istanbul ignore next */ []));
|
|
37
|
+
weekDayTemplate = computed(() => this._userWeekDayTemplate() ?? this.templateWeekDay() ?? this._defaultWeekDayTemplate(), /* @ts-ignore */
|
|
38
|
+
...(ngDevMode ? [{ debugName: "weekDayTemplate" }] : /* istanbul ignore next */ []));
|
|
39
|
+
// Month template
|
|
40
|
+
_defaultMonthTemplate = viewChild.required('defaultMonthTemplate');
|
|
41
|
+
_userMonthTemplate = contentChild('month', /* @ts-ignore */
|
|
42
|
+
...(ngDevMode ? [{ debugName: "_userMonthTemplate" }] : /* istanbul ignore next */ []));
|
|
43
|
+
/**
|
|
44
|
+
* Set a custom template for a month.
|
|
45
|
+
* Can also be set using an `<ng-template>` element with `#month` template reference variable.
|
|
46
|
+
*/
|
|
47
|
+
templateMonth = input(null, /* @ts-ignore */
|
|
48
|
+
...(ngDevMode ? [{ debugName: "templateMonth" }] : /* istanbul ignore next */ []));
|
|
49
|
+
monthTemplate = computed(() => this._userMonthTemplate() ?? this.templateMonth() ?? this._defaultMonthTemplate(), /* @ts-ignore */
|
|
50
|
+
...(ngDevMode ? [{ debugName: "monthTemplate" }] : /* istanbul ignore next */ []));
|
|
51
|
+
// Time template
|
|
52
|
+
_defaultTimeTemplate = viewChild.required('defaultTimeTemplate');
|
|
53
|
+
_userTimeTemplate = contentChild('time', /* @ts-ignore */
|
|
54
|
+
...(ngDevMode ? [{ debugName: "_userTimeTemplate" }] : /* istanbul ignore next */ []));
|
|
55
|
+
/**
|
|
56
|
+
* Set a custom template for the time input.
|
|
57
|
+
* Can also be set using an `<ng-template>` element with `#time` template reference variable.
|
|
58
|
+
*/
|
|
59
|
+
templateTime = input(null, /* @ts-ignore */
|
|
60
|
+
...(ngDevMode ? [{ debugName: "templateTime" }] : /* istanbul ignore next */ []));
|
|
61
|
+
timeTemplate = computed(() => this._userTimeTemplate() ?? this.templateTime() ?? this._defaultTimeTemplate(), /* @ts-ignore */
|
|
62
|
+
...(ngDevMode ? [{ debugName: "timeTemplate" }] : /* istanbul ignore next */ []));
|
|
63
|
+
// Year template
|
|
64
|
+
_defaultYearTemplate = viewChild.required('defaultYearTemplate');
|
|
65
|
+
_userYearTemplate = contentChild('year', /* @ts-ignore */
|
|
66
|
+
...(ngDevMode ? [{ debugName: "_userYearTemplate" }] : /* istanbul ignore next */ []));
|
|
67
|
+
/**
|
|
68
|
+
* Set a custom template for the year input.
|
|
69
|
+
* Can also be set using an `<ng-template>` element with `#year` template reference variable.
|
|
70
|
+
*/
|
|
71
|
+
templateYear = input(null, /* @ts-ignore */
|
|
72
|
+
...(ngDevMode ? [{ debugName: "templateYear" }] : /* istanbul ignore next */ []));
|
|
73
|
+
yearTemplate = computed(() => this._userYearTemplate() ?? this.templateYear() ?? this._defaultYearTemplate(), /* @ts-ignore */
|
|
74
|
+
...(ngDevMode ? [{ debugName: "yearTemplate" }] : /* istanbul ignore next */ []));
|
|
75
|
+
/**
|
|
76
|
+
* Types for the calendar templates.
|
|
77
|
+
*/
|
|
78
|
+
templateTypes = templateTypesFn();
|
|
79
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CalendarTemplates, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
80
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.5", type: CalendarTemplates, isStandalone: true, inputs: { templateDay: { classPropertyName: "templateDay", publicName: "templateDay", isSignal: true, isRequired: false, transformFunction: null }, templateWeekDay: { classPropertyName: "templateWeekDay", publicName: "templateWeekDay", isSignal: true, isRequired: false, transformFunction: null }, templateMonth: { classPropertyName: "templateMonth", publicName: "templateMonth", isSignal: true, isRequired: false, transformFunction: null }, templateTime: { classPropertyName: "templateTime", publicName: "templateTime", isSignal: true, isRequired: false, transformFunction: null }, templateYear: { classPropertyName: "templateYear", publicName: "templateYear", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "_userDayTemplate", first: true, predicate: ["day"], descendants: true, isSignal: true }, { propertyName: "_userWeekDayTemplate", first: true, predicate: ["weekDay"], descendants: true, isSignal: true }, { propertyName: "_userMonthTemplate", first: true, predicate: ["month"], descendants: true, isSignal: true }, { propertyName: "_userTimeTemplate", first: true, predicate: ["time"], descendants: true, isSignal: true }, { propertyName: "_userYearTemplate", first: true, predicate: ["year"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "_defaultDayTemplate", first: true, predicate: ["defaultDayTemplate"], descendants: true, isSignal: true }, { propertyName: "_defaultWeekDayTemplate", first: true, predicate: ["defaultWeekDayTemplate"], descendants: true, isSignal: true }, { propertyName: "_defaultMonthTemplate", first: true, predicate: ["defaultMonthTemplate"], descendants: true, isSignal: true }, { propertyName: "_defaultTimeTemplate", first: true, predicate: ["defaultTimeTemplate"], descendants: true, isSignal: true }, { propertyName: "_defaultYearTemplate", first: true, predicate: ["defaultYearTemplate"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0 });
|
|
81
|
+
}
|
|
82
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CalendarTemplates, decorators: [{
|
|
83
|
+
type: Directive
|
|
84
|
+
}], propDecorators: { _defaultDayTemplate: [{ type: i0.ViewChild, args: ['defaultDayTemplate', { isSignal: true }] }], _userDayTemplate: [{ type: i0.ContentChild, args: ['day', { isSignal: true }] }], templateDay: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateDay", required: false }] }], _defaultWeekDayTemplate: [{ type: i0.ViewChild, args: ['defaultWeekDayTemplate', { isSignal: true }] }], _userWeekDayTemplate: [{ type: i0.ContentChild, args: ['weekDay', { isSignal: true }] }], templateWeekDay: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateWeekDay", required: false }] }], _defaultMonthTemplate: [{ type: i0.ViewChild, args: ['defaultMonthTemplate', { isSignal: true }] }], _userMonthTemplate: [{ type: i0.ContentChild, args: ['month', { isSignal: true }] }], templateMonth: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateMonth", required: false }] }], _defaultTimeTemplate: [{ type: i0.ViewChild, args: ['defaultTimeTemplate', { isSignal: true }] }], _userTimeTemplate: [{ type: i0.ContentChild, args: ['time', { isSignal: true }] }], templateTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateTime", required: false }] }], _defaultYearTemplate: [{ type: i0.ViewChild, args: ['defaultYearTemplate', { isSignal: true }] }], _userYearTemplate: [{ type: i0.ContentChild, args: ['year', { isSignal: true }] }], templateYear: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateYear", required: false }] }] } });
|
|
85
|
+
|
|
86
|
+
const MONTHS = [
|
|
87
|
+
'january',
|
|
88
|
+
'february',
|
|
89
|
+
'march',
|
|
90
|
+
'april',
|
|
91
|
+
'may',
|
|
92
|
+
'june',
|
|
93
|
+
'july',
|
|
94
|
+
'august',
|
|
95
|
+
'september',
|
|
96
|
+
'october',
|
|
97
|
+
'november',
|
|
98
|
+
'december',
|
|
99
|
+
];
|
|
100
|
+
const WEEK_DAYS = [
|
|
101
|
+
'sunday',
|
|
102
|
+
'monday',
|
|
103
|
+
'tuesday',
|
|
104
|
+
'wednesday',
|
|
105
|
+
'thursday',
|
|
106
|
+
'friday',
|
|
107
|
+
'saturday',
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
// Configuration: Number of weeks to show before and after the current month
|
|
111
|
+
const WEEKS_BEFORE = 1;
|
|
112
|
+
const WEEKS_AFTER = 1;
|
|
113
|
+
class CalendarDays {
|
|
114
|
+
component = input.required(/* @ts-ignore */
|
|
115
|
+
...(ngDevMode ? [{ debugName: "component" }] : /* istanbul ignore next */ []));
|
|
116
|
+
theme = input.required(/* @ts-ignore */
|
|
117
|
+
...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
|
|
118
|
+
year = input.required(/* @ts-ignore */
|
|
119
|
+
...(ngDevMode ? [{ debugName: "year" }] : /* istanbul ignore next */ []));
|
|
120
|
+
month = input.required(/* @ts-ignore */
|
|
121
|
+
...(ngDevMode ? [{ debugName: "month" }] : /* istanbul ignore next */ []));
|
|
122
|
+
currentValue = input.required(/* @ts-ignore */
|
|
123
|
+
...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
|
|
124
|
+
firstDayOfWeek = input.required(/* @ts-ignore */
|
|
125
|
+
...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : /* istanbul ignore next */ []));
|
|
126
|
+
dayTemplate = input.required(/* @ts-ignore */
|
|
127
|
+
...(ngDevMode ? [{ debugName: "dayTemplate" }] : /* istanbul ignore next */ []));
|
|
128
|
+
weekDayTemplate = input.required(/* @ts-ignore */
|
|
129
|
+
...(ngDevMode ? [{ debugName: "weekDayTemplate" }] : /* istanbul ignore next */ []));
|
|
130
|
+
timeTemplate = input.required(/* @ts-ignore */
|
|
131
|
+
...(ngDevMode ? [{ debugName: "timeTemplate" }] : /* istanbul ignore next */ []));
|
|
132
|
+
yearTemplate = input.required(/* @ts-ignore */
|
|
133
|
+
...(ngDevMode ? [{ debugName: "yearTemplate" }] : /* istanbul ignore next */ []));
|
|
134
|
+
monthTemplate = input.required(/* @ts-ignore */
|
|
135
|
+
...(ngDevMode ? [{ debugName: "monthTemplate" }] : /* istanbul ignore next */ []));
|
|
136
|
+
showTime = input.required(/* @ts-ignore */
|
|
137
|
+
...(ngDevMode ? [{ debugName: "showTime" }] : /* istanbul ignore next */ []));
|
|
138
|
+
showSeconds = input.required(/* @ts-ignore */
|
|
139
|
+
...(ngDevMode ? [{ debugName: "showSeconds" }] : /* istanbul ignore next */ []));
|
|
140
|
+
previousMonth = output();
|
|
141
|
+
nextMonth = output();
|
|
142
|
+
daySelected = output();
|
|
143
|
+
yearSelected = output();
|
|
144
|
+
monthSelected = output();
|
|
145
|
+
timeChanged = output();
|
|
146
|
+
MONTHS = MONTHS;
|
|
147
|
+
i18n = inject(I18n).translations;
|
|
148
|
+
todaysDay = new Date().getDate();
|
|
149
|
+
todaysMonth = new Date().getMonth();
|
|
150
|
+
todaysYear = new Date().getFullYear();
|
|
151
|
+
doTimeChange = (t) => this.timeChanged.emit(t);
|
|
152
|
+
doYearChange = (t) => this.yearSelected.emit(t);
|
|
153
|
+
doMonthChange = (t) => this.monthSelected.emit(t);
|
|
154
|
+
weekDaysSorted = computed(() => WEEK_DAYS.slice(this._firstDayOfWeekIndex())
|
|
155
|
+
.concat(WEEK_DAYS.slice(0, this._firstDayOfWeekIndex()))
|
|
156
|
+
.map(day => ({
|
|
157
|
+
weekDay: day,
|
|
158
|
+
translation: this.i18n[`calendar_weekdaysShort_${day}`],
|
|
159
|
+
})), /* @ts-ignore */
|
|
160
|
+
...(ngDevMode ? [{ debugName: "weekDaysSorted" }] : /* istanbul ignore next */ []));
|
|
161
|
+
_dayButton = viewChildren('dayButton', /* @ts-ignore */
|
|
162
|
+
...(ngDevMode ? [{ debugName: "_dayButton" }] : /* istanbul ignore next */ []));
|
|
163
|
+
_firstDayOfWeekIndex = computed(() => WEEK_DAYS.indexOf(this.firstDayOfWeek()), /* @ts-ignore */
|
|
164
|
+
...(ngDevMode ? [{ debugName: "_firstDayOfWeekIndex" }] : /* istanbul ignore next */ []));
|
|
165
|
+
_daysInMonth = computed(() => new Date(this.year(), this.month() + 1, 0).getDate(), /* @ts-ignore */
|
|
166
|
+
...(ngDevMode ? [{ debugName: "_daysInMonth" }] : /* istanbul ignore next */ []));
|
|
167
|
+
_daysInPreviousMonth = computed(() => new Date(this.year(), this.month(), 0).getDate(), /* @ts-ignore */
|
|
168
|
+
...(ngDevMode ? [{ debugName: "_daysInPreviousMonth" }] : /* istanbul ignore next */ []));
|
|
169
|
+
_firstDayOfMonth = computed(() => new Date(this.year(), this.month(), 1).getDay(), /* @ts-ignore */
|
|
170
|
+
...(ngDevMode ? [{ debugName: "_firstDayOfMonth" }] : /* istanbul ignore next */ []));
|
|
171
|
+
monthModel = computed(() => {
|
|
172
|
+
const firstDayOfMonth = this._firstDayOfMonth();
|
|
173
|
+
const firstDayOfWeek = this._firstDayOfWeekIndex();
|
|
174
|
+
const daysInMonth = this._daysInMonth();
|
|
175
|
+
const daysInPreviousMonth = this._daysInPreviousMonth();
|
|
176
|
+
// Calculate starting point and total days needed
|
|
177
|
+
const firstDayOffset = (firstDayOfMonth - firstDayOfWeek + 7) % 7;
|
|
178
|
+
const daysFromPreviousMonth = firstDayOffset + WEEKS_BEFORE * 7;
|
|
179
|
+
const daysToNextMonth = (7 - ((daysFromPreviousMonth + daysInMonth) % 7)) % 7;
|
|
180
|
+
const totalDays = daysFromPreviousMonth + daysInMonth + daysToNextMonth + WEEKS_AFTER * 7;
|
|
181
|
+
const startDay = daysInPreviousMonth - daysFromPreviousMonth + 1;
|
|
182
|
+
// Build all days in one pass
|
|
183
|
+
const allDays = [];
|
|
184
|
+
let currentDate = startDay;
|
|
185
|
+
let currentMonth = -1; // -1 = previous, 0 = current, 1 = next
|
|
186
|
+
for (let i = 0; i < totalDays; i++) {
|
|
187
|
+
// Determine which month we're in
|
|
188
|
+
if (currentDate > daysInPreviousMonth && currentMonth === -1) {
|
|
189
|
+
currentDate = 1;
|
|
190
|
+
currentMonth = 0;
|
|
191
|
+
}
|
|
192
|
+
else if (currentDate > daysInMonth && currentMonth === 0) {
|
|
193
|
+
currentDate = 1;
|
|
194
|
+
currentMonth = 1;
|
|
195
|
+
}
|
|
196
|
+
allDays.push({
|
|
197
|
+
date: currentDate,
|
|
198
|
+
monthOffset: currentMonth,
|
|
199
|
+
});
|
|
200
|
+
currentDate++;
|
|
201
|
+
}
|
|
202
|
+
// Group into weeks
|
|
203
|
+
const weeks = [];
|
|
204
|
+
for (let i = 0; i < allDays.length; i += 7) {
|
|
205
|
+
weeks.push({
|
|
206
|
+
days: allDays.slice(i, i + 7),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return { weeks };
|
|
210
|
+
}, /* @ts-ignore */
|
|
211
|
+
...(ngDevMode ? [{ debugName: "monthModel" }] : /* istanbul ignore next */ []));
|
|
212
|
+
tabFocusDay = computed(() => {
|
|
213
|
+
const val = this.currentValue();
|
|
214
|
+
if (val && val.getMonth() === this.month() && val.getFullYear() === this.year()) {
|
|
215
|
+
return val.getDate();
|
|
216
|
+
}
|
|
217
|
+
return 1;
|
|
218
|
+
}, /* @ts-ignore */
|
|
219
|
+
...(ngDevMode ? [{ debugName: "tabFocusDay" }] : /* istanbul ignore next */ []));
|
|
220
|
+
/** Accessible name of the day grid — the visible month and year. */
|
|
221
|
+
gridLabel = computed(() => new Date(this.year(), this.month(), 1).toLocaleDateString(undefined, {
|
|
222
|
+
month: 'long',
|
|
223
|
+
year: 'numeric',
|
|
224
|
+
}), /* @ts-ignore */
|
|
225
|
+
...(ngDevMode ? [{ debugName: "gridLabel" }] : /* istanbul ignore next */ []));
|
|
226
|
+
/** Full, localized date used as each day cell's accessible name (e.g. "Thursday, 15 January 2026"). */
|
|
227
|
+
dayLabel(day) {
|
|
228
|
+
return new Date(this.year(), this.month() + day.monthOffset, day.date).toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
|
229
|
+
}
|
|
230
|
+
/** Whether the given day cell holds the currently selected value. */
|
|
231
|
+
isSelected(day) {
|
|
232
|
+
const val = this.currentValue();
|
|
233
|
+
return (!!val &&
|
|
234
|
+
val.getDate() === day.date &&
|
|
235
|
+
val.getMonth() === this.month() + day.monthOffset &&
|
|
236
|
+
val.getFullYear() === this.year());
|
|
237
|
+
}
|
|
238
|
+
/** Whether the given day cell is today. */
|
|
239
|
+
isToday(day) {
|
|
240
|
+
return (this.todaysMonth === this.month() + day.monthOffset &&
|
|
241
|
+
this.todaysDay === day.date &&
|
|
242
|
+
this.todaysYear === this.year());
|
|
243
|
+
}
|
|
244
|
+
prev() {
|
|
245
|
+
this.previousMonth.emit();
|
|
246
|
+
}
|
|
247
|
+
next() {
|
|
248
|
+
this.nextMonth.emit();
|
|
249
|
+
}
|
|
250
|
+
selectYear(year) {
|
|
251
|
+
this.yearSelected.emit(year);
|
|
252
|
+
}
|
|
253
|
+
dayKeydown(event, day) {
|
|
254
|
+
if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(event.key)) {
|
|
255
|
+
event.preventDefault();
|
|
256
|
+
const getCurrentMonthButtons = () => {
|
|
257
|
+
return this._dayButton()
|
|
258
|
+
.map(ref => ref.nativeElement)
|
|
259
|
+
.filter(x => !x.classList.contains(this.theme().class('day-other-month')));
|
|
260
|
+
};
|
|
261
|
+
const allDayButtons = getCurrentMonthButtons();
|
|
262
|
+
const index = allDayButtons.indexOf(day);
|
|
263
|
+
const daysInMonth = this._daysInMonth();
|
|
264
|
+
if (event.key === 'ArrowRight') {
|
|
265
|
+
if (index < daysInMonth - 1) {
|
|
266
|
+
allDayButtons[index + 1]?.focus();
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
this.next();
|
|
270
|
+
setTimeout(() => {
|
|
271
|
+
getCurrentMonthButtons()[0]?.focus();
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else if (event.key === 'ArrowLeft') {
|
|
276
|
+
if (index > 0) {
|
|
277
|
+
allDayButtons[index - 1]?.focus();
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
this.prev();
|
|
281
|
+
setTimeout(() => {
|
|
282
|
+
const buttons = getCurrentMonthButtons();
|
|
283
|
+
buttons[buttons.length - 1]?.focus();
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else if (event.key === 'ArrowDown') {
|
|
288
|
+
if (index < daysInMonth - 7) {
|
|
289
|
+
allDayButtons[index + 7]?.focus();
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
this.next();
|
|
293
|
+
const nextIndex = (index + 7) % daysInMonth;
|
|
294
|
+
setTimeout(() => {
|
|
295
|
+
getCurrentMonthButtons()[nextIndex]?.focus();
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (event.key === 'ArrowUp') {
|
|
300
|
+
if (index >= 7) {
|
|
301
|
+
allDayButtons[index - 7]?.focus();
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
this.prev();
|
|
305
|
+
setTimeout(() => {
|
|
306
|
+
const buttons = getCurrentMonthButtons();
|
|
307
|
+
const targetIndex = Math.min(buttons.length - 7 + index, buttons.length - 1);
|
|
308
|
+
buttons[targetIndex]?.focus();
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CalendarDays, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
315
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CalendarDays, isStandalone: true, selector: "ngn-calendar-days", inputs: { component: { classPropertyName: "component", publicName: "component", isSignal: true, isRequired: true, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: true, transformFunction: null }, year: { classPropertyName: "year", publicName: "year", isSignal: true, isRequired: true, transformFunction: null }, month: { classPropertyName: "month", publicName: "month", isSignal: true, isRequired: true, transformFunction: null }, currentValue: { classPropertyName: "currentValue", publicName: "currentValue", isSignal: true, isRequired: true, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: true, transformFunction: null }, dayTemplate: { classPropertyName: "dayTemplate", publicName: "dayTemplate", isSignal: true, isRequired: true, transformFunction: null }, weekDayTemplate: { classPropertyName: "weekDayTemplate", publicName: "weekDayTemplate", isSignal: true, isRequired: true, transformFunction: null }, timeTemplate: { classPropertyName: "timeTemplate", publicName: "timeTemplate", isSignal: true, isRequired: true, transformFunction: null }, yearTemplate: { classPropertyName: "yearTemplate", publicName: "yearTemplate", isSignal: true, isRequired: true, transformFunction: null }, monthTemplate: { classPropertyName: "monthTemplate", publicName: "monthTemplate", isSignal: true, isRequired: true, transformFunction: null }, showTime: { classPropertyName: "showTime", publicName: "showTime", isSignal: true, isRequired: true, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { previousMonth: "previousMonth", nextMonth: "nextMonth", daySelected: "daySelected", yearSelected: "yearSelected", monthSelected: "monthSelected", timeChanged: "timeChanged" }, viewQueries: [{ propertyName: "_dayButton", predicate: ["dayButton"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [ptInt]=\"component()\" [ptClass]=\"'details'\">\n <div [ptInt]=\"component()\" [ptClass]=\"'header'\">\n <div [ptInt]=\"component()\" [ptClass]=\"'navigation'\">\n <button\n ngnButton\n [kind]=\"'icon'\"\n [ptInt]=\"component()\"\n [ptDep]=\"'previous'\"\n [attr.aria-label]=\"i18n['calendar_previousMonth']()\"\n (click)=\"prev()\"\n >\n <ngn-icon [defaultIcon]=\"'calendar-previous-month'\" />\n </button>\n <button\n ngnButton\n [kind]=\"'icon'\"\n [ptInt]=\"component()\"\n [ptDep]=\"'next'\"\n [attr.aria-label]=\"i18n['calendar_nextMonth']()\"\n (click)=\"next()\"\n >\n <ngn-icon [defaultIcon]=\"'calendar-next-month'\" />\n </button>\n </div>\n <ng-container\n *ngTemplateOutlet=\"\n monthTemplate();\n context: {\n $implicit: {\n month: MONTHS[month()],\n change: doMonthChange,\n },\n }\n \"\n />\n\n <ng-container\n *ngTemplateOutlet=\"\n yearTemplate();\n context: {\n $implicit: {\n year: year(),\n change: doYearChange,\n },\n }\n \"\n />\n </div>\n <div class=\"ngn-calendar-days\" role=\"grid\" [attr.aria-label]=\"gridLabel()\">\n <div role=\"row\" style=\"display: contents\">\n @for (weekDay of weekDaysSorted(); track weekDay) {\n <span role=\"columnheader\" [ptInt]=\"component()\" [ptClass]=\"'week-day'\">\n <ng-container\n *ngTemplateOutlet=\"\n weekDayTemplate();\n context: {\n $implicit: {\n weekDay: weekDay.weekDay,\n translation: weekDay.translation(),\n },\n }\n \"\n />\n </span>\n }\n </div>\n @for (item of monthModel().weeks; track $index) {\n <div role=\"row\" style=\"display: contents\">\n @for (day of item.days; track $index) {\n <div\n role=\"gridcell\"\n style=\"display: contents\"\n [attr.aria-selected]=\"isSelected(day) ? 'true' : null\"\n >\n <button\n #dayButton\n [ptInt]=\"component()\"\n [ptClass]=\"{\n day: true,\n 'day-other-month': day.monthOffset !== 0,\n 'day-today': isToday(day),\n 'day-selected': isSelected(day),\n }\"\n [tabIndex]=\"!day.monthOffset && tabFocusDay() === day.date ? 0 : -1\"\n [attr.aria-current]=\"isToday(day) ? 'date' : null\"\n [attr.aria-label]=\"dayLabel(day)\"\n type=\"button\"\n (click)=\"\n daySelected.emit({\n date: day.date,\n monthOffset: day.monthOffset,\n })\n \"\n (keydown)=\"dayKeydown($event, dayButton)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n dayTemplate();\n context: {\n $implicit: {\n date: day.date,\n fromOtherMonth: day.monthOffset !== 0,\n selected: isSelected(day),\n today: isToday(day),\n },\n }\n \"\n />\n </button>\n </div>\n }\n </div>\n }\n </div>\n @if (showTime()) {\n <ng-container\n *ngTemplateOutlet=\"\n timeTemplate();\n context: {\n $implicit: { time: currentValue(), showSeconds: showSeconds(), change: doTimeChange },\n }\n \"\n />\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "directive", type: NgnButton, selector: "button[ngnButton], a[ngnButton]", inputs: ["ngnButtonInline"], exportAs: ["ngnButton"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }] });
|
|
316
|
+
}
|
|
317
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CalendarDays, decorators: [{
|
|
318
|
+
type: Component,
|
|
319
|
+
args: [{ selector: 'ngn-calendar-days', imports: [NgTemplateOutlet, NgnPt, NgnButton, NgnIcon], template: "<div [ptInt]=\"component()\" [ptClass]=\"'details'\">\n <div [ptInt]=\"component()\" [ptClass]=\"'header'\">\n <div [ptInt]=\"component()\" [ptClass]=\"'navigation'\">\n <button\n ngnButton\n [kind]=\"'icon'\"\n [ptInt]=\"component()\"\n [ptDep]=\"'previous'\"\n [attr.aria-label]=\"i18n['calendar_previousMonth']()\"\n (click)=\"prev()\"\n >\n <ngn-icon [defaultIcon]=\"'calendar-previous-month'\" />\n </button>\n <button\n ngnButton\n [kind]=\"'icon'\"\n [ptInt]=\"component()\"\n [ptDep]=\"'next'\"\n [attr.aria-label]=\"i18n['calendar_nextMonth']()\"\n (click)=\"next()\"\n >\n <ngn-icon [defaultIcon]=\"'calendar-next-month'\" />\n </button>\n </div>\n <ng-container\n *ngTemplateOutlet=\"\n monthTemplate();\n context: {\n $implicit: {\n month: MONTHS[month()],\n change: doMonthChange,\n },\n }\n \"\n />\n\n <ng-container\n *ngTemplateOutlet=\"\n yearTemplate();\n context: {\n $implicit: {\n year: year(),\n change: doYearChange,\n },\n }\n \"\n />\n </div>\n <div class=\"ngn-calendar-days\" role=\"grid\" [attr.aria-label]=\"gridLabel()\">\n <div role=\"row\" style=\"display: contents\">\n @for (weekDay of weekDaysSorted(); track weekDay) {\n <span role=\"columnheader\" [ptInt]=\"component()\" [ptClass]=\"'week-day'\">\n <ng-container\n *ngTemplateOutlet=\"\n weekDayTemplate();\n context: {\n $implicit: {\n weekDay: weekDay.weekDay,\n translation: weekDay.translation(),\n },\n }\n \"\n />\n </span>\n }\n </div>\n @for (item of monthModel().weeks; track $index) {\n <div role=\"row\" style=\"display: contents\">\n @for (day of item.days; track $index) {\n <div\n role=\"gridcell\"\n style=\"display: contents\"\n [attr.aria-selected]=\"isSelected(day) ? 'true' : null\"\n >\n <button\n #dayButton\n [ptInt]=\"component()\"\n [ptClass]=\"{\n day: true,\n 'day-other-month': day.monthOffset !== 0,\n 'day-today': isToday(day),\n 'day-selected': isSelected(day),\n }\"\n [tabIndex]=\"!day.monthOffset && tabFocusDay() === day.date ? 0 : -1\"\n [attr.aria-current]=\"isToday(day) ? 'date' : null\"\n [attr.aria-label]=\"dayLabel(day)\"\n type=\"button\"\n (click)=\"\n daySelected.emit({\n date: day.date,\n monthOffset: day.monthOffset,\n })\n \"\n (keydown)=\"dayKeydown($event, dayButton)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n dayTemplate();\n context: {\n $implicit: {\n date: day.date,\n fromOtherMonth: day.monthOffset !== 0,\n selected: isSelected(day),\n today: isToday(day),\n },\n }\n \"\n />\n </button>\n </div>\n }\n </div>\n }\n </div>\n @if (showTime()) {\n <ng-container\n *ngTemplateOutlet=\"\n timeTemplate();\n context: {\n $implicit: { time: currentValue(), showSeconds: showSeconds(), change: doTimeChange },\n }\n \"\n />\n }\n</div>\n" }]
|
|
320
|
+
}], propDecorators: { component: [{ type: i0.Input, args: [{ isSignal: true, alias: "component", required: true }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: true }] }], year: [{ type: i0.Input, args: [{ isSignal: true, alias: "year", required: true }] }], month: [{ type: i0.Input, args: [{ isSignal: true, alias: "month", required: true }] }], currentValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentValue", required: true }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: true }] }], dayTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dayTemplate", required: true }] }], weekDayTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "weekDayTemplate", required: true }] }], timeTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeTemplate", required: true }] }], yearTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "yearTemplate", required: true }] }], monthTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthTemplate", required: true }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: true }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: true }] }], previousMonth: [{ type: i0.Output, args: ["previousMonth"] }], nextMonth: [{ type: i0.Output, args: ["nextMonth"] }], daySelected: [{ type: i0.Output, args: ["daySelected"] }], yearSelected: [{ type: i0.Output, args: ["yearSelected"] }], monthSelected: [{ type: i0.Output, args: ["monthSelected"] }], timeChanged: [{ type: i0.Output, args: ["timeChanged"] }], _dayButton: [{ type: i0.ViewChildren, args: ['dayButton', { isSignal: true }] }] } });
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Formats a Date object into a string based on the provided format.
|
|
324
|
+
* @param value The Date object to format. If null, an empty string is returned.
|
|
325
|
+
* @param format The format string that specifies how the date should be formatted. Supported tokens include:
|
|
326
|
+
* Tokens follow the standard (Unicode/moment-style) convention — uppercase `M` is
|
|
327
|
+
* month, lowercase `m` is minute — matching `getDateOrTimeMask`:
|
|
328
|
+
* - 'dd': Day of the month, 2 digits with leading zeros (01 to 31)
|
|
329
|
+
* - 'd': Day of the month without leading zeros (1 to 31)
|
|
330
|
+
* - 'MM': Month, 2 digits with leading zeros (01 to 12)
|
|
331
|
+
* - 'M': Month without leading zeros (1 to 12)
|
|
332
|
+
* - 'yyyy': Year, 4 digits (e.g., 2024)
|
|
333
|
+
* - 'yy': Year, 2 digits (e.g., 24)
|
|
334
|
+
* - 'HH': Hours (24-hour), 2 digits with leading zeros (00 to 23)
|
|
335
|
+
* - 'H': Hours (24-hour) without leading zeros (0 to 23)
|
|
336
|
+
* - 'hh': Hours (12-hour), 2 digits with leading zeros (01 to 12)
|
|
337
|
+
* - 'h': Hours (12-hour) without leading zeros (1 to 12)
|
|
338
|
+
* - 'mm': Minutes, 2 digits with leading zeros (00 to 59)
|
|
339
|
+
* - 'm': Minutes without leading zeros (0 to 59)
|
|
340
|
+
* - 'ss': Seconds, 2 digits with leading zeros (00 to 59)
|
|
341
|
+
* - 's': Seconds without leading zeros (0 to 59)
|
|
342
|
+
* - 'a': AM/PM marker
|
|
343
|
+
* - 'SSS': Milliseconds, 3 digits with leading zeros (000 to 999)
|
|
344
|
+
* @returns The formatted date string.
|
|
345
|
+
*/
|
|
346
|
+
const DATE_TOKEN = /^(yyyy|yy|MM|M|dd|d|HH|H|hh|h|mm|m|ss|s|SSS|a)/;
|
|
347
|
+
/** Maximum digit count per numeric token — bounds the capture so adjacent
|
|
348
|
+
* numeric tokens (separator-free formats like `MMddyyyy`) still parse. */
|
|
349
|
+
const TOKEN_MAX_DIGITS = {
|
|
350
|
+
yyyy: 4,
|
|
351
|
+
yy: 2,
|
|
352
|
+
MM: 2,
|
|
353
|
+
M: 2,
|
|
354
|
+
dd: 2,
|
|
355
|
+
d: 2,
|
|
356
|
+
HH: 2,
|
|
357
|
+
H: 2,
|
|
358
|
+
hh: 2,
|
|
359
|
+
h: 2,
|
|
360
|
+
mm: 2,
|
|
361
|
+
m: 2,
|
|
362
|
+
ss: 2,
|
|
363
|
+
s: 2,
|
|
364
|
+
SSS: 3,
|
|
365
|
+
};
|
|
366
|
+
/**
|
|
367
|
+
* Parses a date string according to `format`, the inverse of {@link formatDate}.
|
|
368
|
+
*
|
|
369
|
+
* Unlike `new Date(string)` this is format-aware (so e.g. `dd.MM.yyyy` parses
|
|
370
|
+
* correctly) and, crucially, **clamps the day to the number of days in the
|
|
371
|
+
* resolved month/year** instead of rolling over: `31` in February becomes the
|
|
372
|
+
* 28th/29th rather than spilling into March. Returns `null` when the string does
|
|
373
|
+
* not match the format.
|
|
374
|
+
*
|
|
375
|
+
* Two-digit years (`yy`) are interpreted as 2000–2099. Numeric tokens are capped
|
|
376
|
+
* at their natural digit count so separator-free formats parse unambiguously.
|
|
377
|
+
*/
|
|
378
|
+
function parseDate(str, format) {
|
|
379
|
+
// Build a regex from the format, remembering the token captured at each group.
|
|
380
|
+
let pattern = '';
|
|
381
|
+
const tokens = [];
|
|
382
|
+
let i = 0;
|
|
383
|
+
while (i < format.length) {
|
|
384
|
+
const m = DATE_TOKEN.exec(format.slice(i));
|
|
385
|
+
if (m) {
|
|
386
|
+
const tok = m[0]; // the whole match is the token
|
|
387
|
+
tokens.push(tok);
|
|
388
|
+
pattern += tok === 'a' ? '(AM|PM)' : `(\\d{1,${TOKEN_MAX_DIGITS[tok]}})`;
|
|
389
|
+
i += tok.length;
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
pattern += format.charAt(i).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
393
|
+
i++;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const match = new RegExp(`^${pattern}$`).exec(str);
|
|
397
|
+
if (!match)
|
|
398
|
+
return null;
|
|
399
|
+
const part = {};
|
|
400
|
+
tokens.forEach((tok, idx) => (part[tok] = match[idx + 1] ?? ''));
|
|
401
|
+
const num = (...keys) => {
|
|
402
|
+
for (const k of keys)
|
|
403
|
+
if (part[k] !== undefined)
|
|
404
|
+
return Number(part[k]);
|
|
405
|
+
return undefined;
|
|
406
|
+
};
|
|
407
|
+
const now = new Date();
|
|
408
|
+
const year = num('yyyy') ?? (part['yy'] !== undefined ? 2000 + Number(part['yy']) : now.getFullYear());
|
|
409
|
+
const month = num('MM', 'M') ?? 1; // 1-12
|
|
410
|
+
const dayRaw = num('dd', 'd') ?? 1;
|
|
411
|
+
let hours = num('HH', 'H') ?? 0;
|
|
412
|
+
const hour12 = num('hh', 'h');
|
|
413
|
+
if (hour12 !== undefined) {
|
|
414
|
+
if (hour12 < 1 || hour12 > 12)
|
|
415
|
+
return null;
|
|
416
|
+
const pm = part['a'] === 'PM';
|
|
417
|
+
hours = (hour12 % 12) + (pm ? 12 : 0);
|
|
418
|
+
}
|
|
419
|
+
const minutes = num('mm', 'm') ?? 0;
|
|
420
|
+
const seconds = num('ss', 's') ?? 0;
|
|
421
|
+
const ms = num('SSS') ?? 0;
|
|
422
|
+
// Reject out-of-range components rather than letting `new Date` silently
|
|
423
|
+
// normalize them (e.g. month 13 → next year, hour 25 → next day).
|
|
424
|
+
if (month < 1 ||
|
|
425
|
+
month > 12 ||
|
|
426
|
+
hours < 0 ||
|
|
427
|
+
hours > 23 ||
|
|
428
|
+
minutes < 0 ||
|
|
429
|
+
minutes > 59 ||
|
|
430
|
+
seconds < 0 ||
|
|
431
|
+
seconds > 59 ||
|
|
432
|
+
ms < 0 ||
|
|
433
|
+
ms > 999) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
// Clamp the day to the resolved month's length (handles leap years) so an
|
|
437
|
+
// out-of-range day never rolls over into the next month.
|
|
438
|
+
const daysInMonth = new Date(year, month, 0).getDate();
|
|
439
|
+
const day = Math.max(1, Math.min(dayRaw, daysInMonth));
|
|
440
|
+
const date = new Date(year, month - 1, day, hours, minutes, seconds, ms);
|
|
441
|
+
return isNaN(date.getTime()) ? null : date;
|
|
442
|
+
}
|
|
443
|
+
function formatDate(value, format) {
|
|
444
|
+
if (!value) {
|
|
445
|
+
return '';
|
|
446
|
+
}
|
|
447
|
+
const day = value.getDate();
|
|
448
|
+
const month = value.getMonth() + 1;
|
|
449
|
+
const year = value.getFullYear();
|
|
450
|
+
const hours = value.getHours();
|
|
451
|
+
const minutes = value.getMinutes();
|
|
452
|
+
const seconds = value.getSeconds();
|
|
453
|
+
const milliseconds = value.getMilliseconds();
|
|
454
|
+
const ampm = hours >= 12 ? 'PM' : 'AM';
|
|
455
|
+
const hour12 = hours % 12 || 12;
|
|
456
|
+
// Longest tokens first within each group; 'a' (AM/PM) last so the inserted
|
|
457
|
+
// letters are never re-matched by an earlier numeric replacement.
|
|
458
|
+
return format
|
|
459
|
+
.replace(/yyyy/g, String(year))
|
|
460
|
+
.replace(/yy/g, String(year).slice(-2))
|
|
461
|
+
.replace(/dd/g, String(day).padStart(2, '0'))
|
|
462
|
+
.replace(/d/g, String(day))
|
|
463
|
+
.replace(/MM/g, String(month).padStart(2, '0'))
|
|
464
|
+
.replace(/M/g, String(month))
|
|
465
|
+
.replace(/HH/g, String(hours).padStart(2, '0'))
|
|
466
|
+
.replace(/H/g, String(hours))
|
|
467
|
+
.replace(/hh/g, String(hour12).padStart(2, '0'))
|
|
468
|
+
.replace(/h/g, String(hour12))
|
|
469
|
+
.replace(/mm/g, String(minutes).padStart(2, '0'))
|
|
470
|
+
.replace(/m/g, String(minutes))
|
|
471
|
+
.replace(/ss/g, String(seconds).padStart(2, '0'))
|
|
472
|
+
.replace(/s/g, String(seconds))
|
|
473
|
+
.replace(/SSS/g, String(milliseconds).padStart(3, '0'))
|
|
474
|
+
.replace(/a/g, ampm);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function generateYearOptions() {
|
|
478
|
+
const MAX_ITEMS = 200;
|
|
479
|
+
const currentYear = new Date().getFullYear();
|
|
480
|
+
return Array.from({ length: MAX_ITEMS }, (_, i) => ({
|
|
481
|
+
label: (currentYear - MAX_ITEMS / 2 + i).toString(),
|
|
482
|
+
value: currentYear - MAX_ITEMS / 2 + i,
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* @category control
|
|
487
|
+
*/
|
|
488
|
+
class NgnCalendar extends CalendarTemplates {
|
|
489
|
+
/**
|
|
490
|
+
* Set the first day of the week.
|
|
491
|
+
* @default 'monday'
|
|
492
|
+
*/
|
|
493
|
+
firstDayOfWeek = input('monday', /* @ts-ignore */
|
|
494
|
+
...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : /* istanbul ignore next */ []));
|
|
495
|
+
/**
|
|
496
|
+
* Whether to show the time input.
|
|
497
|
+
* @default false
|
|
498
|
+
*/
|
|
499
|
+
showTime = input(false, { ...(ngDevMode ? { debugName: "showTime" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
500
|
+
/**
|
|
501
|
+
* Whether to show seconds in the time input.
|
|
502
|
+
* @default false
|
|
503
|
+
*/
|
|
504
|
+
showSeconds = input(false, { ...(ngDevMode ? { debugName: "showSeconds" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
505
|
+
/**
|
|
506
|
+
* Whether to show the calendar inline instead of with a input field & popup.
|
|
507
|
+
* @default false
|
|
508
|
+
*/
|
|
509
|
+
inline = input(false, { ...(ngDevMode ? { debugName: "inline" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
510
|
+
/**
|
|
511
|
+
* The format of the date displayed in the input field.
|
|
512
|
+
* @default 'MM/dd/yyyy'
|
|
513
|
+
*/
|
|
514
|
+
format = input('MM/dd/yyyy', /* @ts-ignore */
|
|
515
|
+
...(ngDevMode ? [{ debugName: "format" }] : /* istanbul ignore next */ []));
|
|
516
|
+
_popover = viewChild(NgnPopover, /* @ts-ignore */
|
|
517
|
+
...(ngDevMode ? [{ debugName: "_popover" }] : /* istanbul ignore next */ []));
|
|
518
|
+
_mask = viewChild(NgnMaskInput, /* @ts-ignore */
|
|
519
|
+
...(ngDevMode ? [{ debugName: "_mask" }] : /* istanbul ignore next */ []));
|
|
520
|
+
_platform = inject(Platform);
|
|
521
|
+
i18n = inject(I18n).translations;
|
|
522
|
+
isFieldControl = true;
|
|
523
|
+
theme = this.injectThemeTemplate(calendarControlTemplate);
|
|
524
|
+
get anchorElement() {
|
|
525
|
+
return (this.element.nativeElement.closest('ngn-input-field') ??
|
|
526
|
+
this.element.nativeElement);
|
|
527
|
+
}
|
|
528
|
+
year = linkedSignal(() => this.value()?.getFullYear() || new Date().getFullYear(), /* @ts-ignore */
|
|
529
|
+
...(ngDevMode ? [{ debugName: "year" }] : /* istanbul ignore next */ []));
|
|
530
|
+
month = linkedSignal(() => this.value()?.getMonth() ?? new Date().getMonth(), /* @ts-ignore */
|
|
531
|
+
...(ngDevMode ? [{ debugName: "month" }] : /* istanbul ignore next */ []));
|
|
532
|
+
yearOptions = generateYearOptions();
|
|
533
|
+
monthOptions = computed(() => {
|
|
534
|
+
const months = Array.from({ length: 12 }, (_, i) => ({
|
|
535
|
+
label: this.i18n[`calendar_months_${MONTHS[i] ?? throwExp('calendar', 'invalid month index')}`],
|
|
536
|
+
value: MONTHS[i],
|
|
537
|
+
testId: `calendar-month-${MONTHS[i]}`,
|
|
538
|
+
}));
|
|
539
|
+
return months;
|
|
540
|
+
}, /* @ts-ignore */
|
|
541
|
+
...(ngDevMode ? [{ debugName: "monthOptions" }] : /* istanbul ignore next */ []));
|
|
542
|
+
valueStr = computed(() => {
|
|
543
|
+
const value = this.value();
|
|
544
|
+
const format = this.format();
|
|
545
|
+
if (!value) {
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
return formatDate(value, format);
|
|
549
|
+
}, /* @ts-ignore */
|
|
550
|
+
...(ngDevMode ? [{ debugName: "valueStr" }] : /* istanbul ignore next */ []));
|
|
551
|
+
maskCfg = computed(() => {
|
|
552
|
+
const format = this.format();
|
|
553
|
+
const mask = getDateOrTimeMask(format);
|
|
554
|
+
const value = this.value();
|
|
555
|
+
if (!value) {
|
|
556
|
+
return mask;
|
|
557
|
+
}
|
|
558
|
+
// Narrow the day field's max to the current month's length (leap-aware) so
|
|
559
|
+
// the mask wraps/limits the day correctly within the month (e.g. Feb caps at
|
|
560
|
+
// 28/29, stepping up from 28 wraps to 1). The mask keeps the typed value
|
|
561
|
+
// across this change because only min/max — not the structure — differs.
|
|
562
|
+
const daysInMonth = new Date(value.getFullYear(), value.getMonth() + 1, 0).getDate();
|
|
563
|
+
return mask.map(seg => typeof seg !== 'string' && seg.kind === 'number' && seg.segment === 'day'
|
|
564
|
+
? { ...seg, max: daysInMonth }
|
|
565
|
+
: seg);
|
|
566
|
+
}, /* @ts-ignore */
|
|
567
|
+
...(ngDevMode ? [{ debugName: "maskCfg" }] : /* istanbul ignore next */ []));
|
|
568
|
+
previousMonth = () => {
|
|
569
|
+
const currentMonth = this.month();
|
|
570
|
+
const currentYear = this.year();
|
|
571
|
+
if (currentMonth === 0) {
|
|
572
|
+
this.year.set(currentYear - 1);
|
|
573
|
+
this.month.set(11); // December
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
this.month.set(currentMonth - 1);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
nextMonth = () => {
|
|
580
|
+
const currentMonth = this.month();
|
|
581
|
+
const currentYear = this.year();
|
|
582
|
+
if (currentMonth === 11) {
|
|
583
|
+
this.year.set(currentYear + 1);
|
|
584
|
+
this.month.set(0); // January
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
this.month.set(currentMonth + 1);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
previousYear = () => {
|
|
591
|
+
this.year.set(this.year() - 1);
|
|
592
|
+
};
|
|
593
|
+
nextYear = () => {
|
|
594
|
+
this.year.set(this.year() + 1);
|
|
595
|
+
};
|
|
596
|
+
selectMonth(month) {
|
|
597
|
+
this.month.set(MONTHS.indexOf(month));
|
|
598
|
+
}
|
|
599
|
+
selectYear(year) {
|
|
600
|
+
this.year.set(year);
|
|
601
|
+
}
|
|
602
|
+
selectDay(day) {
|
|
603
|
+
const newValue = new Date(this.value() || new Date());
|
|
604
|
+
newValue.setFullYear(this.year());
|
|
605
|
+
newValue.setMonth(this.month() + day.monthOffset);
|
|
606
|
+
newValue.setDate(day.date);
|
|
607
|
+
this.value.set(newValue);
|
|
608
|
+
}
|
|
609
|
+
changeTime(newTime) {
|
|
610
|
+
if (!newTime) {
|
|
611
|
+
this.value.set(null);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const currentValue = this.value();
|
|
615
|
+
if (currentValue) {
|
|
616
|
+
const newValue = new Date(currentValue);
|
|
617
|
+
newValue.setHours(newTime.getHours());
|
|
618
|
+
newValue.setMinutes(newTime.getMinutes());
|
|
619
|
+
newValue.setSeconds(newTime.getSeconds());
|
|
620
|
+
this.value.set(newValue);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Places focus from a pointer event: forwards the location to the embedded
|
|
625
|
+
* mask so the section nearest the cursor is selected, then opens the popup.
|
|
626
|
+
* Implemented as the `NgnBase.focusFromPointer` hook so it also works when the
|
|
627
|
+
* calendar is wrapped in an `ngn-input-field` — the outer field delegates the
|
|
628
|
+
* click here (with the real coordinates) instead of synthesising a
|
|
629
|
+
* coordinate-less click. Invoked by the calendar's own field click too.
|
|
630
|
+
*/
|
|
631
|
+
focusFromPointer(event) {
|
|
632
|
+
const handled = this._mask()?.focusFromPointer(event) ?? false;
|
|
633
|
+
if (!this.inline()) {
|
|
634
|
+
this.show();
|
|
635
|
+
}
|
|
636
|
+
return handled;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Shows the calendar popup. Only works if `inline` is `false`.
|
|
640
|
+
*/
|
|
641
|
+
show() {
|
|
642
|
+
if (this.inline()) {
|
|
643
|
+
throw new NgnError('calendar', 'cannot open inline calendar');
|
|
644
|
+
}
|
|
645
|
+
if (this._platform.isTouchDevice() || this.disabled() || this.readonly()) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const popover = this._popover();
|
|
649
|
+
if (!popover) {
|
|
650
|
+
throw new NgnError('calendar', 'popover not found despite inline being false');
|
|
651
|
+
}
|
|
652
|
+
popover.show();
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Hides the calendar popup. Only works if `inline` is `false`.
|
|
656
|
+
*/
|
|
657
|
+
hide() {
|
|
658
|
+
if (this.inline()) {
|
|
659
|
+
throw new NgnError('calendar', 'cannot close inline calendar');
|
|
660
|
+
}
|
|
661
|
+
const popover = this._popover();
|
|
662
|
+
if (!popover) {
|
|
663
|
+
throw new NgnError('calendar', 'popover not found despite inline being false');
|
|
664
|
+
}
|
|
665
|
+
popover.hide();
|
|
666
|
+
}
|
|
667
|
+
onKeyDown(event) {
|
|
668
|
+
// @todo: handle date selection with keys
|
|
669
|
+
// if event is not handled by the listbox, we can handle it here
|
|
670
|
+
if (event.key === 'Enter' && !this.inline()) {
|
|
671
|
+
const popover = this._popover();
|
|
672
|
+
if (!popover) {
|
|
673
|
+
throw new NgnError('calendar', 'popover not found despite inline being false');
|
|
674
|
+
}
|
|
675
|
+
popover.toggle();
|
|
676
|
+
event.stopPropagation();
|
|
677
|
+
event.preventDefault();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
/** The current (possibly partial) text in the mask field, awaiting blur. */
|
|
681
|
+
_inputString = signal(null, /* @ts-ignore */
|
|
682
|
+
...(ngDevMode ? [{ debugName: "_inputString" }] : /* istanbul ignore next */ []));
|
|
683
|
+
_clearingMaskInput = signal(false, /* @ts-ignore */
|
|
684
|
+
...(ngDevMode ? [{ debugName: "_clearingMaskInput" }] : /* istanbul ignore next */ []));
|
|
685
|
+
_clearValueWhenMaskBecomesEmpty = effect(() => {
|
|
686
|
+
if (!this._clearingMaskInput() || !this._mask()?.empty()) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
this._clearingMaskInput.set(false);
|
|
690
|
+
this.value.set(null);
|
|
691
|
+
}, /* @ts-ignore */
|
|
692
|
+
...(ngDevMode ? [{ debugName: "_clearValueWhenMaskBecomesEmpty" }] : /* istanbul ignore next */ []));
|
|
693
|
+
stringValueChange(newValue) {
|
|
694
|
+
this._inputString.set(newValue);
|
|
695
|
+
// The mask emits null both while mid-typing and when fully cleared. Only the
|
|
696
|
+
// fully-cleared case should null out the bound value; a partially-typed entry
|
|
697
|
+
// leaves the previous value intact (one-directional sync).
|
|
698
|
+
if (!newValue) {
|
|
699
|
+
if (this._mask()?.empty()) {
|
|
700
|
+
this._clearingMaskInput.set(false);
|
|
701
|
+
this.value.set(null);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
this._clearingMaskInput.set(true);
|
|
705
|
+
}
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
this._clearingMaskInput.set(false);
|
|
709
|
+
// One-directional sync: typing updates the calendar value (and thus the
|
|
710
|
+
// dropdown UI) only once the mask is COMPLETE — a fully filled, parseable
|
|
711
|
+
// date. Half-typed input never moves the calendar and is never reformatted
|
|
712
|
+
// back over the input. The calendar UI updates the value directly via
|
|
713
|
+
// selectDay / selectMonth / selectYear / changeTime (which then flow back
|
|
714
|
+
// out to the mask text via `valueStr`).
|
|
715
|
+
if (this._mask()?.complete()) {
|
|
716
|
+
this.parseInputString();
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
/** Parse the complete mask string into the calendar value. */
|
|
720
|
+
parseInputString() {
|
|
721
|
+
const raw = this._inputString();
|
|
722
|
+
if (!raw) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
// Format-aware parse that clamps an out-of-range day to the month's length
|
|
726
|
+
// (e.g. Feb 31 → Feb 28) instead of rolling over into the next month.
|
|
727
|
+
const date = parseDate(raw, this.format());
|
|
728
|
+
if (!date) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
if (!this.showTime()) {
|
|
732
|
+
date.setHours(this.value()?.getHours() ?? 0);
|
|
733
|
+
date.setMinutes(this.value()?.getMinutes() ?? 0);
|
|
734
|
+
date.setSeconds(this.value()?.getSeconds() ?? 0);
|
|
735
|
+
}
|
|
736
|
+
else if (!this.showSeconds()) {
|
|
737
|
+
date.setSeconds(this.value()?.getSeconds() ?? 0);
|
|
738
|
+
}
|
|
739
|
+
// Skip if unchanged — prevents an echo loop when our own valueStr→input
|
|
740
|
+
// write re-emits a (complete) valueChange for the value we just set.
|
|
741
|
+
const current = this.value();
|
|
742
|
+
if (current && current.getTime() === date.getTime()) {
|
|
743
|
+
// The value is unchanged, but the mask may be showing a non-canonical
|
|
744
|
+
// string (e.g. a clamped Feb 31 → Feb 28). Push the canonical text back so
|
|
745
|
+
// the displayed sections match the real value.
|
|
746
|
+
const canonical = formatDate(date, this.format());
|
|
747
|
+
if (canonical !== raw) {
|
|
748
|
+
this._mask()?.value.set(canonical);
|
|
749
|
+
}
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
this.value.set(date);
|
|
753
|
+
}
|
|
754
|
+
potentiallyBlurred() {
|
|
755
|
+
setTimeout(() => {
|
|
756
|
+
if (this.element.nativeElement.contains(document.activeElement) || this._popover()?.open()) {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
this.touched.set(true);
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnCalendar, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
763
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnCalendar, isStandalone: true, selector: "ngn-calendar", inputs: { firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, showTime: { classPropertyName: "showTime", publicName: "showTime", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, inline: { classPropertyName: "inline", publicName: "inline", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.display": "\"block\"", "style.width": "inline() ? \"fit-content\" : \"100%\"", "attr.aria-invalid": "invalid() ? \"true\" : null" } }, providers: [provideSelf(NgnCalendar)], viewQueries: [{ propertyName: "_popover", first: true, predicate: NgnPopover, descendants: true, isSignal: true }, { propertyName: "_mask", first: true, predicate: NgnMaskInput, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@if (inline()) {\n <ng-container *ngTemplateOutlet=\"calendar\"></ng-container>\n} @else {\n <div\n #field\n [ptInt]=\"this\"\n [ptClass]=\"{ 'input-field': true, invalid: invalid() }\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n (click)=\"focusFromPointer($event)\"\n (keydown)=\"onKeyDown($event)\"\n (focusout)=\"potentiallyBlurred()\"\n [tabindex]=\"disabled() ? -1 : 0\"\n >\n <ngn-mask-input\n [ptInt]=\"this\"\n [ptDep]=\"'input'\"\n [label]=\"i18n['calendar_input']()\"\n [mask]=\"maskCfg()\"\n [invalid]=\"invalid()\"\n [value]=\"valueStr()\"\n (valueChange)=\"stringValueChange($event)\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n />\n <ngn-icon defaultIcon=\"calendar-trigger\" [ptInt]=\"this\" [ptDep]=\"'trigger-icon'\" />\n </div>\n <ngn-popover\n #popover\n [ptInt]=\"this\"\n [ptDep]=\"'popover'\"\n [anchor]=\"anchorElement\"\n [id]=\"inputId() + '_popover'\"\n (closing)=\"potentiallyBlurred()\"\n >\n <ng-template #lazy>\n <ng-container *ngTemplateOutlet=\"calendar\"></ng-container>\n </ng-template>\n </ngn-popover>\n}\n\n<ng-template #calendar>\n <div\n [ptInt]=\"this\"\n [ptClass]=\"{\n root: true,\n inline: inline(),\n }\"\n >\n <ngn-calendar-days\n [component]=\"this\"\n [theme]=\"theme\"\n [year]=\"year()\"\n [month]=\"month()\"\n [currentValue]=\"value()\"\n [firstDayOfWeek]=\"firstDayOfWeek()\"\n (previousMonth)=\"previousMonth()\"\n (nextMonth)=\"nextMonth()\"\n (daySelected)=\"selectDay($event)\"\n (monthSelected)=\"selectMonth($event)\"\n (yearSelected)=\"selectYear($event)\"\n (timeChanged)=\"changeTime($event)\"\n [dayTemplate]=\"dayTemplate()\"\n [weekDayTemplate]=\"weekDayTemplate()\"\n [timeTemplate]=\"timeTemplate()\"\n [yearTemplate]=\"yearTemplate()\"\n [monthTemplate]=\"monthTemplate()\"\n [showTime]=\"showTime()\"\n [showSeconds]=\"showSeconds()\"\n (focusout)=\"potentiallyBlurred()\"\n />\n </div>\n</ng-template>\n<ng-template #defaultDayTemplate [ngnTemplate]=\"templateTypes.day\" let-day>\n {{ day.date }}\n</ng-template>\n\n<ng-template #defaultWeekDayTemplate [ngnTemplate]=\"templateTypes.weekDay\" let-weekDay>\n <span>\n {{ weekDay.translation }}\n </span>\n</ng-template>\n\n<ng-template #defaultTimeTemplate [ngnTemplate]=\"templateTypes.time\" let-time>\n @defer {\n <div [ptInt]=\"this\" [ptClass]=\"'time'\">\n <ngn-calendar-time\n [component]=\"this\"\n [currentValue]=\"time.time\"\n [showSeconds]=\"time.showSeconds\"\n (timeChange)=\"time.change($event)\"\n />\n </div>\n }\n</ng-template>\n\n<ng-template #defaultYearTemplate [ngnTemplate]=\"templateTypes.year\" let-y>\n @defer {\n <ngn-input-field [ptInt]=\"this\" [ptDep]=\"'current-year-field'\">\n <ngn-select\n [ptInt]=\"this\"\n [ptDep]=\"'current-year'\"\n [editable]=\"true\"\n [value]=\"year() + ''\"\n (valueChange)=\"y.change(+($event || 0))\"\n [options]=\"yearOptions\"\n [virtual]=\"true\"\n [itemHeight]=\"40\"\n [popoverOptions]=\"{ sizeConstraints: { maxHeight: '300px' } }\"\n [scrollToSelectedItemOnOpen]=\"'center'\"\n [editableAutoFilter]=\"false\"\n [tabindex]=\"-1\"\n >\n <input ngnInput type=\"number\" [attr.aria-label]=\"i18n['calendar_selectYear']()\" />\n </ngn-select>\n </ngn-input-field>\n }\n</ng-template>\n\n<ng-template #defaultMonthTemplate [ngnTemplate]=\"templateTypes.month\" let-m>\n @defer {\n <ngn-input-field [ptInt]=\"this\" [ptDep]=\"'current-month-field'\">\n <ngn-select\n [ptInt]=\"this\"\n [ptDep]=\"'current-month'\"\n [label]=\"i18n['calendar_selectMonth']()\"\n [value]=\"m.month\"\n (valueChange)=\"m.change($event!)\"\n [options]=\"monthOptions()\"\n [popoverOptions]=\"{\n sizeConstraints: { maxHeight: '300px', width: '130px', maxWidth: '130px' },\n }\"\n [scrollToSelectedItemOnOpen]=\"'center'\"\n [tabindex]=\"-1\"\n />\n </ngn-input-field>\n }\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgnTemplate, selector: "ng-template[ngnTemplate]", inputs: ["ngnTemplate"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }, { kind: "component", type: NgnPopover, selector: "ngn-popover", inputs: ["open", "anchor", "options", "hasShrinkableContent", "closeBy"], outputs: ["closed", "closing", "openChange"] }, { kind: "component", type: CalendarDays, selector: "ngn-calendar-days", inputs: ["component", "theme", "year", "month", "currentValue", "firstDayOfWeek", "dayTemplate", "weekDayTemplate", "timeTemplate", "yearTemplate", "monthTemplate", "showTime", "showSeconds"], outputs: ["previousMonth", "nextMonth", "daySelected", "yearSelected", "monthSelected", "timeChanged"] }, { kind: "component", type: NgnMaskInput, selector: "ngn-mask-input", inputs: ["mask"] }], deferBlockDependencies: [() => [NgnPt, /* @ts-ignore */
|
|
764
|
+
import('./ngneers-controls-calendar-time-CJT5AFlA.mjs').then(m => m.CalendarTime)], () => [NgnPt, /* @ts-ignore */
|
|
765
|
+
import('@ngneers/controls/input').then(m => m.NgnInput), /* @ts-ignore */
|
|
766
|
+
import('@ngneers/controls/select').then(m => m.NgnSelect), /* @ts-ignore */
|
|
767
|
+
import('@ngneers/controls/input-field').then(m => m.NgnInputField)], () => [NgnPt, /* @ts-ignore */
|
|
768
|
+
import('@ngneers/controls/select').then(m => m.NgnSelect), /* @ts-ignore */
|
|
769
|
+
import('@ngneers/controls/input-field').then(m => m.NgnInputField)]] });
|
|
770
|
+
}
|
|
771
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.5", ngImport: i0, type: NgnCalendar, resolveDeferredDeps: () => [/* @ts-ignore */
|
|
772
|
+
import('./ngneers-controls-calendar-time-CJT5AFlA.mjs').then(m => m.CalendarTime), /* @ts-ignore */
|
|
773
|
+
import('@ngneers/controls/input').then(m => m.NgnInput), /* @ts-ignore */
|
|
774
|
+
import('@ngneers/controls/select').then(m => m.NgnSelect), /* @ts-ignore */
|
|
775
|
+
import('@ngneers/controls/input-field').then(m => m.NgnInputField)], resolveMetadata: (CalendarTime, NgnInput, NgnSelect, NgnInputField) => ({ decorators: [{
|
|
776
|
+
type: Component,
|
|
777
|
+
args: [{ selector: 'ngn-calendar', imports: [
|
|
778
|
+
NgTemplateOutlet,
|
|
779
|
+
NgnTemplate,
|
|
780
|
+
NgnPt,
|
|
781
|
+
NgnInput,
|
|
782
|
+
NgnIcon,
|
|
783
|
+
NgnSelect,
|
|
784
|
+
NgnPopover,
|
|
785
|
+
CalendarDays,
|
|
786
|
+
CalendarTime,
|
|
787
|
+
NgnInputField,
|
|
788
|
+
NgnMaskInput,
|
|
789
|
+
], providers: [provideSelf(NgnCalendar)], host: {
|
|
790
|
+
'[style.display]': '"block"',
|
|
791
|
+
'[style.width]': 'inline() ? "fit-content" : "100%"',
|
|
792
|
+
'[attr.aria-invalid]': 'invalid() ? "true" : null',
|
|
793
|
+
}, template: "@if (inline()) {\n <ng-container *ngTemplateOutlet=\"calendar\"></ng-container>\n} @else {\n <div\n #field\n [ptInt]=\"this\"\n [ptClass]=\"{ 'input-field': true, invalid: invalid() }\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n (click)=\"focusFromPointer($event)\"\n (keydown)=\"onKeyDown($event)\"\n (focusout)=\"potentiallyBlurred()\"\n [tabindex]=\"disabled() ? -1 : 0\"\n >\n <ngn-mask-input\n [ptInt]=\"this\"\n [ptDep]=\"'input'\"\n [label]=\"i18n['calendar_input']()\"\n [mask]=\"maskCfg()\"\n [invalid]=\"invalid()\"\n [value]=\"valueStr()\"\n (valueChange)=\"stringValueChange($event)\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n />\n <ngn-icon defaultIcon=\"calendar-trigger\" [ptInt]=\"this\" [ptDep]=\"'trigger-icon'\" />\n </div>\n <ngn-popover\n #popover\n [ptInt]=\"this\"\n [ptDep]=\"'popover'\"\n [anchor]=\"anchorElement\"\n [id]=\"inputId() + '_popover'\"\n (closing)=\"potentiallyBlurred()\"\n >\n <ng-template #lazy>\n <ng-container *ngTemplateOutlet=\"calendar\"></ng-container>\n </ng-template>\n </ngn-popover>\n}\n\n<ng-template #calendar>\n <div\n [ptInt]=\"this\"\n [ptClass]=\"{\n root: true,\n inline: inline(),\n }\"\n >\n <ngn-calendar-days\n [component]=\"this\"\n [theme]=\"theme\"\n [year]=\"year()\"\n [month]=\"month()\"\n [currentValue]=\"value()\"\n [firstDayOfWeek]=\"firstDayOfWeek()\"\n (previousMonth)=\"previousMonth()\"\n (nextMonth)=\"nextMonth()\"\n (daySelected)=\"selectDay($event)\"\n (monthSelected)=\"selectMonth($event)\"\n (yearSelected)=\"selectYear($event)\"\n (timeChanged)=\"changeTime($event)\"\n [dayTemplate]=\"dayTemplate()\"\n [weekDayTemplate]=\"weekDayTemplate()\"\n [timeTemplate]=\"timeTemplate()\"\n [yearTemplate]=\"yearTemplate()\"\n [monthTemplate]=\"monthTemplate()\"\n [showTime]=\"showTime()\"\n [showSeconds]=\"showSeconds()\"\n (focusout)=\"potentiallyBlurred()\"\n />\n </div>\n</ng-template>\n<ng-template #defaultDayTemplate [ngnTemplate]=\"templateTypes.day\" let-day>\n {{ day.date }}\n</ng-template>\n\n<ng-template #defaultWeekDayTemplate [ngnTemplate]=\"templateTypes.weekDay\" let-weekDay>\n <span>\n {{ weekDay.translation }}\n </span>\n</ng-template>\n\n<ng-template #defaultTimeTemplate [ngnTemplate]=\"templateTypes.time\" let-time>\n @defer {\n <div [ptInt]=\"this\" [ptClass]=\"'time'\">\n <ngn-calendar-time\n [component]=\"this\"\n [currentValue]=\"time.time\"\n [showSeconds]=\"time.showSeconds\"\n (timeChange)=\"time.change($event)\"\n />\n </div>\n }\n</ng-template>\n\n<ng-template #defaultYearTemplate [ngnTemplate]=\"templateTypes.year\" let-y>\n @defer {\n <ngn-input-field [ptInt]=\"this\" [ptDep]=\"'current-year-field'\">\n <ngn-select\n [ptInt]=\"this\"\n [ptDep]=\"'current-year'\"\n [editable]=\"true\"\n [value]=\"year() + ''\"\n (valueChange)=\"y.change(+($event || 0))\"\n [options]=\"yearOptions\"\n [virtual]=\"true\"\n [itemHeight]=\"40\"\n [popoverOptions]=\"{ sizeConstraints: { maxHeight: '300px' } }\"\n [scrollToSelectedItemOnOpen]=\"'center'\"\n [editableAutoFilter]=\"false\"\n [tabindex]=\"-1\"\n >\n <input ngnInput type=\"number\" [attr.aria-label]=\"i18n['calendar_selectYear']()\" />\n </ngn-select>\n </ngn-input-field>\n }\n</ng-template>\n\n<ng-template #defaultMonthTemplate [ngnTemplate]=\"templateTypes.month\" let-m>\n @defer {\n <ngn-input-field [ptInt]=\"this\" [ptDep]=\"'current-month-field'\">\n <ngn-select\n [ptInt]=\"this\"\n [ptDep]=\"'current-month'\"\n [label]=\"i18n['calendar_selectMonth']()\"\n [value]=\"m.month\"\n (valueChange)=\"m.change($event!)\"\n [options]=\"monthOptions()\"\n [popoverOptions]=\"{\n sizeConstraints: { maxHeight: '300px', width: '130px', maxWidth: '130px' },\n }\"\n [scrollToSelectedItemOnOpen]=\"'center'\"\n [tabindex]=\"-1\"\n />\n </ngn-input-field>\n }\n</ng-template>\n" }]
|
|
794
|
+
}], ctorParameters: null, propDecorators: { firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], _popover: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgnPopover), { isSignal: true }] }], _mask: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgnMaskInput), { isSignal: true }] }] } }) });
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Generated bundle index. Do not edit.
|
|
798
|
+
*/
|
|
799
|
+
|
|
800
|
+
export { MONTHS, NgnCalendar, WEEK_DAYS, formatDate, parseDate };
|
|
801
|
+
//# sourceMappingURL=ngneers-controls-calendar.mjs.map
|