quang 20.5.1 → 20.6.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.
@@ -1,6 +1,6 @@
1
1
  import { NgClass } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { input, viewChild, computed, signal, inject, effect, forwardRef, Optional, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { inject, NgZone, ChangeDetectorRef, ApplicationRef, input, viewChild, computed, signal, effect, forwardRef, ChangeDetectionStrategy, Component } from '@angular/core';
4
4
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
5
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
6
  import { TranslocoPipe } from '@jsverse/transloco';
@@ -29,6 +29,10 @@ import { QuangBaseComponent } from 'quang/components/shared';
29
29
  class QuangDateComponent extends QuangBaseComponent {
30
30
  constructor() {
31
31
  super();
32
+ this._ngZone = inject(NgZone);
33
+ this._cdr = inject(ChangeDetectorRef);
34
+ this._appRef = inject(ApplicationRef);
35
+ this._tickScheduled = false;
32
36
  /**
33
37
  * Format to use to show on the input field.
34
38
  * The format is based on the standard {@link https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table}
@@ -71,7 +75,7 @@ class QuangDateComponent extends QuangBaseComponent {
71
75
  this._inputForDate = viewChild('inputForDate', ...(ngDevMode ? [{ debugName: "_inputForDate" }] : []));
72
76
  this.contentTemplate = viewChild.required('calendarButton');
73
77
  this.hasNoContent = computed(() => this.contentTemplate()?.nativeElement.children.length === 0, ...(ngDevMode ? [{ debugName: "hasNoContent" }] : []));
74
- this._quangTranslationService = signal(inject(QuangTranslationService), ...(ngDevMode ? [{ debugName: "_quangTranslationService" }] : []));
78
+ this._quangTranslationService = signal(inject(QuangTranslationService, { optional: true }) ?? undefined, ...(ngDevMode ? [{ debugName: "_quangTranslationService" }] : []));
75
79
  this._quangTranslationActiveLang = computed(() => this._quangTranslationService()?.activeLang() ?? null, ...(ngDevMode ? [{ debugName: "_quangTranslationActiveLang" }] : []));
76
80
  this.multipleDatesSeparator = input(' - ', ...(ngDevMode ? [{ debugName: "multipleDatesSeparator" }] : []));
77
81
  this.rangeSelection = input(false, ...(ngDevMode ? [{ debugName: "rangeSelection" }] : []));
@@ -85,6 +89,9 @@ class QuangDateComponent extends QuangBaseComponent {
85
89
  return navigator.language;
86
90
  }, ...(ngDevMode ? [{ debugName: "_activeLanguage" }] : []));
87
91
  this._airDatepickerInstance = signal(undefined, ...(ngDevMode ? [{ debugName: "_airDatepickerInstance" }] : []));
92
+ // AirDatepicker doesn't reliably support toggling `inline` at runtime via `update()`.
93
+ // Track the mode used to create the current instance and recreate when it changes.
94
+ this._airDatepickerInlineMode = signal(null, ...(ngDevMode ? [{ debugName: "_airDatepickerInlineMode" }] : []));
88
95
  this.searchTextDebounce = input(500, ...(ngDevMode ? [{ debugName: "searchTextDebounce" }] : []));
89
96
  this.targetPosition = signal('bottom left', ...(ngDevMode ? [{ debugName: "targetPosition" }] : []));
90
97
  this._generateAirDatepickerEffect = effect(() => {
@@ -97,6 +104,7 @@ class QuangDateComponent extends QuangBaseComponent {
97
104
  this.showTimepicker = computed(() => !this.rangeSelection() && (this.timepicker() || this.showOnlyTimepicker()), ...(ngDevMode ? [{ debugName: "showTimepicker" }] : []));
98
105
  this.isMouseInsideCalendar = signal(false, ...(ngDevMode ? [{ debugName: "isMouseInsideCalendar" }] : []));
99
106
  this.isMouseOutsideCalendar = computed(() => !this.isMouseInsideCalendar(), ...(ngDevMode ? [{ debugName: "isMouseOutsideCalendar" }] : []));
107
+ this._shouldRefocusInputOnHide = signal(false, ...(ngDevMode ? [{ debugName: "_shouldRefocusInputOnHide" }] : []));
100
108
  fromEvent(document, 'scroll', { capture: true })
101
109
  .pipe(takeUntilDestroyed(), debounceTime(250))
102
110
  .subscribe(() => {
@@ -106,110 +114,146 @@ class QuangDateComponent extends QuangBaseComponent {
106
114
  });
107
115
  }
108
116
  setupCalendar() {
109
- if (this._inputForDate()?.nativeElement) {
110
- let currentValue = this._value();
111
- let targetDate;
112
- if (currentValue && typeof currentValue === 'string') {
113
- if (!this.showTimepicker()) {
114
- currentValue = currentValue.split('T')[0];
115
- }
116
- targetDate = [currentValue];
117
+ if (!this._inputForDate()?.nativeElement)
118
+ return;
119
+ const desiredInlineMode = this.showInline();
120
+ const existingInstance = this._airDatepickerInstance();
121
+ const existingInlineMode = this._airDatepickerInlineMode();
122
+ if (existingInstance && existingInlineMode !== null && existingInlineMode !== desiredInlineMode) {
123
+ const maybeDestroy = existingInstance;
124
+ maybeDestroy.destroy?.();
125
+ this._airDatepickerInstance.set(undefined);
126
+ }
127
+ let currentValue = this._value();
128
+ let targetDate;
129
+ if (currentValue && typeof currentValue === 'string') {
130
+ if (!this.showTimepicker()) {
131
+ currentValue = currentValue.split('T')[0];
117
132
  }
118
- else if (currentValue && typeof currentValue === 'object') {
119
- targetDate = [];
120
- if (currentValue.dateFrom) {
121
- let targetDateFrom = currentValue.dateFrom;
122
- if (!this.showTimepicker()) {
123
- targetDateFrom = currentValue.dateFrom.split('T')[0];
124
- }
125
- targetDate.push(targetDateFrom);
126
- }
127
- if (currentValue.dateTo) {
128
- let targetDateTo = currentValue.dateTo;
129
- if (!this.showTimepicker()) {
130
- targetDateTo = currentValue.dateTo.split('T')[0];
131
- }
132
- targetDate.push(targetDateTo);
133
- }
133
+ targetDate = [currentValue];
134
+ }
135
+ else if (currentValue && typeof currentValue === 'object') {
136
+ targetDate = [];
137
+ if (currentValue.dateFrom) {
138
+ const targetDateFrom = this.showTimepicker()
139
+ ? currentValue.dateFrom
140
+ : currentValue.dateFrom.split('T')[0];
141
+ targetDate.push(targetDateFrom);
134
142
  }
135
- this.setCalendarPosition();
136
- const airDatepickerOpts = {
137
- autoClose: true,
138
- classes: this.calendarClasses(),
139
- dateFormat: this.dateFormat(),
140
- inline: this.showInline(),
141
- isMobile: false,
142
- multipleDatesSeparator: this.multipleDatesSeparator(),
143
- range: this.rangeSelection(),
144
- timepicker: this.showTimepicker(),
145
- onlyTimepicker: this.showOnlyTimepicker(),
146
- timeFormat: this.timeFormat(),
147
- minHours: this.minHour(),
148
- maxHours: this.maxHour(),
149
- minMinutes: this.minMinute(),
150
- maxMinutes: this.maxMinute(),
151
- minDate: this.minDate(),
152
- maxDate: this.maxDate(),
153
- toggleSelected: false,
154
- multipleDates: false,
155
- selectedDates: targetDate,
156
- position: this.targetPosition(),
157
- locale: this.getLocale(),
158
- onSelect: ({ date }) => {
159
- if (!Array.isArray(date)) {
160
- let selectTargetDate = date;
161
- if (!this.showTimepicker()) {
162
- selectTargetDate = this.dateToUtc(date);
143
+ if (currentValue.dateTo) {
144
+ const targetDateTo = this.showTimepicker() ? currentValue.dateTo : currentValue.dateTo.split('T')[0];
145
+ targetDate.push(targetDateTo);
146
+ }
147
+ }
148
+ this.setCalendarPosition();
149
+ const userDatepickerOptions = this.datepickerOptions() ?? {};
150
+ const userOnSelect = userDatepickerOptions.onSelect;
151
+ const userOnHide = userDatepickerOptions.onHide;
152
+ const userOnShow = userDatepickerOptions.onShow;
153
+ const airDatepickerOpts = {
154
+ ...userDatepickerOptions,
155
+ autoClose: !this.showInline(),
156
+ showEvent: 'click',
157
+ classes: this.calendarClasses(),
158
+ dateFormat: this.dateFormat(),
159
+ inline: this.showInline(),
160
+ isMobile: false,
161
+ multipleDatesSeparator: this.multipleDatesSeparator(),
162
+ range: this.rangeSelection(),
163
+ timepicker: this.showTimepicker(),
164
+ onlyTimepicker: this.showOnlyTimepicker(),
165
+ timeFormat: this.timeFormat(),
166
+ minHours: this.minHour(),
167
+ maxHours: this.maxHour(),
168
+ minMinutes: this.minMinute(),
169
+ maxMinutes: this.maxMinute(),
170
+ minDate: this.minDate(),
171
+ maxDate: this.maxDate(),
172
+ toggleSelected: false,
173
+ multipleDates: false,
174
+ selectedDates: targetDate,
175
+ position: this.targetPosition(),
176
+ locale: this.getLocale(),
177
+ onSelect: (args) => {
178
+ const { date } = args;
179
+ // AirDatepicker callbacks may fire outside Angular's zone in some app setups.
180
+ // Ensure CVA propagation happens inside the zone so the connected FormControl updates reliably.
181
+ this._ngZone.run(() => {
182
+ this._shouldRefocusInputOnHide.set(true);
183
+ if (Array.isArray(date)) {
184
+ // Range selection: AirDatepicker emits partial selections too (only start date).
185
+ // Committing `_value` for partial selections can trigger `setupCalendar()` re-sync and
186
+ // break the second click. Only commit once the range is complete.
187
+ const [from, to] = date;
188
+ if (!from || !to) {
189
+ return;
163
190
  }
191
+ const value = {
192
+ dateFrom: (this.showTimepicker() ? from : this.dateToUtc(from)).toISOString(),
193
+ dateTo: (this.showTimepicker() ? to : this.dateToUtc(to)).toISOString(),
194
+ };
195
+ this.onChangedHandler(value);
196
+ }
197
+ else if (date) {
198
+ const selectTargetDate = this.showTimepicker() ? date : this.dateToUtc(date);
164
199
  this.onChangedHandler(selectTargetDate.toISOString());
165
200
  }
166
201
  if (this.showInline()) {
167
- this.onHideCalendar();
168
- }
169
- },
170
- onHide: (isAnimationComplete) => {
171
- if (isAnimationComplete) {
172
- this.onHideCalendar();
173
- }
174
- },
175
- ...(this.datepickerOptions() ?? {}),
176
- onShow: (isAnimationComplete) => {
177
- const datepicker = this._airDatepickerInstance()?.$datepicker;
178
- if (datepicker) {
179
- datepicker.onmouseenter = () => {
180
- this.isMouseInsideCalendar.set(true);
181
- };
182
- datepicker.onmouseleave = () => {
183
- this.isMouseInsideCalendar.set(false);
184
- };
185
- }
186
- if (isAnimationComplete || !this.showTimepicker()) {
187
- return;
202
+ // Inline mode should update the connected control immediately.
203
+ // Do not rely on `onHideCalendar()` because inline never hides and the input may be visually hidden.
204
+ this.propagateValueToControl();
188
205
  }
189
- this.setupTimepicker();
190
- },
191
- };
192
- if (this._airDatepickerInstance()) {
193
- if (this._airDatepickerInstance()?.visible) {
194
- this._airDatepickerInstance()?.update(airDatepickerOpts);
195
- }
196
- else {
197
- this._airDatepickerInstance()?.update(airDatepickerOpts, { silent: true });
206
+ });
207
+ userOnSelect?.(args);
208
+ },
209
+ onHide: (isAnimationComplete) => {
210
+ if (isAnimationComplete) {
211
+ this.onHideCalendar();
198
212
  }
199
- if (!targetDate) {
200
- this._airDatepickerInstance()?.setFocusDate(false);
201
- this._airDatepickerInstance()?.clear({ silent: true });
213
+ userOnHide?.(isAnimationComplete);
214
+ },
215
+ onShow: (isAnimationComplete) => {
216
+ const datepicker = this._airDatepickerInstance()?.$datepicker;
217
+ if (datepicker) {
218
+ datepicker.onmouseenter = () => {
219
+ this.isMouseInsideCalendar.set(true);
220
+ };
221
+ datepicker.onmouseleave = () => {
222
+ this.isMouseInsideCalendar.set(false);
223
+ };
202
224
  }
203
- else {
204
- this._airDatepickerInstance()?.selectDate(targetDate, { updateTime: true });
225
+ if (isAnimationComplete || !this.showTimepicker()) {
226
+ return;
205
227
  }
228
+ this.setupTimepicker();
229
+ userOnShow?.(isAnimationComplete);
230
+ },
231
+ };
232
+ if (this._airDatepickerInstance()) {
233
+ if (this._airDatepickerInstance()?.visible) {
234
+ this._airDatepickerInstance()?.update(airDatepickerOpts);
206
235
  }
207
236
  else {
208
- this._airDatepickerInstance.set(new AirDatepicker(this._inputForDate()?.nativeElement, airDatepickerOpts));
237
+ this._airDatepickerInstance()?.update(airDatepickerOpts, { silent: true });
209
238
  }
210
- if (this.showInline()) {
211
- this.setupTimepicker();
239
+ if (targetDate) {
240
+ this._airDatepickerInstance()?.selectDate(targetDate, { updateTime: true, silent: true });
212
241
  }
242
+ else {
243
+ this._airDatepickerInstance()?.setFocusDate(false);
244
+ this._airDatepickerInstance()?.clear({ silent: true });
245
+ }
246
+ }
247
+ else {
248
+ this._airDatepickerInstance.set(new AirDatepicker(this._inputForDate()?.nativeElement, airDatepickerOpts));
249
+ }
250
+ this._airDatepickerInlineMode.set(desiredInlineMode);
251
+ if (desiredInlineMode) {
252
+ // Ensure inline calendar is visible after re-creation/update.
253
+ this._airDatepickerInstance()?.show?.();
254
+ }
255
+ if (this.showInline()) {
256
+ this.setupTimepicker();
213
257
  }
214
258
  }
215
259
  onChangeText($event) {
@@ -218,14 +262,23 @@ class QuangDateComponent extends QuangBaseComponent {
218
262
  // TODO: check format for DateRange
219
263
  if (value.length === this.valueFormat().length && isMatch(value, this.valueFormat())) {
220
264
  this.onChangedHandler(this.setupInputStringToDate(value).toISOString());
265
+ if (this.showInline()) {
266
+ this.propagateValueToControl();
267
+ }
221
268
  }
222
269
  }
223
270
  else {
224
271
  this.onChangedHandler(value);
272
+ if (this.showInline()) {
273
+ this.propagateValueToControl();
274
+ }
225
275
  }
226
276
  }
227
277
  onBlurHandler() {
228
278
  super.onBlurHandler();
279
+ if (this.showInline()) {
280
+ return;
281
+ }
229
282
  if (this.isMouseOutsideCalendar() && this._airDatepickerInstance()?.visible) {
230
283
  this._airDatepickerInstance()?.hide();
231
284
  }
@@ -235,9 +288,9 @@ class QuangDateComponent extends QuangBaseComponent {
235
288
  if (value.length !== targetValueFormat.length) {
236
289
  targetValueFormat = targetValueFormat.replace('yyyy', 'yy');
237
290
  }
238
- let targetDate = parse(value, targetValueFormat, new Date());
291
+ const targetDate = parse(value, targetValueFormat, new Date());
239
292
  if (!this.showTimepicker()) {
240
- targetDate = this.dateToUtc(targetDate);
293
+ return this.dateToUtc(targetDate);
241
294
  }
242
295
  return targetDate;
243
296
  }
@@ -276,6 +329,42 @@ class QuangDateComponent extends QuangBaseComponent {
276
329
  }
277
330
  this._value.set(targetDate);
278
331
  }
332
+ propagateValueToControl() {
333
+ if (this.formControl()?.getRawValue() !== this._value()) {
334
+ super.onChangedHandler(this._value());
335
+ }
336
+ else if (this.onTouched) {
337
+ this.onTouched();
338
+ }
339
+ this.requestRender();
340
+ }
341
+ requestRender() {
342
+ // Inline datepicker interactions can happen outside Angular-managed events.
343
+ // Marking the view dirty is not always enough in zoneless/event-coalesced setups,
344
+ // so we coalesce a manual tick.
345
+ this._cdr.markForCheck();
346
+ if (this._tickScheduled) {
347
+ return;
348
+ }
349
+ this._tickScheduled = true;
350
+ queueMicrotask(() => {
351
+ this._tickScheduled = false;
352
+ this._appRef.tick();
353
+ });
354
+ }
355
+ syncValueFromDatepickerSelection() {
356
+ if (!this.showInline()) {
357
+ return;
358
+ }
359
+ const datepickerInstance = this._airDatepickerInstance();
360
+ const selectedDate = datepickerInstance?.selectedDates?.[0];
361
+ if (!(selectedDate instanceof Date)) {
362
+ return;
363
+ }
364
+ const targetDate = this.showTimepicker() ? selectedDate : this.dateToUtc(selectedDate);
365
+ this.onChangedHandler(targetDate.toISOString());
366
+ this.propagateValueToControl();
367
+ }
279
368
  onHideCalendar() {
280
369
  const valueInput = this._inputForDate()?.nativeElement.value;
281
370
  let value = valueInput;
@@ -284,18 +373,14 @@ class QuangDateComponent extends QuangBaseComponent {
284
373
  const [dateFrom, dateTo] = valueInput.split(this.multipleDatesSeparator());
285
374
  value.dateFrom = dateFrom ?? '';
286
375
  value.dateTo = dateTo ?? '';
287
- if (!value.dateFrom || !this.checkDateMatch(value.dateFrom)) {
288
- value.dateFrom = null;
289
- }
290
- else {
291
- value.dateFrom = this.setupInputStringToDate(value.dateFrom).toISOString();
292
- }
293
- if (!value.dateTo || !this.checkDateMatch(value.dateTo)) {
294
- value.dateTo = null;
295
- }
296
- else {
297
- value.dateTo = this.setupInputStringToDate(value.dateTo).toISOString();
298
- }
376
+ value.dateFrom =
377
+ !value.dateFrom || !this.checkDateMatch(value.dateFrom)
378
+ ? null
379
+ : this.setupInputStringToDate(value.dateFrom).toISOString();
380
+ value.dateTo =
381
+ !value.dateTo || !this.checkDateMatch(value.dateTo)
382
+ ? null
383
+ : this.setupInputStringToDate(value.dateTo).toISOString();
299
384
  this.onChangedHandler(value);
300
385
  }
301
386
  else if (this.checkDateMatch(value)) {
@@ -304,21 +389,20 @@ class QuangDateComponent extends QuangBaseComponent {
304
389
  else {
305
390
  this.onChangedHandler(null);
306
391
  }
307
- if (this.formControl()?.getRawValue() !== this._value()) {
308
- super.onChangedHandler(this._value());
309
- }
310
- else if (this.onTouched) {
311
- this.onTouched();
392
+ this.propagateValueToControl();
393
+ if (this.showInline()) {
394
+ return;
312
395
  }
313
- // Only focus the input if the active element is still within this component
314
- // (e.g., user selected a date via keyboard or mouse click on the calendar).
396
+ // Only focus the input when the user actually interacted with the calendar.
315
397
  // Avoids infinite focus loop when tabbing between multiple datepickers.
316
398
  const activeElement = document.activeElement;
317
399
  const calendarElement = this._airDatepickerInstance()?.$datepicker;
318
400
  const inputElement = this._inputForDate()?.nativeElement;
319
401
  const isCalendarFocused = calendarElement?.contains(activeElement);
320
- if (isCalendarFocused || this.isMouseInsideCalendar()) {
321
- inputElement?.focus();
402
+ const shouldRefocus = this._shouldRefocusInputOnHide() || isCalendarFocused || this.isMouseInsideCalendar();
403
+ this._shouldRefocusInputOnHide.set(false);
404
+ if (shouldRefocus) {
405
+ setTimeout(() => inputElement?.focus(), 0);
322
406
  }
323
407
  this.onBlurHandler();
324
408
  }
@@ -327,6 +411,9 @@ class QuangDateComponent extends QuangBaseComponent {
327
411
  return format(val, this.valueFormat());
328
412
  }
329
413
  if (val && typeof val === 'object') {
414
+ if (!val.dateFrom && !val.dateTo) {
415
+ return '';
416
+ }
330
417
  let dateFromFormat = '';
331
418
  let dateToFormat = '';
332
419
  if (val.dateFrom) {
@@ -340,8 +427,29 @@ class QuangDateComponent extends QuangBaseComponent {
340
427
  return '';
341
428
  }
342
429
  openDatePicker() {
343
- if (this._inputForDate()?.nativeElement) {
344
- this._inputForDate()?.nativeElement.focus();
430
+ const inputEl = this._inputForDate()?.nativeElement;
431
+ if (!inputEl || this._isDisabled()) {
432
+ return;
433
+ }
434
+ inputEl.focus();
435
+ if (!this._airDatepickerInstance()) {
436
+ this.setupCalendar();
437
+ }
438
+ this._airDatepickerInstance()?.show();
439
+ }
440
+ onInputKeydown(event) {
441
+ if (this._isDisabled()) {
442
+ return;
443
+ }
444
+ const datepickerInstance = this._airDatepickerInstance();
445
+ if (event.key === 'Escape' && datepickerInstance?.visible) {
446
+ event.preventDefault();
447
+ datepickerInstance.hide();
448
+ return;
449
+ }
450
+ if (event.key === 'Enter' || event.key === 'ArrowDown') {
451
+ event.preventDefault();
452
+ this.openDatePicker();
345
453
  }
346
454
  }
347
455
  interceptInputInteraction($event) {
@@ -354,15 +462,25 @@ class QuangDateComponent extends QuangBaseComponent {
354
462
  getLocale() {
355
463
  switch (this._activeLanguage()?.toLowerCase()) {
356
464
  case 'en':
357
- return en.default || en;
465
+ return this.unwrapLocaleModule(en);
358
466
  case 'it':
359
- return it.default || it;
467
+ return this.unwrapLocaleModule(it);
360
468
  case 'fr':
361
- return fr.default || fr;
469
+ return this.unwrapLocaleModule(fr);
362
470
  default:
363
- return en.default || en;
471
+ return this.unwrapLocaleModule(en);
364
472
  }
365
473
  }
474
+ unwrapLocaleModule(localeModule) {
475
+ if (typeof localeModule === 'object' && localeModule !== null && 'default' in localeModule) {
476
+ const moduleWithDefault = localeModule;
477
+ if (moduleWithDefault.default) {
478
+ return moduleWithDefault.default;
479
+ }
480
+ return localeModule;
481
+ }
482
+ return localeModule;
483
+ }
366
484
  onCancel() {
367
485
  this._inputForDate()?.nativeElement.blur();
368
486
  }
@@ -370,10 +488,6 @@ class QuangDateComponent extends QuangBaseComponent {
370
488
  // convert to UTC time removing the timezone
371
489
  return new Date(date.getTime() - date.getTimezoneOffset() * 60000);
372
490
  }
373
- dateToLocal(date) {
374
- // convert to local time adding the timezone
375
- return new Date(date.getTime() + date.getTimezoneOffset() * 60000);
376
- }
377
491
  setCalendarPosition() {
378
492
  const windowInnerHeight = window.innerHeight;
379
493
  const inputBoundingClientRect = this._inputForDate()?.nativeElement.getBoundingClientRect();
@@ -386,8 +500,23 @@ class QuangDateComponent extends QuangBaseComponent {
386
500
  }
387
501
  }
388
502
  setupTimepicker() {
389
- const timepicker = document.getElementsByClassName('air-datepicker-time')?.[0];
390
- if (timepicker) {
503
+ const datepickerRoot = this._airDatepickerInstance()?.$datepicker;
504
+ if (!datepickerRoot) {
505
+ return;
506
+ }
507
+ // AirDatepicker may re-render time inputs; use delegated listeners so we don't lose handlers.
508
+ if (!datepickerRoot.dataset['quangTimepickerListeners']) {
509
+ datepickerRoot.dataset['quangTimepickerListeners'] = 'true';
510
+ datepickerRoot.addEventListener('input', () => {
511
+ if (!this.showInline()) {
512
+ return;
513
+ }
514
+ // Let AirDatepicker update its internal selection first.
515
+ setTimeout(() => this._ngZone.run(() => this.syncValueFromDatepickerSelection()), 0);
516
+ }, { capture: true });
517
+ }
518
+ const timepickers = datepickerRoot.getElementsByClassName('air-datepicker-time');
519
+ for (const timepicker of Array.from(timepickers)) {
391
520
  const inputs = timepicker.getElementsByTagName('input');
392
521
  for (const input of Array.from(inputs)) {
393
522
  input.setAttribute('type', 'number');
@@ -397,7 +526,7 @@ class QuangDateComponent extends QuangBaseComponent {
397
526
  evt.stopImmediatePropagation();
398
527
  };
399
528
  input.onblur = () => {
400
- if (this.isMouseOutsideCalendar()) {
529
+ if (!this.showInline() && this.isMouseOutsideCalendar()) {
401
530
  this._airDatepickerInstance()?.hide();
402
531
  }
403
532
  };
@@ -408,14 +537,12 @@ class QuangDateComponent extends QuangBaseComponent {
408
537
  return isMatch(date, this.valueFormat()) || isMatch(date, this.valueFormat().replace('yyyy', 'yy'));
409
538
  }
410
539
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: QuangDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
411
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: QuangDateComponent, isStandalone: true, selector: "quang-date", inputs: { dateFormat: { classPropertyName: "dateFormat", publicName: "dateFormat", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, activeLanguageOverride: { classPropertyName: "activeLanguageOverride", publicName: "activeLanguageOverride", isSignal: true, isRequired: false, transformFunction: null }, timepicker: { classPropertyName: "timepicker", publicName: "timepicker", isSignal: true, isRequired: false, transformFunction: null }, invalidDateMessage: { classPropertyName: "invalidDateMessage", publicName: "invalidDateMessage", isSignal: true, isRequired: false, transformFunction: null }, showOnlyTimepicker: { classPropertyName: "showOnlyTimepicker", publicName: "showOnlyTimepicker", isSignal: true, isRequired: false, transformFunction: null }, minHour: { classPropertyName: "minHour", publicName: "minHour", isSignal: true, isRequired: false, transformFunction: null }, maxHour: { classPropertyName: "maxHour", publicName: "maxHour", isSignal: true, isRequired: false, transformFunction: null }, minMinute: { classPropertyName: "minMinute", publicName: "minMinute", isSignal: true, isRequired: false, transformFunction: null }, maxMinute: { classPropertyName: "maxMinute", publicName: "maxMinute", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, showInline: { classPropertyName: "showInline", publicName: "showInline", isSignal: true, isRequired: false, transformFunction: null }, calendarClasses: { classPropertyName: "calendarClasses", publicName: "calendarClasses", isSignal: true, isRequired: false, transformFunction: null }, buttonClass: { classPropertyName: "buttonClass", publicName: "buttonClass", isSignal: true, isRequired: false, transformFunction: null }, datepickerOptions: { classPropertyName: "datepickerOptions", publicName: "datepickerOptions", isSignal: true, isRequired: false, transformFunction: null }, multipleDatesSeparator: { classPropertyName: "multipleDatesSeparator", publicName: "multipleDatesSeparator", isSignal: true, isRequired: false, transformFunction: null }, rangeSelection: { classPropertyName: "rangeSelection", publicName: "rangeSelection", isSignal: true, isRequired: false, transformFunction: null }, searchTextDebounce: { classPropertyName: "searchTextDebounce", publicName: "searchTextDebounce", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => QuangDateComponent), multi: true }], viewQueries: [{ propertyName: "_inputForDate", first: true, predicate: ["inputForDate"], descendants: true, isSignal: true }, { propertyName: "contentTemplate", first: true, predicate: ["calendarButton"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"mb-3\">\n @if (componentLabel()) {\n <label\n [htmlFor]=\"componentId()\"\n class=\"form-label d-flex gap-2\"\n >\n <div>\n <span>{{ componentLabel() | transloco }}</span>\n <span [hidden]=\"!_isRequired()\">*</span>\n </div>\n @if (helpMessage() && helpMessageTooltip()) {\n <div [quangTooltip]=\"helpMessage() | transloco\">\n <ng-content select=\"[help-icon]\" />\n </div>\n }\n </label>\n }\n <div\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n class=\"input-date-container\"\n >\n <input\n [attr.required]=\"getIsRequiredControl()\"\n [autocomplete]=\"'off'\"\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n [class.with-button-calendar]=\"!hasNoContent()\"\n [disabled]=\"_isDisabled()\"\n [hidden]=\"showInline()\"\n [id]=\"componentId()\"\n [ngClass]=\"componentClass()\"\n [placeholder]=\"componentPlaceholder() | transloco\"\n [readOnly]=\"isReadonly()\"\n [tabIndex]=\"componentTabIndex()\"\n [value]=\"inputValueString()\"\n (blur)=\"onBlurHandler()\"\n (focus)=\"interceptInputInteraction($event)\"\n (input)=\"onChangeText($event)\"\n (mouseenter)=\"isMouseInsideCalendar.set(true)\"\n (mouseleave)=\"isMouseInsideCalendar.set(false)\"\n (search)=\"onCancel()\"\n #inputForDate\n class=\"form-control\"\n type=\"search\"\n />\n <button\n [attr.required]=\"getIsRequiredControl()\"\n [class.border-danger]=\"_showErrors()\"\n [class.border-success]=\"_showSuccess()\"\n [hidden]=\"showInline() || hasNoContent() || _isDisabled()\"\n [ngClass]=\"buttonClass()\"\n (click)=\"_ngControl()?.disabled ? null : openDatePicker()\"\n #calendarButton\n aria-label=\"calendar-button\"\n class=\"btn btn-outline-secondary btn-outline-calendar\"\n type=\"button\"\n >\n <ng-content></ng-content>\n </button>\n </div>\n <div class=\"valid-feedback\">\n {{ successMessage() | transloco }}\n </div>\n <div class=\"invalid-feedback\">\n {{ _currentErrorMessage() | transloco: _currentErrorMessageExtraData() }}\n </div>\n @if (helpMessage() && !helpMessageTooltip()) {\n <small\n [hidden]=\"_showSuccess() || _showErrors()\"\n aria-live=\"assertive\"\n class=\"form-text text-muted\"\n >\n {{ helpMessage() | transloco }}\n </small>\n }\n</div>\n", styles: ["input::-webkit-search-cancel-button{-webkit-appearance:none;height:.75rem;width:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 3 1024 1024' width='12' height='12' fill='currentColor'%3E%3Cpath d='M9 1018q5 4 10.5 6.5 5.5 2.5 11.5 2.5 6 0 11.5-2.5 5.5-2.5 10.5-6.5l459-459 459 459q5 4 10.5 6.5 5.5 2.5 11.5 2.5 6 0 11.5-2.5 5.5-2.5 10.5-6.5 9-9 9-22 0-13-9-22l-459-459 459-459q9-9 9-22 0-13-9-22-9-9-22-9-13 0-22 9l-459 459-459-459q-9-9-22-9-13 0-22 9-9 9-9 22 0 13 9 22l459 459-459 459q-9 9-9 22 0 13 9 22l0 0z'/%3E%3C/svg%3E%0A\");cursor:pointer}::ng-deep .air-datepicker{z-index:99999}::ng-deep .air-datepicker.-inline-{z-index:unset}::ng-deep .air-datepicker .air-datepicker--pointer{display:none}::ng-deep .air-datepicker .air-datepicker-time{display:block}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--current{display:none}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders{display:flex;justify-content:center;padding-top:.25rem;gap:1rem}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row{width:100%;position:relative}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row:first-child:after{content:\":\";display:inline;font-size:1rem;position:absolute;right:-.65rem}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row input{width:100%;text-align:center;padding:0;padding-left:1rem;border-color:var(--bs-border-color)}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row .form-control:focus{box-shadow:unset!important}::ng-deep .air-datepicker.-inline-.-only-timepicker-{border:0}::ng-deep .air-datepicker.-inline-.-only-timepicker- .air-datepicker--time{border:0}:host{display:block}:host .input-date-container{display:flex}:host input{flex:1}:host input.with-button-calendar{border-top-right-radius:0;border-bottom-right-radius:0}:host input:disabled{border-radius:var(--bs-border-radius)}:host .btn-outline-calendar{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0;min-width:unset;display:flex;border-color:var(--bs-border-color)}:host .border-danger{border-color:var(--bs-form-invalid-border-color)}:host .border-success{border-color:var(--bs-form-valid-border-color)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: QuangTooltipDirective, selector: "[quangTooltip]", inputs: ["quangTooltip", "showMethod"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
540
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: QuangDateComponent, isStandalone: true, selector: "quang-date", inputs: { dateFormat: { classPropertyName: "dateFormat", publicName: "dateFormat", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, activeLanguageOverride: { classPropertyName: "activeLanguageOverride", publicName: "activeLanguageOverride", isSignal: true, isRequired: false, transformFunction: null }, timepicker: { classPropertyName: "timepicker", publicName: "timepicker", isSignal: true, isRequired: false, transformFunction: null }, invalidDateMessage: { classPropertyName: "invalidDateMessage", publicName: "invalidDateMessage", isSignal: true, isRequired: false, transformFunction: null }, showOnlyTimepicker: { classPropertyName: "showOnlyTimepicker", publicName: "showOnlyTimepicker", isSignal: true, isRequired: false, transformFunction: null }, minHour: { classPropertyName: "minHour", publicName: "minHour", isSignal: true, isRequired: false, transformFunction: null }, maxHour: { classPropertyName: "maxHour", publicName: "maxHour", isSignal: true, isRequired: false, transformFunction: null }, minMinute: { classPropertyName: "minMinute", publicName: "minMinute", isSignal: true, isRequired: false, transformFunction: null }, maxMinute: { classPropertyName: "maxMinute", publicName: "maxMinute", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, showInline: { classPropertyName: "showInline", publicName: "showInline", isSignal: true, isRequired: false, transformFunction: null }, calendarClasses: { classPropertyName: "calendarClasses", publicName: "calendarClasses", isSignal: true, isRequired: false, transformFunction: null }, buttonClass: { classPropertyName: "buttonClass", publicName: "buttonClass", isSignal: true, isRequired: false, transformFunction: null }, datepickerOptions: { classPropertyName: "datepickerOptions", publicName: "datepickerOptions", isSignal: true, isRequired: false, transformFunction: null }, multipleDatesSeparator: { classPropertyName: "multipleDatesSeparator", publicName: "multipleDatesSeparator", isSignal: true, isRequired: false, transformFunction: null }, rangeSelection: { classPropertyName: "rangeSelection", publicName: "rangeSelection", isSignal: true, isRequired: false, transformFunction: null }, searchTextDebounce: { classPropertyName: "searchTextDebounce", publicName: "searchTextDebounce", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => QuangDateComponent), multi: true }], viewQueries: [{ propertyName: "_inputForDate", first: true, predicate: ["inputForDate"], descendants: true, isSignal: true }, { propertyName: "contentTemplate", first: true, predicate: ["calendarButton"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"mb-3\">\n @if (componentLabel()) {\n <label\n [htmlFor]=\"componentId()\"\n class=\"form-label d-flex gap-2\"\n >\n <div>\n <span>{{ componentLabel() | transloco }}</span>\n <span [hidden]=\"!_isRequired()\">*</span>\n </div>\n @if (helpMessage() && helpMessageTooltip()) {\n <div [quangTooltip]=\"helpMessage() | transloco\">\n <ng-content select=\"[help-icon]\" />\n </div>\n }\n </label>\n }\n <div\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n class=\"input-date-container\"\n >\n <input\n [attr.aria-hidden]=\"showInline() ? 'true' : null\"\n [attr.required]=\"getIsRequiredControl()\"\n [autocomplete]=\"'off'\"\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n [class.quang-date-inline-hidden]=\"showInline()\"\n [class.with-button-calendar]=\"!hasNoContent()\"\n [disabled]=\"_isDisabled()\"\n [id]=\"componentId()\"\n [ngClass]=\"componentClass()\"\n [placeholder]=\"componentPlaceholder() | transloco\"\n [readOnly]=\"isReadonly()\"\n [tabIndex]=\"componentTabIndex()\"\n [value]=\"inputValueString()\"\n (blur)=\"onBlurHandler()\"\n (focus)=\"interceptInputInteraction($event)\"\n (input)=\"onChangeText($event)\"\n (keydown)=\"onInputKeydown($event)\"\n (mouseenter)=\"isMouseInsideCalendar.set(true)\"\n (mouseleave)=\"isMouseInsideCalendar.set(false)\"\n (search)=\"onCancel()\"\n #inputForDate\n class=\"form-control\"\n type=\"search\"\n />\n <button\n [attr.aria-hidden]=\"showInline() ? 'true' : null\"\n [attr.required]=\"getIsRequiredControl()\"\n [class.border-danger]=\"_showErrors()\"\n [class.border-success]=\"_showSuccess()\"\n [class.quang-date-inline-hidden]=\"showInline()\"\n [hidden]=\"hasNoContent() || _isDisabled()\"\n [ngClass]=\"buttonClass()\"\n (click)=\"_ngControl()?.disabled ? null : openDatePicker()\"\n #calendarButton\n aria-label=\"calendar-button\"\n class=\"btn btn-outline-secondary btn-outline-calendar\"\n type=\"button\"\n >\n <ng-content></ng-content>\n </button>\n </div>\n <div class=\"valid-feedback\">\n {{ successMessage() | transloco }}\n </div>\n <div class=\"invalid-feedback\">\n {{ _currentErrorMessage() | transloco: _currentErrorMessageExtraData() }}\n </div>\n @if (helpMessage() && !helpMessageTooltip()) {\n <small\n [hidden]=\"_showSuccess() || _showErrors()\"\n aria-live=\"assertive\"\n class=\"form-text text-muted\"\n >\n {{ helpMessage() | transloco }}\n </small>\n }\n</div>\n", styles: ["input::-webkit-search-cancel-button{-webkit-appearance:none;height:.75rem;width:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 3 1024 1024' width='12' height='12' fill='currentColor'%3E%3Cpath d='M9 1018q5 4 10.5 6.5 5.5 2.5 11.5 2.5 6 0 11.5-2.5 5.5-2.5 10.5-6.5l459-459 459 459q5 4 10.5 6.5 5.5 2.5 11.5 2.5 6 0 11.5-2.5 5.5-2.5 10.5-6.5 9-9 9-22 0-13-9-22l-459-459 459-459q9-9 9-22 0-13-9-22-9-9-22-9-13 0-22 9l-459 459-459-459q-9-9-22-9-13 0-22 9-9 9-9 22 0 13 9 22l459 459-459 459q-9 9-9 22 0 13 9 22l0 0z'/%3E%3C/svg%3E%0A\");cursor:pointer}::ng-deep .air-datepicker{z-index:99999}::ng-deep .air-datepicker.-inline-{z-index:unset}::ng-deep .air-datepicker .air-datepicker--pointer{display:none}::ng-deep .air-datepicker .air-datepicker-time{display:block}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--current{display:none}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders{display:flex;justify-content:center;padding-top:.25rem;gap:1rem}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row{width:100%;position:relative}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row:first-child:after{content:\":\";display:inline;font-size:1rem;position:absolute;right:-.65rem}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row input{width:100%;text-align:center;padding:0;padding-left:1rem;border-color:var(--bs-border-color)}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row .form-control:focus{box-shadow:unset!important}::ng-deep .air-datepicker.-inline-.-only-timepicker-{border:0;padding:0!important}::ng-deep .air-datepicker.-inline-.-only-timepicker- .air-datepicker--time{border:0;padding:0!important}::ng-deep .air-datepicker.-inline-.-only-timepicker- .air-datepicker-time{padding:0!important}:host{display:block}:host .quang-date-inline-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:0!important;border:0!important;opacity:0!important;pointer-events:none!important;overflow:hidden!important;white-space:nowrap!important;clip:rect(0 0 0 0)!important;clip-path:inset(50%)!important}:host .input-date-container{display:flex}:host input{flex:1}:host input.with-button-calendar{border-top-right-radius:0;border-bottom-right-radius:0}:host input:disabled{border-radius:var(--bs-border-radius)}:host .btn-outline-calendar{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0;min-width:unset;display:flex;border-color:var(--bs-border-color)}:host .border-danger{border-color:var(--bs-form-invalid-border-color)}:host .border-success{border-color:var(--bs-form-valid-border-color)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: QuangTooltipDirective, selector: "[quangTooltip]", inputs: ["quangTooltip", "showMethod"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
412
541
  }
413
542
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: QuangDateComponent, decorators: [{
414
543
  type: Component,
415
- args: [{ selector: 'quang-date', providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => QuangDateComponent), multi: true }], imports: [TranslocoPipe, NgClass, QuangTooltipDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-3\">\n @if (componentLabel()) {\n <label\n [htmlFor]=\"componentId()\"\n class=\"form-label d-flex gap-2\"\n >\n <div>\n <span>{{ componentLabel() | transloco }}</span>\n <span [hidden]=\"!_isRequired()\">*</span>\n </div>\n @if (helpMessage() && helpMessageTooltip()) {\n <div [quangTooltip]=\"helpMessage() | transloco\">\n <ng-content select=\"[help-icon]\" />\n </div>\n }\n </label>\n }\n <div\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n class=\"input-date-container\"\n >\n <input\n [attr.required]=\"getIsRequiredControl()\"\n [autocomplete]=\"'off'\"\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n [class.with-button-calendar]=\"!hasNoContent()\"\n [disabled]=\"_isDisabled()\"\n [hidden]=\"showInline()\"\n [id]=\"componentId()\"\n [ngClass]=\"componentClass()\"\n [placeholder]=\"componentPlaceholder() | transloco\"\n [readOnly]=\"isReadonly()\"\n [tabIndex]=\"componentTabIndex()\"\n [value]=\"inputValueString()\"\n (blur)=\"onBlurHandler()\"\n (focus)=\"interceptInputInteraction($event)\"\n (input)=\"onChangeText($event)\"\n (mouseenter)=\"isMouseInsideCalendar.set(true)\"\n (mouseleave)=\"isMouseInsideCalendar.set(false)\"\n (search)=\"onCancel()\"\n #inputForDate\n class=\"form-control\"\n type=\"search\"\n />\n <button\n [attr.required]=\"getIsRequiredControl()\"\n [class.border-danger]=\"_showErrors()\"\n [class.border-success]=\"_showSuccess()\"\n [hidden]=\"showInline() || hasNoContent() || _isDisabled()\"\n [ngClass]=\"buttonClass()\"\n (click)=\"_ngControl()?.disabled ? null : openDatePicker()\"\n #calendarButton\n aria-label=\"calendar-button\"\n class=\"btn btn-outline-secondary btn-outline-calendar\"\n type=\"button\"\n >\n <ng-content></ng-content>\n </button>\n </div>\n <div class=\"valid-feedback\">\n {{ successMessage() | transloco }}\n </div>\n <div class=\"invalid-feedback\">\n {{ _currentErrorMessage() | transloco: _currentErrorMessageExtraData() }}\n </div>\n @if (helpMessage() && !helpMessageTooltip()) {\n <small\n [hidden]=\"_showSuccess() || _showErrors()\"\n aria-live=\"assertive\"\n class=\"form-text text-muted\"\n >\n {{ helpMessage() | transloco }}\n </small>\n }\n</div>\n", styles: ["input::-webkit-search-cancel-button{-webkit-appearance:none;height:.75rem;width:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 3 1024 1024' width='12' height='12' fill='currentColor'%3E%3Cpath d='M9 1018q5 4 10.5 6.5 5.5 2.5 11.5 2.5 6 0 11.5-2.5 5.5-2.5 10.5-6.5l459-459 459 459q5 4 10.5 6.5 5.5 2.5 11.5 2.5 6 0 11.5-2.5 5.5-2.5 10.5-6.5 9-9 9-22 0-13-9-22l-459-459 459-459q9-9 9-22 0-13-9-22-9-9-22-9-13 0-22 9l-459 459-459-459q-9-9-22-9-13 0-22 9-9 9-9 22 0 13 9 22l459 459-459 459q-9 9-9 22 0 13 9 22l0 0z'/%3E%3C/svg%3E%0A\");cursor:pointer}::ng-deep .air-datepicker{z-index:99999}::ng-deep .air-datepicker.-inline-{z-index:unset}::ng-deep .air-datepicker .air-datepicker--pointer{display:none}::ng-deep .air-datepicker .air-datepicker-time{display:block}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--current{display:none}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders{display:flex;justify-content:center;padding-top:.25rem;gap:1rem}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row{width:100%;position:relative}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row:first-child:after{content:\":\";display:inline;font-size:1rem;position:absolute;right:-.65rem}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row input{width:100%;text-align:center;padding:0;padding-left:1rem;border-color:var(--bs-border-color)}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row .form-control:focus{box-shadow:unset!important}::ng-deep .air-datepicker.-inline-.-only-timepicker-{border:0}::ng-deep .air-datepicker.-inline-.-only-timepicker- .air-datepicker--time{border:0}:host{display:block}:host .input-date-container{display:flex}:host input{flex:1}:host input.with-button-calendar{border-top-right-radius:0;border-bottom-right-radius:0}:host input:disabled{border-radius:var(--bs-border-radius)}:host .btn-outline-calendar{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0;min-width:unset;display:flex;border-color:var(--bs-border-color)}:host .border-danger{border-color:var(--bs-form-invalid-border-color)}:host .border-success{border-color:var(--bs-form-valid-border-color)}\n"] }]
416
- }], ctorParameters: () => [], propDecorators: { dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], activeLanguageOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeLanguageOverride", required: false }] }], timepicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "timepicker", required: false }] }], invalidDateMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidDateMessage", required: false }] }], showOnlyTimepicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOnlyTimepicker", required: false }] }], minHour: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHour", required: false }] }], maxHour: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHour", required: false }] }], minMinute: [{ type: i0.Input, args: [{ isSignal: true, alias: "minMinute", required: false }] }], maxMinute: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxMinute", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], showInline: [{ type: i0.Input, args: [{ isSignal: true, alias: "showInline", required: false }] }], calendarClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarClasses", required: false }] }], buttonClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonClass", required: false }] }], datepickerOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "datepickerOptions", required: false }] }], _inputForDate: [{ type: i0.ViewChild, args: ['inputForDate', { isSignal: true }] }], contentTemplate: [{ type: i0.ViewChild, args: ['calendarButton', { isSignal: true }] }], _quangTranslationService: [{
417
- type: Optional
418
- }], multipleDatesSeparator: [{ type: i0.Input, args: [{ isSignal: true, alias: "multipleDatesSeparator", required: false }] }], rangeSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeSelection", required: false }] }], searchTextDebounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTextDebounce", required: false }] }] } });
544
+ args: [{ selector: 'quang-date', providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => QuangDateComponent), multi: true }], imports: [TranslocoPipe, NgClass, QuangTooltipDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mb-3\">\n @if (componentLabel()) {\n <label\n [htmlFor]=\"componentId()\"\n class=\"form-label d-flex gap-2\"\n >\n <div>\n <span>{{ componentLabel() | transloco }}</span>\n <span [hidden]=\"!_isRequired()\">*</span>\n </div>\n @if (helpMessage() && helpMessageTooltip()) {\n <div [quangTooltip]=\"helpMessage() | transloco\">\n <ng-content select=\"[help-icon]\" />\n </div>\n }\n </label>\n }\n <div\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n class=\"input-date-container\"\n >\n <input\n [attr.aria-hidden]=\"showInline() ? 'true' : null\"\n [attr.required]=\"getIsRequiredControl()\"\n [autocomplete]=\"'off'\"\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n [class.quang-date-inline-hidden]=\"showInline()\"\n [class.with-button-calendar]=\"!hasNoContent()\"\n [disabled]=\"_isDisabled()\"\n [id]=\"componentId()\"\n [ngClass]=\"componentClass()\"\n [placeholder]=\"componentPlaceholder() | transloco\"\n [readOnly]=\"isReadonly()\"\n [tabIndex]=\"componentTabIndex()\"\n [value]=\"inputValueString()\"\n (blur)=\"onBlurHandler()\"\n (focus)=\"interceptInputInteraction($event)\"\n (input)=\"onChangeText($event)\"\n (keydown)=\"onInputKeydown($event)\"\n (mouseenter)=\"isMouseInsideCalendar.set(true)\"\n (mouseleave)=\"isMouseInsideCalendar.set(false)\"\n (search)=\"onCancel()\"\n #inputForDate\n class=\"form-control\"\n type=\"search\"\n />\n <button\n [attr.aria-hidden]=\"showInline() ? 'true' : null\"\n [attr.required]=\"getIsRequiredControl()\"\n [class.border-danger]=\"_showErrors()\"\n [class.border-success]=\"_showSuccess()\"\n [class.quang-date-inline-hidden]=\"showInline()\"\n [hidden]=\"hasNoContent() || _isDisabled()\"\n [ngClass]=\"buttonClass()\"\n (click)=\"_ngControl()?.disabled ? null : openDatePicker()\"\n #calendarButton\n aria-label=\"calendar-button\"\n class=\"btn btn-outline-secondary btn-outline-calendar\"\n type=\"button\"\n >\n <ng-content></ng-content>\n </button>\n </div>\n <div class=\"valid-feedback\">\n {{ successMessage() | transloco }}\n </div>\n <div class=\"invalid-feedback\">\n {{ _currentErrorMessage() | transloco: _currentErrorMessageExtraData() }}\n </div>\n @if (helpMessage() && !helpMessageTooltip()) {\n <small\n [hidden]=\"_showSuccess() || _showErrors()\"\n aria-live=\"assertive\"\n class=\"form-text text-muted\"\n >\n {{ helpMessage() | transloco }}\n </small>\n }\n</div>\n", styles: ["input::-webkit-search-cancel-button{-webkit-appearance:none;height:.75rem;width:.75rem;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 3 1024 1024' width='12' height='12' fill='currentColor'%3E%3Cpath d='M9 1018q5 4 10.5 6.5 5.5 2.5 11.5 2.5 6 0 11.5-2.5 5.5-2.5 10.5-6.5l459-459 459 459q5 4 10.5 6.5 5.5 2.5 11.5 2.5 6 0 11.5-2.5 5.5-2.5 10.5-6.5 9-9 9-22 0-13-9-22l-459-459 459-459q9-9 9-22 0-13-9-22-9-9-22-9-13 0-22 9l-459 459-459-459q-9-9-22-9-13 0-22 9-9 9-9 22 0 13 9 22l459 459-459 459q-9 9-9 22 0 13 9 22l0 0z'/%3E%3C/svg%3E%0A\");cursor:pointer}::ng-deep .air-datepicker{z-index:99999}::ng-deep .air-datepicker.-inline-{z-index:unset}::ng-deep .air-datepicker .air-datepicker--pointer{display:none}::ng-deep .air-datepicker .air-datepicker-time{display:block}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--current{display:none}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders{display:flex;justify-content:center;padding-top:.25rem;gap:1rem}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row{width:100%;position:relative}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row:first-child:after{content:\":\";display:inline;font-size:1rem;position:absolute;right:-.65rem}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row input{width:100%;text-align:center;padding:0;padding-left:1rem;border-color:var(--bs-border-color)}::ng-deep .air-datepicker .air-datepicker-time .air-datepicker-time--sliders .air-datepicker-time--row .form-control:focus{box-shadow:unset!important}::ng-deep .air-datepicker.-inline-.-only-timepicker-{border:0;padding:0!important}::ng-deep .air-datepicker.-inline-.-only-timepicker- .air-datepicker--time{border:0;padding:0!important}::ng-deep .air-datepicker.-inline-.-only-timepicker- .air-datepicker-time{padding:0!important}:host{display:block}:host .quang-date-inline-hidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:0!important;border:0!important;opacity:0!important;pointer-events:none!important;overflow:hidden!important;white-space:nowrap!important;clip:rect(0 0 0 0)!important;clip-path:inset(50%)!important}:host .input-date-container{display:flex}:host input{flex:1}:host input.with-button-calendar{border-top-right-radius:0;border-bottom-right-radius:0}:host input:disabled{border-radius:var(--bs-border-radius)}:host .btn-outline-calendar{border-left:0;border-top-left-radius:0;border-bottom-left-radius:0;min-width:unset;display:flex;border-color:var(--bs-border-color)}:host .border-danger{border-color:var(--bs-form-invalid-border-color)}:host .border-success{border-color:var(--bs-form-valid-border-color)}\n"] }]
545
+ }], ctorParameters: () => [], propDecorators: { dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], activeLanguageOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeLanguageOverride", required: false }] }], timepicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "timepicker", required: false }] }], invalidDateMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidDateMessage", required: false }] }], showOnlyTimepicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOnlyTimepicker", required: false }] }], minHour: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHour", required: false }] }], maxHour: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHour", required: false }] }], minMinute: [{ type: i0.Input, args: [{ isSignal: true, alias: "minMinute", required: false }] }], maxMinute: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxMinute", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], showInline: [{ type: i0.Input, args: [{ isSignal: true, alias: "showInline", required: false }] }], calendarClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "calendarClasses", required: false }] }], buttonClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonClass", required: false }] }], datepickerOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "datepickerOptions", required: false }] }], _inputForDate: [{ type: i0.ViewChild, args: ['inputForDate', { isSignal: true }] }], contentTemplate: [{ type: i0.ViewChild, args: ['calendarButton', { isSignal: true }] }], multipleDatesSeparator: [{ type: i0.Input, args: [{ isSignal: true, alias: "multipleDatesSeparator", required: false }] }], rangeSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeSelection", required: false }] }], searchTextDebounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTextDebounce", required: false }] }] } });
419
546
 
420
547
  /**
421
548
  * Generated bundle index. Do not edit.