quang 20.5.2-0 → 21.0.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.
Files changed (63) hide show
  1. package/fesm2022/quang-auth.mjs +20 -20
  2. package/fesm2022/quang-auth.mjs.map +1 -1
  3. package/fesm2022/quang-components-autocomplete.mjs +3 -3
  4. package/fesm2022/quang-components-autocomplete.mjs.map +1 -1
  5. package/fesm2022/quang-components-checkbox.mjs +3 -3
  6. package/fesm2022/quang-components-checkbox.mjs.map +1 -1
  7. package/fesm2022/quang-components-date.mjs +219 -134
  8. package/fesm2022/quang-components-date.mjs.map +1 -1
  9. package/fesm2022/quang-components-input.mjs +3 -3
  10. package/fesm2022/quang-components-input.mjs.map +1 -1
  11. package/fesm2022/quang-components-paginator.mjs +14 -14
  12. package/fesm2022/quang-components-paginator.mjs.map +1 -1
  13. package/fesm2022/quang-components-radio-group.mjs +3 -3
  14. package/fesm2022/quang-components-radio-group.mjs.map +1 -1
  15. package/fesm2022/quang-components-select.mjs +3 -3
  16. package/fesm2022/quang-components-select.mjs.map +1 -1
  17. package/fesm2022/quang-components-shared.mjs +8 -8
  18. package/fesm2022/quang-components-shared.mjs.map +1 -1
  19. package/fesm2022/quang-components-table.mjs +4 -4
  20. package/fesm2022/quang-components-table.mjs.map +1 -1
  21. package/fesm2022/quang-components-wysiwyg.mjs +3 -3
  22. package/fesm2022/quang-components-wysiwyg.mjs.map +1 -1
  23. package/fesm2022/quang-device.mjs +3 -3
  24. package/fesm2022/quang-device.mjs.map +1 -1
  25. package/fesm2022/quang-loader.mjs +6 -6
  26. package/fesm2022/quang-loader.mjs.map +1 -1
  27. package/fesm2022/quang-overlay-modal.mjs +6 -6
  28. package/fesm2022/quang-overlay-modal.mjs.map +1 -1
  29. package/fesm2022/quang-overlay-popover.mjs +7 -7
  30. package/fesm2022/quang-overlay-popover.mjs.map +1 -1
  31. package/fesm2022/quang-overlay-shared.mjs +9 -9
  32. package/fesm2022/quang-overlay-shared.mjs.map +1 -1
  33. package/fesm2022/quang-overlay-toast.mjs +6 -6
  34. package/fesm2022/quang-overlay-toast.mjs.map +1 -1
  35. package/fesm2022/quang-overlay-tooltip.mjs +7 -7
  36. package/fesm2022/quang-overlay-tooltip.mjs.map +1 -1
  37. package/fesm2022/quang-translation.mjs +6 -6
  38. package/fesm2022/quang-translation.mjs.map +1 -1
  39. package/package.json +26 -26
  40. package/{components/date/index.d.ts → types/quang-components-date.d.ts} +8 -1
  41. package/{components/shared/index.d.ts → types/quang-components-shared.d.ts} +1 -1
  42. /package/{auth/mobile/index.d.ts → types/quang-auth-mobile.d.ts} +0 -0
  43. /package/{auth/index.d.ts → types/quang-auth.d.ts} +0 -0
  44. /package/{components/autocomplete/index.d.ts → types/quang-components-autocomplete.d.ts} +0 -0
  45. /package/{components/checkbox/index.d.ts → types/quang-components-checkbox.d.ts} +0 -0
  46. /package/{components/input/index.d.ts → types/quang-components-input.d.ts} +0 -0
  47. /package/{components/paginator/index.d.ts → types/quang-components-paginator.d.ts} +0 -0
  48. /package/{components/radio-group/index.d.ts → types/quang-components-radio-group.d.ts} +0 -0
  49. /package/{components/select/index.d.ts → types/quang-components-select.d.ts} +0 -0
  50. /package/{components/table/index.d.ts → types/quang-components-table.d.ts} +0 -0
  51. /package/{components/wysiwyg/index.d.ts → types/quang-components-wysiwyg.d.ts} +0 -0
  52. /package/{data-handling/index.d.ts → types/quang-data-handling.d.ts} +0 -0
  53. /package/{device/index.d.ts → types/quang-device.d.ts} +0 -0
  54. /package/{forms/index.d.ts → types/quang-forms.d.ts} +0 -0
  55. /package/{loader/index.d.ts → types/quang-loader.d.ts} +0 -0
  56. /package/{overlay/modal/index.d.ts → types/quang-overlay-modal.d.ts} +0 -0
  57. /package/{overlay/popover/index.d.ts → types/quang-overlay-popover.d.ts} +0 -0
  58. /package/{overlay/shared/index.d.ts → types/quang-overlay-shared.d.ts} +0 -0
  59. /package/{overlay/toast/index.d.ts → types/quang-overlay-toast.d.ts} +0 -0
  60. /package/{overlay/tooltip/index.d.ts → types/quang-overlay-tooltip.d.ts} +0 -0
  61. /package/{shared/index.d.ts → types/quang-shared.d.ts} +0 -0
  62. /package/{translation/index.d.ts → types/quang-translation.d.ts} +0 -0
  63. /package/{index.d.ts → types/quang.d.ts} +0 -0
