@tacdaed/fragments 1.0.0-beta.0 → 1.0.0-beta.2

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 (248) hide show
  1. package/README.md +2 -18
  2. package/ng-package.json +25 -0
  3. package/package.json +22 -29
  4. package/src/lib/components/accordion/accordion.component.html +103 -0
  5. package/src/lib/components/accordion/accordion.component.scss +382 -0
  6. package/src/lib/components/accordion/accordion.component.spec.ts +147 -0
  7. package/src/lib/components/accordion/accordion.component.ts +211 -0
  8. package/src/lib/components/accordion/accordion.type.ts +82 -0
  9. package/src/lib/components/breadcrumb/breadcrumb.component.html +43 -0
  10. package/src/lib/components/breadcrumb/breadcrumb.component.scss +112 -0
  11. package/src/lib/components/breadcrumb/breadcrumb.component.spec.ts +33 -0
  12. package/src/lib/components/breadcrumb/breadcrumb.component.ts +103 -0
  13. package/src/lib/components/breadcrumb/breadcrumb.interface.ts +7 -0
  14. package/src/lib/components/button/button.component.html +57 -0
  15. package/src/lib/components/button/button.component.scss +445 -0
  16. package/src/lib/components/button/button.component.spec.ts +99 -0
  17. package/src/lib/components/button/button.component.ts +143 -0
  18. package/src/lib/components/button/button.type.ts +7 -0
  19. package/src/lib/components/card/card.component.html +44 -0
  20. package/src/lib/components/card/card.component.scss +114 -0
  21. package/src/lib/components/card/card.component.spec.ts +65 -0
  22. package/src/lib/components/card/card.component.ts +21 -0
  23. package/src/lib/components/card/card.type.ts +3 -0
  24. package/src/lib/components/code-block/code-block.component.html +55 -0
  25. package/src/lib/components/code-block/code-block.component.scss +122 -0
  26. package/src/lib/components/code-block/code-block.component.spec.ts +81 -0
  27. package/src/lib/components/code-block/code-block.component.ts +302 -0
  28. package/src/lib/components/code-block/code-block.interface.ts +28 -0
  29. package/src/lib/components/code-block/code-block.type.ts +73 -0
  30. package/src/lib/components/decorative/sparkle-field/sparkle-field.component.html +14 -0
  31. package/src/lib/components/decorative/sparkle-field/sparkle-field.component.scss +20 -0
  32. package/src/lib/components/decorative/sparkle-field/sparkle-field.component.spec.ts +38 -0
  33. package/src/lib/components/decorative/sparkle-field/sparkle-field.component.ts +181 -0
  34. package/src/lib/components/input/input-base.ts +187 -0
  35. package/src/lib/components/input/input-calendar/input-calendar.component.html +76 -0
  36. package/src/lib/components/input/input-calendar/input-calendar.component.scss +179 -0
  37. package/src/lib/components/input/input-calendar/input-calendar.component.spec.ts +44 -0
  38. package/src/lib/components/input/input-calendar/input-calendar.component.ts +299 -0
  39. package/src/lib/components/input/input-checkbox/input-checkbox.component.html +37 -0
  40. package/src/lib/components/input/input-checkbox/input-checkbox.component.scss +128 -0
  41. package/src/lib/components/input/input-checkbox/input-checkbox.component.spec.ts +43 -0
  42. package/src/lib/components/input/input-checkbox/input-checkbox.component.ts +112 -0
  43. package/src/lib/components/input/input-checkbox-group/input-checkbox-group.component.html +43 -0
  44. package/src/lib/components/input/input-checkbox-group/input-checkbox-group.component.scss +140 -0
  45. package/src/lib/components/input/input-checkbox-group/input-checkbox-group.component.spec.ts +62 -0
  46. package/src/lib/components/input/input-checkbox-group/input-checkbox-group.component.ts +136 -0
  47. package/src/lib/components/input/input-clock-picker/input-clock-picker.component.html +81 -0
  48. package/src/lib/components/input/input-clock-picker/input-clock-picker.component.scss +228 -0
  49. package/src/lib/components/input/input-clock-picker/input-clock-picker.component.spec.ts +62 -0
  50. package/src/lib/components/input/input-clock-picker/input-clock-picker.component.ts +178 -0
  51. package/src/lib/components/input/input-consts.ts +132 -0
  52. package/src/lib/components/input/input-date/input-date-validators.ts +41 -0
  53. package/src/lib/components/input/input-date/input-date.component.html +41 -0
  54. package/src/lib/components/input/input-date/input-date.component.scss +95 -0
  55. package/src/lib/components/input/input-date/input-date.component.spec.ts +43 -0
  56. package/src/lib/components/input/input-date/input-date.component.ts +359 -0
  57. package/src/lib/components/input/input-date-time/input-date-time.component.html +70 -0
  58. package/src/lib/components/input/input-date-time/input-date-time.component.scss +133 -0
  59. package/src/lib/components/input/input-date-time/input-date-time.component.spec.ts +36 -0
  60. package/src/lib/components/input/input-date-time/input-date-time.component.ts +387 -0
  61. package/src/lib/components/input/input-file-upload/input-file-upload.component.html +89 -0
  62. package/src/lib/components/input/input-file-upload/input-file-upload.component.scss +171 -0
  63. package/src/lib/components/input/input-file-upload/input-file-upload.component.spec.ts +43 -0
  64. package/src/lib/components/input/input-file-upload/input-file-upload.component.ts +351 -0
  65. package/src/lib/components/input/input-interface.ts +8 -0
  66. package/src/lib/components/input/input-number/input-number-validators.ts +0 -0
  67. package/src/lib/components/input/input-number/input-number.component.html +51 -0
  68. package/src/lib/components/input/input-number/input-number.component.scss +140 -0
  69. package/src/lib/components/input/input-number/input-number.component.spec.ts +44 -0
  70. package/src/lib/components/input/input-number/input-number.component.ts +343 -0
  71. package/src/lib/components/input/input-radio-group/input-radio-group.component.html +44 -0
  72. package/src/lib/components/input/input-radio-group/input-radio-group.component.scss +139 -0
  73. package/src/lib/components/input/input-radio-group/input-radio-group.component.spec.ts +58 -0
  74. package/src/lib/components/input/input-radio-group/input-radio-group.component.ts +132 -0
  75. package/src/lib/components/input/input-slider/input-slider.component.html +111 -0
  76. package/src/lib/components/input/input-slider/input-slider.component.scss +203 -0
  77. package/src/lib/components/input/input-slider/input-slider.component.spec.ts +46 -0
  78. package/src/lib/components/input/input-slider/input-slider.component.ts +410 -0
  79. package/src/lib/components/input/input-text/input-text-validators.ts +67 -0
  80. package/src/lib/components/input/input-text/input-text.component.html +71 -0
  81. package/src/lib/components/input/input-text/input-text.component.scss +118 -0
  82. package/src/lib/components/input/input-text/input-text.component.spec.ts +55 -0
  83. package/src/lib/components/input/input-text/input-text.component.ts +215 -0
  84. package/src/lib/components/input/input-time/input-time-validators.ts +42 -0
  85. package/src/lib/components/input/input-time/input-time.component.html +92 -0
  86. package/src/lib/components/input/input-time/input-time.component.scss +191 -0
  87. package/src/lib/components/input/input-time/input-time.component.spec.ts +39 -0
  88. package/src/lib/components/input/input-time/input-time.component.ts +691 -0
  89. package/src/lib/components/input/input-toggle-switch/input-toggle-switch.component.html +36 -0
  90. package/src/lib/components/input/input-toggle-switch/input-toggle-switch.component.scss +121 -0
  91. package/src/lib/components/input/input-toggle-switch/input-toggle-switch.component.spec.ts +54 -0
  92. package/src/lib/components/input/input-toggle-switch/input-toggle-switch.component.ts +117 -0
  93. package/src/lib/components/input/input-type.ts +18 -0
  94. package/src/lib/components/input/input-validation/input-validation.component.html +19 -0
  95. package/src/lib/components/input/input-validation/input-validation.component.scss +39 -0
  96. package/src/lib/components/input/input-validation/input-validation.component.spec.ts +45 -0
  97. package/src/lib/components/input/input-validation/input-validation.component.ts +13 -0
  98. package/src/lib/components/input/input.pipe.ts +14 -0
  99. package/src/lib/components/layout/container/container.component.html +1 -0
  100. package/src/lib/components/layout/container/container.component.scss +33 -0
  101. package/src/lib/components/layout/container/container.component.ts +32 -0
  102. package/src/lib/components/layout/container/container.type.ts +1 -0
  103. package/src/lib/components/layout/divider/divider.component.html +1 -0
  104. package/src/lib/components/layout/divider/divider.component.scss +60 -0
  105. package/src/lib/components/layout/divider/divider.component.ts +38 -0
  106. package/src/lib/components/layout/divider/divider.type.ts +2 -0
  107. package/src/lib/components/layout/section/section.component.html +21 -0
  108. package/src/lib/components/layout/section/section.component.scss +43 -0
  109. package/src/lib/components/layout/section/section.component.ts +33 -0
  110. package/src/lib/components/layout/section/section.type.ts +2 -0
  111. package/src/lib/components/layout/separator/separator.component.html +9 -0
  112. package/src/lib/components/layout/separator/separator.component.scss +52 -0
  113. package/src/lib/components/layout/separator/separator.component.ts +25 -0
  114. package/src/lib/components/layout/separator/separator.type.ts +1 -0
  115. package/src/lib/components/loader/content-blur/content-blur.component.html +13 -0
  116. package/src/lib/components/loader/content-blur/content-blur.component.scss +43 -0
  117. package/src/lib/components/loader/content-blur/content-blur.component.spec.ts +42 -0
  118. package/src/lib/components/loader/content-blur/content-blur.component.ts +34 -0
  119. package/src/lib/components/loader/loader.type.ts +2 -0
  120. package/src/lib/components/loader/progress-bar/progress-bar.component.html +26 -0
  121. package/src/lib/components/loader/progress-bar/progress-bar.component.scss +151 -0
  122. package/src/lib/components/loader/progress-bar/progress-bar.component.spec.ts +47 -0
  123. package/src/lib/components/loader/progress-bar/progress-bar.component.ts +28 -0
  124. package/src/lib/components/loader/progress-bar/progress-bar.type.ts +8 -0
  125. package/src/lib/components/loader/pulse-loader/pulse-loader.component.html +12 -0
  126. package/src/lib/components/loader/pulse-loader/pulse-loader.component.scss +202 -0
  127. package/src/lib/components/loader/pulse-loader/pulse-loader.component.spec.ts +55 -0
  128. package/src/lib/components/loader/pulse-loader/pulse-loader.component.ts +73 -0
  129. package/src/lib/components/loader/pulse-loader/pulse-loader.type.ts +6 -0
  130. package/src/lib/components/loader/skeleton-loader/skeleton-loader.component.html +13 -0
  131. package/src/lib/components/loader/skeleton-loader/skeleton-loader.component.scss +113 -0
  132. package/src/lib/components/loader/skeleton-loader/skeleton-loader.component.spec.ts +37 -0
  133. package/src/lib/components/loader/skeleton-loader/skeleton-loader.component.ts +51 -0
  134. package/src/lib/components/loader/skeleton-loader/skeleton-loader.type.ts +6 -0
  135. package/src/lib/components/loader/spinner/spinner.component.html +20 -0
  136. package/src/lib/components/loader/spinner/spinner.component.scss +137 -0
  137. package/src/lib/components/loader/spinner/spinner.component.spec.ts +43 -0
  138. package/src/lib/components/loader/spinner/spinner.component.ts +32 -0
  139. package/src/lib/components/loader/spinner/spinner.type.ts +6 -0
  140. package/src/lib/components/modal/modal.component.html +47 -0
  141. package/src/lib/components/modal/modal.component.scss +139 -0
  142. package/src/lib/components/modal/modal.component.spec.ts +60 -0
  143. package/src/lib/components/modal/modal.component.ts +83 -0
  144. package/src/lib/components/modal/modal.type.ts +9 -0
  145. package/src/lib/components/morph/blob-moph/blob-moprh.component.spec.ts +79 -0
  146. package/src/lib/components/morph/blob-moph/blob-moprh.component.ts +96 -0
  147. package/src/lib/components/morph/blob-moph/blob-morph.component.html +34 -0
  148. package/src/lib/components/morph/blob-moph/blob-morph.component.scss +7 -0
  149. package/src/lib/components/morph/morph.abstract.ts +13 -0
  150. package/src/lib/components/pagination/pagination.interface.ts +4 -0
  151. package/src/lib/components/pagination/small-pagination/small-pagination.component.html +61 -0
  152. package/src/lib/components/pagination/small-pagination/small-pagination.component.scss +187 -0
  153. package/src/lib/components/pagination/small-pagination/small-pagination.component.spec.ts +88 -0
  154. package/src/lib/components/pagination/small-pagination/small-pagination.component.ts +177 -0
  155. package/src/lib/components/selection-lists/multi-select/multi-select.component.html +170 -0
  156. package/src/lib/components/selection-lists/multi-select/multi-select.component.scss +312 -0
  157. package/src/lib/components/selection-lists/multi-select/multi-select.component.spec.ts +61 -0
  158. package/src/lib/components/selection-lists/multi-select/multi-select.component.ts +372 -0
  159. package/src/lib/components/selection-lists/selection-list/selection-list.component.html +125 -0
  160. package/src/lib/components/selection-lists/selection-list/selection-list.component.scss +267 -0
  161. package/src/lib/components/selection-lists/selection-list/selection-list.component.spec.ts +66 -0
  162. package/src/lib/components/selection-lists/selection-list/selection-list.component.ts +315 -0
  163. package/src/lib/components/selection-lists/selection-lists-base.ts +35 -0
  164. package/src/lib/components/selection-lists/selection-lists-const.ts +17 -0
  165. package/src/lib/components/selection-lists/selection-lists-interface.ts +7 -0
  166. package/src/lib/components/selection-lists/selection-lists.type.ts +1 -0
  167. package/src/lib/components/side-nav/side-nav.component.html +101 -0
  168. package/src/lib/components/side-nav/side-nav.component.scss +295 -0
  169. package/src/lib/components/side-nav/side-nav.component.spec.ts +0 -0
  170. package/src/lib/components/side-nav/side-nav.component.ts +18 -0
  171. package/src/lib/components/side-nav/side-nav.type.ts +28 -0
  172. package/src/lib/components/snackbar/snackbar.component.html +33 -0
  173. package/src/lib/components/snackbar/snackbar.component.scss +195 -0
  174. package/src/lib/components/snackbar/snackbar.component.ts +112 -0
  175. package/src/lib/components/snackbar/snackbar.type.ts +27 -0
  176. package/src/lib/components/status/chip/chip.component.html +51 -0
  177. package/src/lib/components/status/chip/chip.component.scss +149 -0
  178. package/src/lib/components/status/chip/chip.component.spec.ts +62 -0
  179. package/src/lib/components/status/chip/chip.component.ts +83 -0
  180. package/src/lib/components/status/chip/chip.type.ts +42 -0
  181. package/src/lib/components/status/directives/badge/badge.directive.spec.ts +60 -0
  182. package/src/lib/components/status/directives/badge/badge.directive.ts +190 -0
  183. package/src/lib/components/status/directives/badge/badge.interface.ts +19 -0
  184. package/src/lib/components/status/pill/pill.component.html +40 -0
  185. package/src/lib/components/status/pill/pill.component.scss +113 -0
  186. package/src/lib/components/status/pill/pill.component.spec.ts +47 -0
  187. package/src/lib/components/status/pill/pill.component.ts +83 -0
  188. package/src/lib/components/status/pill/pill.type.ts +42 -0
  189. package/src/lib/components/status/status.interface.ts +57 -0
  190. package/src/lib/components/status/status.type.ts +62 -0
  191. package/src/lib/components/status/tag/tag.component.html +39 -0
  192. package/src/lib/components/status/tag/tag.component.scss +140 -0
  193. package/src/lib/components/status/tag/tag.component.spec.ts +47 -0
  194. package/src/lib/components/status/tag/tag.component.ts +83 -0
  195. package/src/lib/components/status/tag/tag.type.ts +42 -0
  196. package/src/lib/components/stepper/stepper.component.html +83 -0
  197. package/src/lib/components/stepper/stepper.component.scss +196 -0
  198. package/src/lib/components/stepper/stepper.component.ts +482 -0
  199. package/src/lib/components/stepper/stepper.type.ts +60 -0
  200. package/src/lib/components/table/table.component.html +438 -0
  201. package/src/lib/components/table/table.component.scss +259 -0
  202. package/src/lib/components/table/table.component.spec.ts +117 -0
  203. package/src/lib/components/table/table.component.ts +215 -0
  204. package/src/lib/components/table/table.enum.ts +4 -0
  205. package/src/lib/components/table/table.function.ts +47 -0
  206. package/src/lib/components/table/table.interface.ts +143 -0
  207. package/src/lib/components/table/table.pipe.ts +62 -0
  208. package/src/lib/components/table/table.type.ts +15 -0
  209. package/src/lib/components/tabs/tabs.component.html +88 -0
  210. package/src/lib/components/tabs/tabs.component.scss +305 -0
  211. package/src/lib/components/tabs/tabs.component.spec.ts +94 -0
  212. package/src/lib/components/tabs/tabs.component.ts +282 -0
  213. package/src/lib/components/tabs/tabs.type.ts +81 -0
  214. package/src/lib/components/title-bar/title-bar.component.html +21 -0
  215. package/src/lib/components/title-bar/title-bar.component.scss +139 -0
  216. package/src/lib/components/title-bar/title-bar.component.spec.ts +44 -0
  217. package/src/lib/components/title-bar/title-bar.component.ts +13 -0
  218. package/src/lib/components/toast/toast.component.html +36 -0
  219. package/src/lib/components/toast/toast.component.scss +241 -0
  220. package/src/lib/components/toast/toast.component.ts +165 -0
  221. package/src/lib/components/toast/toast.type.ts +37 -0
  222. package/src/lib/components/toast-stack/toast-stack.component.html +30 -0
  223. package/src/lib/components/toast-stack/toast-stack.component.scss +35 -0
  224. package/src/lib/components/toast-stack/toast-stack.component.ts +51 -0
  225. package/src/lib/consts/country-prefix.ts +244 -0
  226. package/src/lib/directives/tooltip/popover.directive.ts +274 -0
  227. package/src/lib/directives/tooltip/tooltip.directive.spec.ts +86 -0
  228. package/src/lib/directives/tooltip/tooltip.directive.ts +234 -0
  229. package/src/lib/directives/tooltip/tooltip.interface.ts +29 -0
  230. package/src/lib/directives/tooltip/tooltip.type.ts +9 -0
  231. package/src/lib/interfaces/common.interfaces.ts +4 -0
  232. package/src/lib/pipes/chunk.pipe.ts +16 -0
  233. package/src/lib/pipes/safe-html.pipe.ts +14 -0
  234. package/src/lib/pipes/sanitize-html.pipe.ts +23 -0
  235. package/src/lib/types/base.types.ts +23 -0
  236. package/src/lib/types/common.types.ts +98 -0
  237. package/src/lib/types/form.types.ts +5 -0
  238. package/src/lib/utils/common.utils.ts +53 -0
  239. package/src/lib/utils/date.utils.ts +474 -0
  240. package/src/lib/utils/number.utils.ts +16 -0
  241. package/src/lib/utils/uuid.utils.ts +39 -0
  242. package/src/public-api.ts +114 -0
  243. package/tsconfig.lib.json +17 -0
  244. package/tsconfig.lib.prod.json +10 -0
  245. package/tsconfig.spec.json +9 -0
  246. package/fesm2022/fragments.mjs +0 -8928
  247. package/fesm2022/fragments.mjs.map +0 -1
  248. package/index.d.ts +0 -3929
@@ -0,0 +1,691 @@
1
+ import { NgClass, NgTemplateOutlet } from '@angular/common';
2
+ import { Component, Input } from '@angular/core';
3
+ import { Validators } from '@angular/forms';
4
+ import { TimeFormat } from '../../../types/common.types';
5
+ import { buildTimeFormatRegex, convertStringToTime, formatTimeToString } from '../../../utils/date.utils';
6
+ import { InputBase } from '../input-base';
7
+ import { DEFAULT_INPUT_TIME_ERROR_MESSAGES, DEFAULT_INPUT_TIME_WARNINGS_MESSAGES } from '../input-consts';
8
+ import { InputTimeMeridiem, InputTimePartial, InputTimeType } from '../input-type';
9
+ import { InputValidationComponent } from '../input-validation/input-validation.component';
10
+ import { InputRequiredLabelPipe } from '../input.pipe';
11
+ import { maxTime, minTime } from './input-time-validators';
12
+ import { InputTimeParts } from '../input-interface';
13
+
14
+ @Component({
15
+ selector: 'frg-input-time',
16
+ imports: [InputRequiredLabelPipe, InputValidationComponent, NgClass, NgTemplateOutlet],
17
+ templateUrl: './input-time.component.html',
18
+ styleUrl: './input-time.component.scss',
19
+ })
20
+ export class InputTimeComponent extends InputBase<string> {
21
+
22
+ /**
23
+ * @inheritdoc
24
+ */
25
+ @Input() public override label: string = '';
26
+ /**
27
+ * @inheritdoc
28
+ */
29
+ @Input() public override placeholder: string = '';
30
+ /**
31
+ * @inheritdoc
32
+ */
33
+ @Input() public override required: boolean = false;
34
+ /**
35
+ * @inheritdoc
36
+ */
37
+ @Input() public override showValidation: boolean = true;
38
+ /**
39
+ * @inheritdoc
40
+ */
41
+ @Input() public override errorMessages: Record<string, string> = {};
42
+ /**
43
+ * The type of the input element.
44
+ * Can be 'text', 'password', 'email', 'tel', etc.
45
+ * Defaults to 'text'.
46
+ *
47
+ * @see InputTextType
48
+ */
49
+ @Input() public type: InputTimeType = 'time';
50
+ /**
51
+ * Minimum year selectable in the calendar
52
+ */
53
+ @Input() public minTime: string = '00:00:00';
54
+ /**
55
+ * Maximum year selectable in the calendar
56
+ */
57
+ @Input() public maxTime: string = '23:59:59';
58
+ /**
59
+ * The format of time
60
+ */
61
+ @Input() public format: TimeFormat = 'HH:mm';
62
+ /**
63
+ * Enables strict mode for email validation.
64
+ * When set to true, the email validator will use a more stringent pattern.
65
+ * Defaults to false.
66
+ */
67
+ @Input() public validatorStrictMode: boolean = false;
68
+
69
+ /**
70
+ * Determines the actual input type to be used in the template.
71
+ * If the type is 'password', it toggles between 'text' and 'password'
72
+ * based on the showPassword property.
73
+ *
74
+ * @returns The input type as a string.
75
+ */
76
+ public get inputType(): string {
77
+ return this.type;
78
+ }
79
+
80
+ /**
81
+ * Minimum hour based on minTime
82
+ */
83
+ public get minHour(): number {
84
+ return Number(this.minTime.split(':')[0] || 0);
85
+ }
86
+
87
+ /**
88
+ * Maximum hour based on maxTime and format
89
+ */
90
+ public get maxHour(): number {
91
+ return Number(this.maxTime.split(':')[0] || (this.format.includes('HH') ? 23 : 12));
92
+ }
93
+
94
+ /**
95
+ * Minimum minute allowed based on `minTime`
96
+ */
97
+ public get minMinute(): number {
98
+ return Number(this.minTime.split(':')[1] || 0);
99
+ }
100
+
101
+ /**
102
+ * Maximum minute allowed based on `maxTime`
103
+ */
104
+ public get maxMinute(): number {
105
+ return Number(this.maxTime.split(':')[1] || 59);
106
+ }
107
+
108
+ /**
109
+ * Minimum second allowed based on `minTime`
110
+ */
111
+ public get minSecond(): number {
112
+ const parts = this.minTime.split(':');
113
+ return parts[2] ? Number(parts[2]) : 0;
114
+ }
115
+
116
+ /**
117
+ * Maximum second allowed based on `maxTime`
118
+ */
119
+ public get maxSecond(): number {
120
+ const parts = this.maxTime.split(':');
121
+ return parts[2] ? Number(parts[2]) : 59;
122
+ }
123
+
124
+ /** Internal store for warnings */
125
+ private warnings: Record<string, boolean> = {};
126
+
127
+ /**
128
+ * @inheritdoc
129
+ */
130
+ public override ngOnInit(): void {
131
+ super.ngOnInit();
132
+ this.initPlaceholder();
133
+ this.initValidators();
134
+ }
135
+
136
+ public onKeydown(event: Event) {
137
+ if (event) {
138
+ event.preventDefault();
139
+ }
140
+
141
+ const checkbox = document.getElementById(this.id + '-meridiem') as HTMLInputElement;
142
+ if (!checkbox) return;
143
+
144
+ checkbox.checked = !checkbox.checked;
145
+
146
+ const fakeEvent = { target: checkbox } as unknown as Event;
147
+ this.onInput(fakeEvent);
148
+ }
149
+
150
+ /**
151
+ * @inheritdoc
152
+ */
153
+ public override onBlur(event: FocusEvent): void {
154
+ const target = event.target as HTMLInputElement;
155
+ const partialTime = target.dataset['partialtime'] as InputTimePartial;
156
+ const inputValue = target.value.trim() || null;
157
+
158
+ this.value = inputValue ? this.mergePartialInput(partialTime, inputValue) : '';
159
+ this.validate(this.value);
160
+ this.updateInputsFromValue();
161
+ this.onTouched();
162
+ this.onChange(this.value);
163
+ }
164
+
165
+ /**
166
+ * @inheritdoc
167
+ */
168
+ public override onInput(event: Event): void {
169
+ const target = event.target as HTMLInputElement;
170
+ const partialTime = target.dataset['partialtime'] as InputTimePartial;
171
+
172
+ let inputValue: string | null;
173
+ if (partialTime === 'meridiem') {
174
+ inputValue = target.checked ? 'PM' : 'AM';
175
+ } else {
176
+ inputValue = target.value.trim() || null;
177
+ }
178
+
179
+ this.value = this.mergePartialInput(partialTime, inputValue);
180
+ this.updateInputsFromValue();
181
+ this.onTouched();
182
+ this.onChange(this.value);
183
+ }
184
+
185
+ /**
186
+ * Decrements a specific part of the time (hour, minute, or second)
187
+ * and updates the input value accordingly.
188
+ *
189
+ * @param partialTime - The time part to decrement: 'hour', 'minute', or 'second'.
190
+ */
191
+ protected decrease(partialTime: InputTimePartial): void {
192
+ const valueParts = this.parseValueToParts();
193
+
194
+ switch (partialTime) {
195
+ case 'hour':
196
+ valueParts.hour = this.decrementHour(valueParts.hour ?? this.minHour);
197
+ break;
198
+ case 'minute':
199
+ valueParts.minute = this.decrementMinute(valueParts.minute ?? this.minMinute);
200
+ break;
201
+ case 'second':
202
+ valueParts.second = this.decrementSecond(valueParts.second ?? this.minSecond);
203
+ break;
204
+ }
205
+
206
+ this.value = this.formatPartsToTimeString(valueParts);
207
+ this.updateInputsFromValue();
208
+ this.onTouched();
209
+ this.onChange(this.value);
210
+ }
211
+
212
+ /**
213
+ * Format value for input display
214
+ * @param value Date value
215
+ * @returns Formatted time string
216
+ */
217
+ protected formatTime(value: string | null, partialTime?: InputTimePartial): string {
218
+ if (value) {
219
+ return formatTimeToString(value, this.format, partialTime);
220
+ }
221
+
222
+ return '';
223
+ }
224
+
225
+ /**
226
+ * Increments a specific part of the time (hour, minute, or second)
227
+ * and updates the input value accordingly.
228
+ *
229
+ * @param partialTime - The time part to increment: 'hour', 'minute', or 'second'.
230
+ */
231
+ protected increase(partialTime: InputTimePartial): void {
232
+ const valueParts = this.parseValueToParts();
233
+
234
+ switch (partialTime) {
235
+ case 'hour':
236
+ valueParts.hour = this.incrementHour(valueParts.hour);
237
+ break;
238
+ case 'minute':
239
+ valueParts.minute = this.incrementMinute(valueParts.minute);
240
+ break;
241
+ case 'second':
242
+ valueParts.second = this.incrementSecond(valueParts.second);
243
+ break;
244
+ }
245
+
246
+ this.value = this.formatPartsToTimeString(valueParts);
247
+ this.updateInputsFromValue();
248
+ this.onTouched();
249
+ this.onChange(this.value);
250
+ }
251
+
252
+ /**
253
+ * @inheritdoc
254
+ */
255
+ protected updateView(_value: string | null): void {
256
+ // Handled by Angular binding
257
+ }
258
+
259
+ /**
260
+ * @inheritdoc
261
+ */
262
+ protected updateDisabledState(_isDisabled: boolean): void {
263
+ // Handled by Angular binding
264
+ }
265
+
266
+ /**
267
+ * @inheritdoc
268
+ */
269
+ protected override get errorList(): string[] {
270
+ if (!this.control?.errors) return [];
271
+
272
+ const errors = this.control.errors;
273
+ return Object.keys(errors)
274
+ .filter(key => !key.startsWith('warning_'))
275
+ .map(key => {
276
+ if (this.errorMessages[key]) {
277
+ return this.errorMessages[key];
278
+ }
279
+ let template = this.errorMessages[key] || DEFAULT_INPUT_TIME_ERROR_MESSAGES[key] || 'Invalid field.';
280
+
281
+ switch (key) {
282
+ case 'min':
283
+ template = template.replace('{{time}}', formatTimeToString(this.minTime, this.format));
284
+ break;
285
+ case 'max':
286
+ template = template.replace('{{time}}', formatTimeToString(this.maxTime, this.format));
287
+ break;
288
+ }
289
+
290
+ return template;
291
+ });
292
+ }
293
+
294
+ /**
295
+ * Returns dynamic warning messages, replacing {{limit}} with actual values.
296
+ */
297
+ protected override get warningList(): string[] {
298
+ return Object.keys(this.warnings).map(key => {
299
+ const template = DEFAULT_INPUT_TIME_WARNINGS_MESSAGES[key] || 'Check this value.';
300
+
301
+ let replacement: string | undefined;
302
+
303
+ switch (key) {
304
+ case 'format':
305
+ replacement = this.format;
306
+ break;
307
+ case 'min':
308
+ replacement = formatTimeToString(this.minTime, this.format);
309
+ break;
310
+ case 'max':
311
+ replacement = formatTimeToString(this.maxTime, this.format);
312
+ break;
313
+ }
314
+
315
+ if (replacement != null) {
316
+ const placeholder = key === 'format' ? '{{format}}' : '{{limit}}';
317
+ return template.replace(placeholder, replacement.toString());
318
+ }
319
+
320
+ return template;
321
+ });
322
+ }
323
+
324
+ /**
325
+ * Returns whether any warnings are active.
326
+ */
327
+ protected get hasWarning(): boolean {
328
+ return this.warningList.length > 0;
329
+ }
330
+
331
+ /**
332
+ * Adds a warning flag
333
+ * @param key Warning key
334
+ */
335
+ private addWarning(key: string): void {
336
+ this.warnings[key] = true;
337
+ }
338
+
339
+ /**
340
+ * Builds a formatted time string from the given time parts.
341
+ * - Undefined numeric parts are represented as "undefined".
342
+ * - Meridiem is always included if the format includes 'a'.
343
+ *
344
+ * @param parts Object containing hour, minute, second, and optional meridiem
345
+ * @returns Formatted time string according to the current format
346
+ */
347
+ private formatPartsToTimeString(parts: InputTimeParts): string {
348
+ const segments: string[] = [];
349
+
350
+ if (this.format.includes('HH') || this.format.includes('hh')) {
351
+ segments.push(parts.hour !== undefined ? String(parts.hour).padStart(2, '0') : 'undefined');
352
+ }
353
+ if (this.format.includes('mm')) {
354
+ segments.push(parts.minute !== undefined ? String(parts.minute).padStart(2, '0') : 'undefined');
355
+ }
356
+ if (this.format.includes('ss')) {
357
+ segments.push(parts.second !== undefined ? String(parts.second).padStart(2, '0') : 'undefined');
358
+ }
359
+
360
+ const timeString = segments.join(':');
361
+
362
+ if (this.format.includes('a')) {
363
+ const meridiem = parts.meridiem || 'AM';
364
+ return `${timeString} ${meridiem}`;
365
+ }
366
+
367
+ return timeString;
368
+ }
369
+
370
+ /**
371
+ * Clamps a numeric value between a minimum and maximum.
372
+ * @param value The number to clamp.
373
+ * @param min Minimum allowed value.
374
+ * @param max Maximum allowed value.
375
+ * @returns The clamped number, or undefined if input was undefined.
376
+ */
377
+ private clamp(value: number | undefined, min: number, max: number): number | undefined {
378
+ if (value === undefined) return undefined;
379
+ return Math.max(min, Math.min(max, value));
380
+ }
381
+
382
+ /**
383
+ * Decrement hour based on format (12h or 24h)
384
+ * @param hour Current hour
385
+ * @returns Decremented hour
386
+ */
387
+ private decrementHour(hour: number): number {
388
+ if (this.format.includes('a')) {
389
+ hour--;
390
+ if (hour < 1) {
391
+ hour = 12;
392
+ this.toggleMeridiem();
393
+ }
394
+ return hour;
395
+ } else {
396
+ hour--;
397
+ if (hour < this.minHour) return this.maxHour;
398
+ return hour;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Decrement minute with wrapping
404
+ * @param minute Current minute
405
+ * @returns Decremented minute
406
+ */
407
+ private decrementMinute(minute: number): number {
408
+ minute--;
409
+ if (minute < this.minMinute) return this.maxMinute;
410
+ return minute;
411
+ }
412
+
413
+ /**
414
+ * Decrement second with wrapping
415
+ * @param second Current second
416
+ * @returns Decremented second
417
+ */
418
+ private decrementSecond(second: number): number {
419
+ second--;
420
+ if (second < this.minSecond) return this.maxSecond;
421
+ return second;
422
+ }
423
+
424
+ /**
425
+ * Returns the current time parts extracted from the component's value.
426
+ * Numeric parts are converted to numbers; meridiem defaults to 'AM' if format includes 'a'.
427
+ * @returns An object containing hour, minute, second, and meridiem (if applicable).
428
+ */
429
+ private getCurrentParts(): InputTimeParts {
430
+ return {
431
+ hour: this.parseNumberOrUndefined(this.formatTime(this.value, 'hour')),
432
+ minute: this.parseNumberOrUndefined(this.formatTime(this.value, 'minute')),
433
+ second: this.parseNumberOrUndefined(this.formatTime(this.value, 'second')),
434
+ meridiem: this.format.includes('a')
435
+ ? ((this.formatTime(this.value, 'meridiem') as InputTimeMeridiem) || 'AM')
436
+ : undefined,
437
+ };
438
+ }
439
+
440
+ /**
441
+ * Initialize the placeholder based on the time format
442
+ */
443
+ private initPlaceholder() {
444
+ this.placeholder = 'HH:mm';
445
+ }
446
+
447
+ /**
448
+ * Increment hour based on format (12h or 24h)
449
+ * @param hour Current hour
450
+ * @returns Incremented hour
451
+ */
452
+ private incrementHour(hour: number | undefined): number {
453
+ if (hour === undefined) {
454
+ return Number(this.format.includes('a'));
455
+ }
456
+ if (this.format.includes('a')) {
457
+ hour++;
458
+ if (hour > 12) {
459
+ hour = 1;
460
+ this.toggleMeridiem();
461
+ }
462
+ return hour;
463
+ } else {
464
+ hour++;
465
+ if (hour > this.maxHour) return this.minHour;
466
+ return hour;
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Increment minute with wrapping
472
+ * @param minute Current minute
473
+ * @returns Incremented minute
474
+ */
475
+ private incrementMinute(minute: number | undefined): number {
476
+ if (minute === undefined) {
477
+ return 0;
478
+ }
479
+ minute++;
480
+ if (minute > this.maxMinute) return this.minMinute;
481
+ return minute;
482
+ }
483
+
484
+ /**
485
+ * Increment second with wrapping
486
+ * @param second Current second
487
+ * @returns Incremented second
488
+ */
489
+ private incrementSecond(second: number | undefined): number {
490
+ if (second === undefined) {
491
+ return 0;
492
+ }
493
+ second++;
494
+ if (second > this.maxSecond) return this.minSecond;
495
+ return second;
496
+ }
497
+
498
+ /**
499
+ * Initialize validators based on inputs
500
+ */
501
+ private initValidators(): void {
502
+ if (this.control) {
503
+ const validators = [];
504
+
505
+ if (this.required) {
506
+ validators.push(Validators.required);
507
+ }
508
+
509
+ if (this.minTime) {
510
+ validators.push(minTime(this.minTime));
511
+ }
512
+
513
+ if (this.maxTime) {
514
+ validators.push(maxTime(this.maxTime));
515
+ }
516
+
517
+ this.control.setValidators(validators);
518
+ this.control.updateValueAndValidity();
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Merges a partial time input into the current value and returns the resulting full time string.
524
+ * @param partial The time part being updated ('hour', 'minute', 'second', 'meridiem').
525
+ * @param partialValue The new value for the specified part.
526
+ * @returns The merged full time string.
527
+ */
528
+ private mergePartialInput(partial: InputTimePartial, partialValue: string | null): string {
529
+ const parts = this.getCurrentParts();
530
+ this.setPartialValue(parts, partial, partialValue);
531
+ return this.formatPartsToTimeString(parts);
532
+ }
533
+
534
+ /**
535
+ * Converts a string to a number or returns undefined if the value is invalid.
536
+ * @param value The string to parse.
537
+ * @returns The number value, or undefined if input is null, 'undefined', or NaN.
538
+ */
539
+ private parseNumberOrUndefined(value: string | null): number | undefined {
540
+ if (!value || value === 'undefined') return undefined;
541
+ const n = Number(value);
542
+ return isNaN(n) ? undefined : n;
543
+ }
544
+
545
+ /**
546
+ * Parse the current value to its components
547
+ * @returns An object containing hour, minute, second, and meridiem (if applicable)
548
+ */
549
+ private parseValueToParts(): InputTimeParts {
550
+ const result: InputTimeParts = {};
551
+ if (!this.value) return result;
552
+
553
+ let coreValue = this.value.trim();
554
+ let meridiem: InputTimeMeridiem | undefined;
555
+
556
+ const meridiemMatch = /\s?(AM|PM)$/i.exec(coreValue);
557
+ if (meridiemMatch) {
558
+ meridiem = meridiemMatch[1].toUpperCase() as InputTimeMeridiem;
559
+ coreValue = coreValue.replace(/\s?(AM|PM)$/i, '').trim();
560
+ }
561
+
562
+ const segments = coreValue.split(':').map(s => (s === 'undefined' ? undefined : s));
563
+
564
+ const tokenMap: [string, keyof InputTimeParts][] = [
565
+ ['HH', 'hour'],
566
+ ['hh', 'hour'],
567
+ ['mm', 'minute'],
568
+ ['ss', 'second'],
569
+ ];
570
+
571
+ let index = 0;
572
+ for (const [token, key] of tokenMap) {
573
+ if (this.format.includes(token)) {
574
+ const segment = segments[index++];
575
+ result[key as 'hour' | 'minute' | 'second'] =
576
+ segment !== undefined ? Number(segment) : undefined;
577
+ }
578
+ }
579
+
580
+ if (this.format.includes('a')) {
581
+ result.meridiem = meridiem;
582
+ }
583
+
584
+ return result;
585
+ }
586
+
587
+ /**
588
+ * Updates a specific part of a time parts object with a new partial value and clamps numeric values.
589
+ * @param parts The time parts object to update.
590
+ * @param partial The part to update ('hour', 'minute', 'second', 'meridiem').
591
+ * @param partialValue The new value for the part.
592
+ */
593
+ private setPartialValue(parts: InputTimeParts, partial: InputTimePartial, partialValue: string | null): void {
594
+ switch (partial) {
595
+ case 'hour':
596
+ parts.hour = partialValue !== null ? Number(partialValue) : undefined;
597
+ break;
598
+ case 'minute':
599
+ parts.minute = partialValue !== null ? Number(partialValue) : 0;
600
+ break;
601
+ case 'second':
602
+ parts.second = partialValue !== null ? Number(partialValue) : 0;
603
+ break;
604
+ case 'meridiem':
605
+ parts.meridiem = partialValue === 'AM' || partialValue === 'PM' ? partialValue : 'AM';
606
+ break;
607
+ }
608
+
609
+ parts.hour = this.clamp(parts.hour, this.minHour, this.maxHour);
610
+ parts.minute = this.clamp(parts.minute, this.minMinute, this.maxMinute);
611
+ parts.second = this.clamp(parts.second, this.minSecond, this.maxSecond);
612
+ }
613
+
614
+ /**
615
+ * Toggle the meridiem (AM/PM) in the current value
616
+ * This is used when incrementing hours in 12-hour format.
617
+ */
618
+ private toggleMeridiem(): void {
619
+ if (!this.value) return;
620
+ const parts = this.value.split(' ');
621
+ if (parts[1] === 'AM') parts[1] = 'PM';
622
+ else parts[1] = 'AM';
623
+ this.value = parts.join(' ');
624
+ }
625
+
626
+ /**
627
+ * Update individual input fields based on the current full time value
628
+ * This ensures that each part (hour, minute, second, meridiem) reflects the current value
629
+ * and is properly formatted.
630
+ */
631
+ private updateInputsFromValue(): void {
632
+ const hoursInput = document.getElementById(this.id + '-hour') as HTMLInputElement;
633
+ const minutesInput = document.getElementById(this.id + '-minute') as HTMLInputElement;
634
+ const secondsInput = document.getElementById(this.id + '-second') as HTMLInputElement;
635
+ const meridiemInput = document.getElementById(this.id + '-meridiem') as HTMLInputElement;
636
+
637
+ const safeValue = (part: InputTimePartial): string => {
638
+ const formatted = this.formatTime(this.value, part);
639
+ return formatted && !['HH', 'mm', 'ss'].includes(formatted) ? formatted : '';
640
+ };
641
+
642
+ if (hoursInput) hoursInput.value = safeValue('hour');
643
+ if (minutesInput) minutesInput.value = safeValue('minute');
644
+ if (secondsInput) secondsInput.value = safeValue('second');
645
+ if (meridiemInput) meridiemInput.value = safeValue('meridiem');
646
+ }
647
+
648
+ /**
649
+ * Validate the input value against the specified format
650
+ * @param value Input value
651
+ */
652
+ private validate(value: string): void {
653
+ this.removeWarning('format');
654
+ this.removeWarning('min');
655
+ this.removeWarning('max');
656
+
657
+ if (!value) return;
658
+
659
+ if (this.format) {
660
+ const regex = buildTimeFormatRegex(this.format);
661
+ if (!regex.test(value)) {
662
+ this.addWarning('format');
663
+ return;
664
+ }
665
+ }
666
+
667
+ const valueSeconds = convertStringToTime(value, this.format);
668
+ if (valueSeconds === null) {
669
+ this.addWarning('invalidFormat');
670
+ return;
671
+ }
672
+
673
+ const minSeconds = this.minTime ? convertStringToTime(this.minTime, this.format) : null;
674
+ const maxSeconds = this.maxTime ? convertStringToTime(this.maxTime, this.format) : null;
675
+
676
+ if (minSeconds !== null && valueSeconds < minSeconds) {
677
+ this.addWarning('min');
678
+ }
679
+ if (maxSeconds !== null && valueSeconds > maxSeconds) {
680
+ this.addWarning('max');
681
+ }
682
+ }
683
+
684
+ /**
685
+ * Remove a warning flag
686
+ * @param key Warning key
687
+ */
688
+ private removeWarning(key: string): void {
689
+ delete this.warnings[key];
690
+ }
691
+ }