@@ -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, 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}
@@ -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(() => {
@@ -107,122 +114,147 @@ class QuangDateComponent extends QuangBaseComponent {
107
114
  });
108
115
  }
109
116
  setupCalendar() {
110
- if (this._inputForDate()?.nativeElement) {
111
- let currentValue = this._value();
112
- let targetDate;
113
- if (currentValue && typeof currentValue === 'string') {
114
- if (!this.showTimepicker()) {
115
- currentValue = currentValue.split('T')[0];
116
- }
117
- 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];
118
132
  }
119
- else if (currentValue && typeof currentValue === 'object') {
120
- targetDate = [];
121
- if (currentValue.dateFrom) {
122
- let targetDateFrom = currentValue.dateFrom;
123
- if (!this.showTimepicker()) {
124
- targetDateFrom = currentValue.dateFrom.split('T')[0];
125
- }
126
- targetDate.push(targetDateFrom);
127
- }
128
- if (currentValue.dateTo) {
129
- let targetDateTo = currentValue.dateTo;
130
- if (!this.showTimepicker()) {
131
- targetDateTo = currentValue.dateTo.split('T')[0];
132
- }
133
- targetDate.push(targetDateTo);
134
- }
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);
142
+ }
143
+ if (currentValue.dateTo) {
144
+ const targetDateTo = this.showTimepicker() ? currentValue.dateTo : currentValue.dateTo.split('T')[0];
145
+ targetDate.push(targetDateTo);
135
146
  }
136
- this.setCalendarPosition();
137
- const airDatepickerOpts = {
138
- autoClose: true,
139
- showEvent: 'click',
140
- classes: this.calendarClasses(),
141
- dateFormat: this.dateFormat(),
142
- inline: this.showInline(),
143
- isMobile: false,
144
- multipleDatesSeparator: this.multipleDatesSeparator(),
145
- range: this.rangeSelection(),
146
- timepicker: this.showTimepicker(),
147
- onlyTimepicker: this.showOnlyTimepicker(),
148
- timeFormat: this.timeFormat(),
149
- minHours: this.minHour(),
150
- maxHours: this.maxHour(),
151
- minMinutes: this.minMinute(),
152
- maxMinutes: this.maxMinute(),
153
- minDate: this.minDate(),
154
- maxDate: this.maxDate(),
155
- toggleSelected: false,
156
- multipleDates: false,
157
- selectedDates: targetDate,
158
- position: this.targetPosition(),
159
- locale: this.getLocale(),
160
- onSelect: ({ date }) => {
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(() => {
161
182
  this._shouldRefocusInputOnHide.set(true);
162
- if (!Array.isArray(date)) {
163
- let selectTargetDate = date;
164
- if (!this.showTimepicker()) {
165
- selectTargetDate = this.dateToUtc(date);
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;
166
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);
167
199
  this.onChangedHandler(selectTargetDate.toISOString());
168
200
  }
169
201
  if (this.showInline()) {
170
- this.onHideCalendar();
171
- }
172
- },
173
- onHide: (isAnimationComplete) => {
174
- if (isAnimationComplete) {
175
- this.onHideCalendar();
176
- }
177
- },
178
- ...(this.datepickerOptions() ?? {}),
179
- onShow: (isAnimationComplete) => {
180
- const datepicker = this._airDatepickerInstance()?.$datepicker;
181
- if (datepicker) {
182
- datepicker.onmouseenter = () => {
183
- this.isMouseInsideCalendar.set(true);
184
- };
185
- datepicker.onmouseleave = () => {
186
- this.isMouseInsideCalendar.set(false);
187
- };
188
- datepicker.onkeydown = (evt) => {
189
- if (evt.key !== 'Enter' || this.showInline()) {
190
- return;
191
- }
192
- // Air-datepicker keyboard selection doesn't always auto-close reliably.
193
- // Close on Enter to keep behavior consistent with autoClose and focus expectations.
194
- this._shouldRefocusInputOnHide.set(true);
195
- setTimeout(() => this._airDatepickerInstance()?.hide(), 0);
196
- };
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();
197
205
  }
198
- if (isAnimationComplete || !this.showTimepicker()) {
199
- return;
200
- }
201
- this.setupTimepicker();
202
- },
203
- };
204
- if (this._airDatepickerInstance()) {
205
- if (this._airDatepickerInstance()?.visible) {
206
- this._airDatepickerInstance()?.update(airDatepickerOpts);
207
- }
208
- else {
209
- this._airDatepickerInstance()?.update(airDatepickerOpts, { silent: true });
206
+ });
207
+ userOnSelect?.(args);
208
+ },
209
+ onHide: (isAnimationComplete) => {
210
+ if (isAnimationComplete) {
211
+ this.onHideCalendar();
210
212
  }
211
- if (!targetDate) {
212
- this._airDatepickerInstance()?.setFocusDate(false);
213
- 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
+ };
214
224
  }
215
- else {
216
- this._airDatepickerInstance()?.selectDate(targetDate, { updateTime: true });
225
+ if (isAnimationComplete || !this.showTimepicker()) {
226
+ return;
217
227
  }
228
+ this.setupTimepicker();
229
+ userOnShow?.(isAnimationComplete);
230
+ },
231
+ };
232
+ if (this._airDatepickerInstance()) {
233
+ if (this._airDatepickerInstance()?.visible) {
234
+ this._airDatepickerInstance()?.update(airDatepickerOpts);
218
235
  }
219
236
  else {
220
- this._airDatepickerInstance.set(new AirDatepicker(this._inputForDate()?.nativeElement, airDatepickerOpts));
237
+ this._airDatepickerInstance()?.update(airDatepickerOpts, { silent: true });
221
238
  }
222
- if (this.showInline()) {
223
- this.setupTimepicker();
239
+ if (targetDate) {
240
+ this._airDatepickerInstance()?.selectDate(targetDate, { updateTime: true, silent: true });
241
+ }
242
+ else {
243
+ this._airDatepickerInstance()?.setFocusDate(false);
244
+ this._airDatepickerInstance()?.clear({ silent: true });
224
245
  }
225
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();
257
+ }
226
258
  }
227
259
  onChangeText($event) {
228
260
  const value = $event.target?.value;
@@ -230,14 +262,23 @@ class QuangDateComponent extends QuangBaseComponent {
230
262
  // TODO: check format for DateRange
231
263
  if (value.length === this.valueFormat().length && isMatch(value, this.valueFormat())) {
232
264
  this.onChangedHandler(this.setupInputStringToDate(value).toISOString());
265
+ if (this.showInline()) {
266
+ this.propagateValueToControl();
267
+ }
233
268
  }
234
269
  }
235
270
  else {
236
271
  this.onChangedHandler(value);
272
+ if (this.showInline()) {
273
+ this.propagateValueToControl();
274
+ }
237
275
  }
238
276
  }
239
277
  onBlurHandler() {
240
278
  super.onBlurHandler();
279
+ if (this.showInline()) {
280
+ return;
281
+ }
241
282
  if (this.isMouseOutsideCalendar() && this._airDatepickerInstance()?.visible) {
242
283
  this._airDatepickerInstance()?.hide();
243
284
  }
@@ -247,9 +288,9 @@ class QuangDateComponent extends QuangBaseComponent {
247
288
  if (value.length !== targetValueFormat.length) {
248
289
  targetValueFormat = targetValueFormat.replace('yyyy', 'yy');
249
290
  }
250
- let targetDate = parse(value, targetValueFormat, new Date());
291
+ const targetDate = parse(value, targetValueFormat, new Date());
251
292
  if (!this.showTimepicker()) {
252
- targetDate = this.dateToUtc(targetDate);
293
+ return this.dateToUtc(targetDate);
253
294
  }
254
295
  return targetDate;
255
296
  }
@@ -288,6 +329,42 @@ class QuangDateComponent extends QuangBaseComponent {
288
329
  }
289
330
  this._value.set(targetDate);
290
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
+ }
291
368
  onHideCalendar() {
292
369
  const valueInput = this._inputForDate()?.nativeElement.value;
293
370
  let value = valueInput;
@@ -296,18 +373,14 @@ class QuangDateComponent extends QuangBaseComponent {
296
373
  const [dateFrom, dateTo] = valueInput.split(this.multipleDatesSeparator());
297
374
  value.dateFrom = dateFrom ?? '';
298
375
  value.dateTo = dateTo ?? '';
299
- if (!value.dateFrom || !this.checkDateMatch(value.dateFrom)) {
300
- value.dateFrom = null;
301
- }
302
- else {
303
- value.dateFrom = this.setupInputStringToDate(value.dateFrom).toISOString();
304
- }
305
- if (!value.dateTo || !this.checkDateMatch(value.dateTo)) {
306
- value.dateTo = null;
307
- }
308
- else {
309
- value.dateTo = this.setupInputStringToDate(value.dateTo).toISOString();
310
- }
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();
311
384
  this.onChangedHandler(value);
312
385
  }
313
386
  else if (this.checkDateMatch(value)) {
@@ -316,11 +389,9 @@ class QuangDateComponent extends QuangBaseComponent {
316
389
  else {
317
390
  this.onChangedHandler(null);
318
391
  }
319
- if (this.formControl()?.getRawValue() !== this._value()) {
320
- super.onChangedHandler(this._value());
321
- }
322
- else if (this.onTouched) {
323
- this.onTouched();
392
+ this.propagateValueToControl();
393
+ if (this.showInline()) {
394
+ return;
324
395
  }
325
396
  // Only focus the input when the user actually interacted with the calendar.
326
397
  // Avoids infinite focus loop when tabbing between multiple datepickers.
@@ -340,6 +411,9 @@ class QuangDateComponent extends QuangBaseComponent {
340
411
  return format(val, this.valueFormat());
341
412
  }
342
413
  if (val && typeof val === 'object') {
414
+ if (!val.dateFrom && !val.dateTo) {
415
+ return '';
416
+ }
343
417
  let dateFromFormat = '';
344
418
  let dateToFormat = '';
345
419
  if (val.dateFrom) {
@@ -367,10 +441,10 @@ class QuangDateComponent extends QuangBaseComponent {
367
441
  if (this._isDisabled()) {
368
442
  return;
369
443
  }
370
- const dp = this._airDatepickerInstance();
371
- if (event.key === 'Escape' && dp?.visible) {
444
+ const datepickerInstance = this._airDatepickerInstance();
445
+ if (event.key === 'Escape' && datepickerInstance?.visible) {
372
446
  event.preventDefault();
373
- dp.hide();
447
+ datepickerInstance.hide();
374
448
  return;
375
449
  }
376
450
  if (event.key === 'Enter' || event.key === 'ArrowDown') {
@@ -414,10 +488,6 @@ class QuangDateComponent extends QuangBaseComponent {
414
488
  // convert to UTC time removing the timezone
415
489
  return new Date(date.getTime() - date.getTimezoneOffset() * 60000);
416
490
  }
417
- dateToLocal(date) {
418
- // convert to local time adding the timezone
419
- return new Date(date.getTime() + date.getTimezoneOffset() * 60000);
420
- }
421
491
  setCalendarPosition() {
422
492
  const windowInnerHeight = window.innerHeight;
423
493
  const inputBoundingClientRect = this._inputForDate()?.nativeElement.getBoundingClientRect();
@@ -430,8 +500,23 @@ class QuangDateComponent extends QuangBaseComponent {
430
500
  }
431
501
  }
432
502
  setupTimepicker() {
433
- const timepicker = document.getElementsByClassName('air-datepicker-time')?.[0];
434
- 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)) {
435
520
  const inputs = timepicker.getElementsByTagName('input');
436
521
  for (const input of Array.from(inputs)) {
437
522
  input.setAttribute('type', 'number');
@@ -441,7 +526,7 @@ class QuangDateComponent extends QuangBaseComponent {
441
526
  evt.stopImmediatePropagation();
442
527
  };
443
528
  input.onblur = () => {
444
- if (this.isMouseOutsideCalendar()) {
529
+ if (!this.showInline() && this.isMouseOutsideCalendar()) {
445
530
  this._airDatepickerInstance()?.hide();
446
531
  }
447
532
  };
@@ -451,12 +536,12 @@ class QuangDateComponent extends QuangBaseComponent {
451
536
  checkDateMatch(date) {
452
537
  return isMatch(date, this.valueFormat()) || isMatch(date, this.valueFormat().replace('yyyy', 'yy'));
453
538
  }
454
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: QuangDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
455
- 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 (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.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 }); }
539
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: QuangDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
540
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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 }); }
456
541
  }
457
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: QuangDateComponent, decorators: [{
542
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: QuangDateComponent, decorators: [{
458
543
  type: Component,
459
- 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 (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.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"] }]
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"] }]
460
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 }] }] } });
461
546
 
462
547
  /**