@keenthemes/ktui 1.1.0 → 1.1.1

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 (222) hide show
  1. package/README.md +0 -27
  2. package/dist/ktui.js +6790 -14063
  3. package/dist/ktui.min.js +1 -1
  4. package/dist/ktui.min.js.map +1 -1
  5. package/dist/styles.css +1132 -2705
  6. package/lib/cjs/components/datatable/__tests__/pagination-reset.test.js +596 -0
  7. package/lib/cjs/components/datatable/__tests__/pagination-reset.test.js.map +1 -0
  8. package/lib/cjs/components/datatable/__tests__/race-conditions.test.js +548 -0
  9. package/lib/cjs/components/datatable/__tests__/race-conditions.test.js.map +1 -0
  10. package/lib/cjs/components/datatable/__tests__/setup.js +63 -0
  11. package/lib/cjs/components/datatable/__tests__/setup.js.map +1 -0
  12. package/lib/cjs/components/datatable/datatable.js +92 -30
  13. package/lib/cjs/components/datatable/datatable.js.map +1 -1
  14. package/lib/cjs/index.js +1 -10
  15. package/lib/cjs/index.js.map +1 -1
  16. package/lib/esm/components/datatable/__tests__/pagination-reset.test.js +594 -0
  17. package/lib/esm/components/datatable/__tests__/pagination-reset.test.js.map +1 -0
  18. package/lib/esm/components/datatable/__tests__/race-conditions.test.js +546 -0
  19. package/lib/esm/components/datatable/__tests__/race-conditions.test.js.map +1 -0
  20. package/lib/esm/components/datatable/__tests__/setup.js +58 -0
  21. package/lib/esm/components/datatable/__tests__/setup.js.map +1 -0
  22. package/lib/esm/components/datatable/datatable.js +92 -30
  23. package/lib/esm/components/datatable/datatable.js.map +1 -1
  24. package/lib/esm/index.js +0 -7
  25. package/lib/esm/index.js.map +1 -1
  26. package/package.json +7 -9
  27. package/src/components/alert/alert.css +188 -429
  28. package/src/components/datatable/__tests__/pagination-reset.test.ts +657 -0
  29. package/src/components/datatable/__tests__/race-conditions.test.ts +455 -0
  30. package/src/components/datatable/__tests__/setup.ts +67 -0
  31. package/src/components/datatable/datatable.ts +66 -11
  32. package/src/components/input/input.css +0 -1
  33. package/src/components/select/select.css +0 -1
  34. package/src/components/select/variants.css +4 -0
  35. package/src/components/textarea/textarea.css +0 -1
  36. package/src/index.ts +0 -10
  37. package/styles.css +0 -1
  38. package/lib/cjs/components/alert/alert.js +0 -1025
  39. package/lib/cjs/components/alert/alert.js.map +0 -1
  40. package/lib/cjs/components/alert/index.js +0 -20
  41. package/lib/cjs/components/alert/index.js.map +0 -1
  42. package/lib/cjs/components/alert/templates.js +0 -120
  43. package/lib/cjs/components/alert/templates.js.map +0 -1
  44. package/lib/cjs/components/alert/types.js +0 -7
  45. package/lib/cjs/components/alert/types.js.map +0 -1
  46. package/lib/cjs/components/datepicker/config/config.js +0 -42
  47. package/lib/cjs/components/datepicker/config/config.js.map +0 -1
  48. package/lib/cjs/components/datepicker/config/index.js +0 -24
  49. package/lib/cjs/components/datepicker/config/index.js.map +0 -1
  50. package/lib/cjs/components/datepicker/config/interfaces.js +0 -7
  51. package/lib/cjs/components/datepicker/config/interfaces.js.map +0 -1
  52. package/lib/cjs/components/datepicker/config/types.js +0 -7
  53. package/lib/cjs/components/datepicker/config/types.js.map +0 -1
  54. package/lib/cjs/components/datepicker/core/event-manager.js +0 -135
  55. package/lib/cjs/components/datepicker/core/event-manager.js.map +0 -1
  56. package/lib/cjs/components/datepicker/core/focus-manager.js +0 -167
  57. package/lib/cjs/components/datepicker/core/focus-manager.js.map +0 -1
  58. package/lib/cjs/components/datepicker/core/helpers.js +0 -219
  59. package/lib/cjs/components/datepicker/core/helpers.js.map +0 -1
  60. package/lib/cjs/components/datepicker/core/index.js +0 -25
  61. package/lib/cjs/components/datepicker/core/index.js.map +0 -1
  62. package/lib/cjs/components/datepicker/core/unified-state-manager.js +0 -394
  63. package/lib/cjs/components/datepicker/core/unified-state-manager.js.map +0 -1
  64. package/lib/cjs/components/datepicker/datepicker.js +0 -2252
  65. package/lib/cjs/components/datepicker/datepicker.js.map +0 -1
  66. package/lib/cjs/components/datepicker/index.js +0 -24
  67. package/lib/cjs/components/datepicker/index.js.map +0 -1
  68. package/lib/cjs/components/datepicker/ui/index.js +0 -23
  69. package/lib/cjs/components/datepicker/ui/index.js.map +0 -1
  70. package/lib/cjs/components/datepicker/ui/input/dropdown.js +0 -489
  71. package/lib/cjs/components/datepicker/ui/input/dropdown.js.map +0 -1
  72. package/lib/cjs/components/datepicker/ui/input/index.js +0 -23
  73. package/lib/cjs/components/datepicker/ui/input/index.js.map +0 -1
  74. package/lib/cjs/components/datepicker/ui/input/segmented-input.js +0 -640
  75. package/lib/cjs/components/datepicker/ui/input/segmented-input.js.map +0 -1
  76. package/lib/cjs/components/datepicker/ui/renderers/calendar.js +0 -446
  77. package/lib/cjs/components/datepicker/ui/renderers/calendar.js.map +0 -1
  78. package/lib/cjs/components/datepicker/ui/renderers/footer.js +0 -42
  79. package/lib/cjs/components/datepicker/ui/renderers/footer.js.map +0 -1
  80. package/lib/cjs/components/datepicker/ui/renderers/header.js +0 -32
  81. package/lib/cjs/components/datepicker/ui/renderers/header.js.map +0 -1
  82. package/lib/cjs/components/datepicker/ui/renderers/index.js +0 -25
  83. package/lib/cjs/components/datepicker/ui/renderers/index.js.map +0 -1
  84. package/lib/cjs/components/datepicker/ui/renderers/time-picker.js +0 -384
  85. package/lib/cjs/components/datepicker/ui/renderers/time-picker.js.map +0 -1
  86. package/lib/cjs/components/datepicker/ui/templates/index.js +0 -22
  87. package/lib/cjs/components/datepicker/ui/templates/index.js.map +0 -1
  88. package/lib/cjs/components/datepicker/ui/templates/templates.js +0 -253
  89. package/lib/cjs/components/datepicker/ui/templates/templates.js.map +0 -1
  90. package/lib/cjs/components/datepicker/utils/date-formatters.js +0 -88
  91. package/lib/cjs/components/datepicker/utils/date-formatters.js.map +0 -1
  92. package/lib/cjs/components/datepicker/utils/date-utils.js +0 -194
  93. package/lib/cjs/components/datepicker/utils/date-utils.js.map +0 -1
  94. package/lib/cjs/components/datepicker/utils/index.js +0 -24
  95. package/lib/cjs/components/datepicker/utils/index.js.map +0 -1
  96. package/lib/cjs/components/datepicker/utils/time-utils.js +0 -213
  97. package/lib/cjs/components/datepicker/utils/time-utils.js.map +0 -1
  98. package/lib/esm/components/alert/alert.js +0 -1022
  99. package/lib/esm/components/alert/alert.js.map +0 -1
  100. package/lib/esm/components/alert/index.js +0 -4
  101. package/lib/esm/components/alert/index.js.map +0 -1
  102. package/lib/esm/components/alert/templates.js +0 -112
  103. package/lib/esm/components/alert/templates.js.map +0 -1
  104. package/lib/esm/components/alert/types.js +0 -6
  105. package/lib/esm/components/alert/types.js.map +0 -1
  106. package/lib/esm/components/datepicker/config/config.js +0 -39
  107. package/lib/esm/components/datepicker/config/config.js.map +0 -1
  108. package/lib/esm/components/datepicker/config/index.js +0 -8
  109. package/lib/esm/components/datepicker/config/index.js.map +0 -1
  110. package/lib/esm/components/datepicker/config/interfaces.js +0 -6
  111. package/lib/esm/components/datepicker/config/interfaces.js.map +0 -1
  112. package/lib/esm/components/datepicker/config/types.js +0 -6
  113. package/lib/esm/components/datepicker/config/types.js.map +0 -1
  114. package/lib/esm/components/datepicker/core/event-manager.js +0 -133
  115. package/lib/esm/components/datepicker/core/event-manager.js.map +0 -1
  116. package/lib/esm/components/datepicker/core/focus-manager.js +0 -164
  117. package/lib/esm/components/datepicker/core/focus-manager.js.map +0 -1
  118. package/lib/esm/components/datepicker/core/helpers.js +0 -211
  119. package/lib/esm/components/datepicker/core/helpers.js.map +0 -1
  120. package/lib/esm/components/datepicker/core/index.js +0 -9
  121. package/lib/esm/components/datepicker/core/index.js.map +0 -1
  122. package/lib/esm/components/datepicker/core/unified-state-manager.js +0 -391
  123. package/lib/esm/components/datepicker/core/unified-state-manager.js.map +0 -1
  124. package/lib/esm/components/datepicker/datepicker.js +0 -2248
  125. package/lib/esm/components/datepicker/datepicker.js.map +0 -1
  126. package/lib/esm/components/datepicker/index.js +0 -7
  127. package/lib/esm/components/datepicker/index.js.map +0 -1
  128. package/lib/esm/components/datepicker/ui/index.js +0 -7
  129. package/lib/esm/components/datepicker/ui/index.js.map +0 -1
  130. package/lib/esm/components/datepicker/ui/input/dropdown.js +0 -486
  131. package/lib/esm/components/datepicker/ui/input/dropdown.js.map +0 -1
  132. package/lib/esm/components/datepicker/ui/input/index.js +0 -7
  133. package/lib/esm/components/datepicker/ui/input/index.js.map +0 -1
  134. package/lib/esm/components/datepicker/ui/input/segmented-input.js +0 -637
  135. package/lib/esm/components/datepicker/ui/input/segmented-input.js.map +0 -1
  136. package/lib/esm/components/datepicker/ui/renderers/calendar.js +0 -443
  137. package/lib/esm/components/datepicker/ui/renderers/calendar.js.map +0 -1
  138. package/lib/esm/components/datepicker/ui/renderers/footer.js +0 -39
  139. package/lib/esm/components/datepicker/ui/renderers/footer.js.map +0 -1
  140. package/lib/esm/components/datepicker/ui/renderers/header.js +0 -29
  141. package/lib/esm/components/datepicker/ui/renderers/header.js.map +0 -1
  142. package/lib/esm/components/datepicker/ui/renderers/index.js +0 -9
  143. package/lib/esm/components/datepicker/ui/renderers/index.js.map +0 -1
  144. package/lib/esm/components/datepicker/ui/renderers/time-picker.js +0 -381
  145. package/lib/esm/components/datepicker/ui/renderers/time-picker.js.map +0 -1
  146. package/lib/esm/components/datepicker/ui/templates/index.js +0 -6
  147. package/lib/esm/components/datepicker/ui/templates/index.js.map +0 -1
  148. package/lib/esm/components/datepicker/ui/templates/templates.js +0 -242
  149. package/lib/esm/components/datepicker/ui/templates/templates.js.map +0 -1
  150. package/lib/esm/components/datepicker/utils/date-formatters.js +0 -83
  151. package/lib/esm/components/datepicker/utils/date-formatters.js.map +0 -1
  152. package/lib/esm/components/datepicker/utils/date-utils.js +0 -184
  153. package/lib/esm/components/datepicker/utils/date-utils.js.map +0 -1
  154. package/lib/esm/components/datepicker/utils/index.js +0 -8
  155. package/lib/esm/components/datepicker/utils/index.js.map +0 -1
  156. package/lib/esm/components/datepicker/utils/time-utils.js +0 -201
  157. package/lib/esm/components/datepicker/utils/time-utils.js.map +0 -1
  158. package/src/components/alert/alert.ts +0 -990
  159. package/src/components/alert/index.ts +0 -4
  160. package/src/components/alert/templates.ts +0 -110
  161. package/src/components/alert/tests/accessibility/aria-roles.test.ts +0 -19
  162. package/src/components/alert/tests/accessibility/focus-management.test.ts +0 -19
  163. package/src/components/alert/tests/accessibility/keyboard-nav.test.ts +0 -22
  164. package/src/components/alert/tests/actions/confirm-cancel.test.ts +0 -122
  165. package/src/components/alert/tests/actions/input-field.test.ts +0 -180
  166. package/src/components/alert/tests/alert.basic.test.ts +0 -126
  167. package/src/components/alert/tests/alert.config.test.ts +0 -75
  168. package/src/components/alert/tests/alert.templates.test.ts +0 -17
  169. package/src/components/alert/tests/config/attribute-config.test.ts +0 -94
  170. package/src/components/alert/tests/config/json-config.test.ts +0 -119
  171. package/src/components/alert/tests/config/merging.test.ts +0 -89
  172. package/src/components/alert/tests/dismissal/auto-dismiss.test.ts +0 -96
  173. package/src/components/alert/tests/dismissal/escape-key-dismiss.test.ts +0 -105
  174. package/src/components/alert/tests/dismissal/manual-dismiss.test.ts +0 -90
  175. package/src/components/alert/tests/dismissal/outside-click-dismiss.test.ts +0 -91
  176. package/src/components/alert/tests/edge-cases/invalid-config.test.ts +0 -19
  177. package/src/components/alert/tests/edge-cases/multiple-alerts.test.ts +0 -19
  178. package/src/components/alert/tests/rendering/custom-content.test.ts +0 -81
  179. package/src/components/alert/tests/rendering/info-alert.test.ts +0 -84
  180. package/src/components/alert/tests/rendering/success-alert.test.ts +0 -100
  181. package/src/components/alert/tests/templates/default-templates.test.ts +0 -16
  182. package/src/components/alert/tests/templates/user-templates.test.ts +0 -16
  183. package/src/components/alert/types.ts +0 -145
  184. package/src/components/datepicker/__tests__/datepicker-events.test.ts +0 -356
  185. package/src/components/datepicker/__tests__/datepicker-init.test.ts +0 -343
  186. package/src/components/datepicker/__tests__/datepicker-integration.test.ts +0 -435
  187. package/src/components/datepicker/__tests__/datepicker-timezone.test.ts +0 -220
  188. package/src/components/datepicker/__tests__/segmented-input-focus.test.ts +0 -380
  189. package/src/components/datepicker/__tests__/selective-state-updates.test.ts +0 -400
  190. package/src/components/datepicker/__tests__/state-manager.test.ts +0 -421
  191. package/src/components/datepicker/__tests__/time-preservation.test.ts +0 -387
  192. package/src/components/datepicker/config/config.ts +0 -40
  193. package/src/components/datepicker/config/index.ts +0 -8
  194. package/src/components/datepicker/config/interfaces.ts +0 -82
  195. package/src/components/datepicker/config/types.ts +0 -188
  196. package/src/components/datepicker/core/event-manager.ts +0 -159
  197. package/src/components/datepicker/core/focus-manager.ts +0 -201
  198. package/src/components/datepicker/core/helpers.ts +0 -231
  199. package/src/components/datepicker/core/index.ts +0 -9
  200. package/src/components/datepicker/core/unified-state-manager.ts +0 -459
  201. package/src/components/datepicker/datepicker.css +0 -435
  202. package/src/components/datepicker/datepicker.ts +0 -2548
  203. package/src/components/datepicker/index.ts +0 -8
  204. package/src/components/datepicker/ui/index.ts +0 -7
  205. package/src/components/datepicker/ui/input/dropdown.ts +0 -552
  206. package/src/components/datepicker/ui/input/index.ts +0 -7
  207. package/src/components/datepicker/ui/input/segmented-input.ts +0 -638
  208. package/src/components/datepicker/ui/renderers/__tests__/calendar-optimizations.test.ts +0 -611
  209. package/src/components/datepicker/ui/renderers/calendar.ts +0 -530
  210. package/src/components/datepicker/ui/renderers/footer.ts +0 -43
  211. package/src/components/datepicker/ui/renderers/header.ts +0 -33
  212. package/src/components/datepicker/ui/renderers/index.ts +0 -9
  213. package/src/components/datepicker/ui/renderers/time-picker.ts +0 -438
  214. package/src/components/datepicker/ui/templates/index.ts +0 -6
  215. package/src/components/datepicker/ui/templates/templates.ts +0 -306
  216. package/src/components/datepicker/utils/__tests__/date-formatters.test.ts +0 -160
  217. package/src/components/datepicker/utils/__tests__/date-utils-keys.test.ts +0 -86
  218. package/src/components/datepicker/utils/__tests__/date-utils-timezone.test.ts +0 -215
  219. package/src/components/datepicker/utils/date-formatters.ts +0 -85
  220. package/src/components/datepicker/utils/date-utils.ts +0 -172
  221. package/src/components/datepicker/utils/index.ts +0 -8
  222. package/src/components/datepicker/utils/time-utils.ts +0 -221
@@ -1,637 +0,0 @@
1
- /*
2
- * segmented-input.ts - Modular segmented input for KTDatepicker (2025+)
3
- * Each date/time part is rendered as a focusable, editable segment.
4
- *
5
- * Features:
6
- * - Segments: day, month, year, (optionally hour, minute, second, AM/PM)
7
- * - Keyboard navigation: Tab, Shift+Tab, arrow keys, Enter
8
- * - Direct typing/editing of segments with focus preservation
9
- * - ARIA roles and accessibility for all segments
10
- * - Emits change events on value update (debounced)
11
- * - Integrates with KTDatepicker for value sync
12
- *
13
- * Keyboard Navigation:
14
- * - Tab/Shift+Tab: Move between segments
15
- * - Arrow Left/Right: Move between segments
16
- * - Arrow Up/Down: Increment/decrement segment value
17
- * - Enter: Move to next segment (wraps from last to first)
18
- * - Number keys: Direct input with validation (optimized for performance)
19
- *
20
- * Performance Optimizations:
21
- * - No DOM re-rendering during number typing
22
- * - Focus preserved during rapid input
23
- * - Debounced onChange events (150ms delay)
24
- * - Caret position maintained during editing
25
- */
26
- import { getTemplateStrings } from '../templates/templates';
27
- /**
28
- * SegmentedInput - renders a segmented date/time input.
29
- * @param container - HTMLElement to render into
30
- * @param options - SegmentedInputOptions
31
- * @returns cleanup function
32
- */
33
- export function SegmentedInput(container, options) {
34
- // --- Internal state ---
35
- var currentValue = new Date(options.value);
36
- var segments = options.segments || ['month', 'day', 'year'];
37
- var locale = options.locale || 'default';
38
- // Global flag to track arrow navigation across all segmented inputs
39
- if (!window.__ktui_segmented_input_arrow_navigation) {
40
- window.__ktui_segmented_input_arrow_navigation = false;
41
- }
42
- // --- Get templates ---
43
- // Use a minimal config to get templates; in real usage, pass full config if available
44
- var templates = getTemplateStrings({});
45
- var segmentTpl = templates.dateSegment;
46
- var separatorTpl = templates.segmentSeparator;
47
- // --- Utility: get separator between segments ---
48
- function getSeparatorBetweenSegments(segment1, segment2, format) {
49
- // Time segments use ":" as separator
50
- var timeSegments = ['hour', 'minute', 'second'];
51
- if (timeSegments.includes(segment1) && timeSegments.includes(segment2)) {
52
- return ':';
53
- }
54
- // Space between date and time
55
- var dateSegments = ['day', 'month', 'year'];
56
- if (dateSegments.includes(segment1) && timeSegments.includes(segment2)) {
57
- return ' ';
58
- }
59
- // AM/PM has space before it
60
- if (segment2 === 'ampm') {
61
- return ' ';
62
- }
63
- // Second to AM/PM has space
64
- if (segment1 === 'second' && segment2 === 'ampm') {
65
- return ' ';
66
- }
67
- // Date segments use separator from format
68
- if (dateSegments.includes(segment1) && dateSegments.includes(segment2)) {
69
- return getSeparatorFromFormat(format);
70
- }
71
- // Default fallback
72
- return '';
73
- }
74
- // --- Utility: get separator from format ---
75
- function getSeparatorFromFormat(format) {
76
- if (!format)
77
- return '/'; // Default fallback
78
- // Find the first non-date token character to use as separator
79
- var tokenRegex = /(yyyy|yy|MM|M|dd|d)/g;
80
- var lastIndex = 0;
81
- var match;
82
- while ((match = tokenRegex.exec(format)) !== null) {
83
- if (match.index > lastIndex) {
84
- // Found a separator character
85
- var separator = format.slice(lastIndex, match.index);
86
- if (separator && separator.length > 0) {
87
- return separator;
88
- }
89
- }
90
- lastIndex = match.index + match[0].length;
91
- }
92
- // Check for separator after the last token
93
- if (lastIndex < format.length) {
94
- var separator = format.slice(lastIndex);
95
- if (separator && separator.length > 0) {
96
- return separator;
97
- }
98
- }
99
- return '/'; // Default fallback
100
- }
101
- // --- Utility: check if segment should be padded based on format ---
102
- function shouldPadSegment(segment, format) {
103
- if (!format)
104
- return true; // Default to padded for backward compatibility
105
- var tokenRegex = /(yyyy|yy|MM|M|dd|d|HH|H|mm|m|ss|s)/g;
106
- var match;
107
- while ((match = tokenRegex.exec(format)) !== null) {
108
- switch (match[0]) {
109
- case 'dd':
110
- if (segment === 'day')
111
- return true;
112
- break;
113
- case 'd':
114
- if (segment === 'day')
115
- return false;
116
- break;
117
- case 'MM':
118
- if (segment === 'month')
119
- return true;
120
- break;
121
- case 'M':
122
- if (segment === 'month')
123
- return false;
124
- break;
125
- case 'yyyy':
126
- if (segment === 'year')
127
- return true;
128
- break; // 4-digit year
129
- case 'yy':
130
- if (segment === 'year')
131
- return false;
132
- break; // 2-digit year
133
- case 'HH':
134
- case 'H':
135
- if (segment === 'hour')
136
- return match[0] === 'HH';
137
- break;
138
- case 'mm':
139
- case 'm':
140
- if (segment === 'minute')
141
- return match[0] === 'mm';
142
- break;
143
- case 'ss':
144
- case 's':
145
- if (segment === 'second')
146
- return match[0] === 'ss';
147
- break;
148
- }
149
- }
150
- return true; // Default fallback
151
- }
152
- // --- Utility: get segment value as string ---
153
- function getSegmentValue(segment, date) {
154
- var shouldPad = shouldPadSegment(segment, options.format);
155
- switch (segment) {
156
- case 'day':
157
- var day = date.getDate();
158
- return shouldPad ? day.toString().padStart(2, '0') : day.toString();
159
- case 'month':
160
- var month = date.getMonth() + 1;
161
- return shouldPad ? month.toString().padStart(2, '0') : month.toString();
162
- case 'year':
163
- var year = date.getFullYear();
164
- return shouldPad ? year.toString() : year.toString().slice(-2); // yy format shows last 2 digits
165
- case 'hour':
166
- // For 12-hour format, convert to 1-12 range
167
- var hourValue = void 0;
168
- if (options.timeFormat === '12h') {
169
- var hour24 = date.getHours();
170
- hourValue = hour24 === 0 ? 12 : hour24 > 12 ? hour24 - 12 : hour24;
171
- }
172
- else {
173
- hourValue = date.getHours();
174
- }
175
- return shouldPad ? hourValue.toString().padStart(2, '0') : hourValue.toString();
176
- case 'minute':
177
- var minute = date.getMinutes();
178
- return shouldPad ? minute.toString().padStart(2, '0') : minute.toString();
179
- case 'second':
180
- var second = date.getSeconds();
181
- return shouldPad ? second.toString().padStart(2, '0') : second.toString();
182
- case 'ampm': return date.getHours() < 12 ? 'AM' : 'PM';
183
- default: return '';
184
- }
185
- }
186
- // --- Utility: set segment value ---
187
- function setSegmentValue(segment, value, date) {
188
- var d = new Date(date);
189
- switch (segment) {
190
- case 'day':
191
- d.setDate(Number(value));
192
- break;
193
- case 'month':
194
- d.setMonth(Number(value) - 1);
195
- break;
196
- case 'year':
197
- d.setFullYear(Number(value));
198
- break;
199
- case 'hour':
200
- // Handle 12-hour vs 24-hour format
201
- var hourValue = Number(value);
202
- if (options.timeFormat === '12h') {
203
- // In 12-hour mode, convert 12-hour input to 24-hour
204
- var currentHour = d.getHours();
205
- var isPM = currentHour >= 12;
206
- if (hourValue === 12) {
207
- hourValue = isPM ? 12 : 0; // 12 AM = 0, 12 PM = 12
208
- }
209
- else if (isPM) {
210
- hourValue += 12; // PM hours: add 12
211
- }
212
- }
213
- // Preserve existing minutes and seconds when setting hour
214
- var currentMinutes = d.getMinutes();
215
- var currentSecondsForHour = d.getSeconds();
216
- d.setHours(hourValue, currentMinutes, currentSecondsForHour);
217
- break;
218
- case 'minute':
219
- // Preserve existing seconds when setting minute
220
- var currentSecondsForMinute = d.getSeconds();
221
- d.setMinutes(Number(value), currentSecondsForMinute);
222
- break;
223
- case 'second':
224
- d.setSeconds(Number(value));
225
- break;
226
- case 'ampm':
227
- if (value === 'AM' && d.getHours() >= 12) {
228
- d.setHours(d.getHours() - 12);
229
- }
230
- else if (value === 'PM' && d.getHours() < 12) {
231
- d.setHours(d.getHours() + 12);
232
- }
233
- break;
234
- }
235
- return d;
236
- }
237
- // --- Utility: get min/max for a segment ---
238
- function getSegmentMin(segment, date) {
239
- switch (segment) {
240
- case 'day':
241
- // Use actual month/year for max days
242
- return 1;
243
- case 'month':
244
- return 1;
245
- case 'year':
246
- return options.min ? options.min.getFullYear() : undefined;
247
- case 'hour':
248
- return 0;
249
- case 'minute':
250
- case 'second':
251
- return 0;
252
- case 'ampm':
253
- return 0; // 0 = AM, 1 = PM
254
- default:
255
- return undefined;
256
- }
257
- }
258
- function getSegmentMax(segment, date) {
259
- switch (segment) {
260
- case 'day':
261
- // Use actual month/year for max days
262
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
263
- case 'month':
264
- return 12;
265
- case 'year':
266
- return options.max ? options.max.getFullYear() : undefined;
267
- case 'hour':
268
- return 23;
269
- case 'minute':
270
- case 'second':
271
- return 59;
272
- case 'ampm':
273
- return 1; // 0 = AM, 1 = PM
274
- default:
275
- return undefined;
276
- }
277
- }
278
- // --- Track focused segment index ---
279
- var focusedIdx = 0;
280
- // --- Track caret position (offset) ---
281
- var caretOffset = null;
282
- // --- Track if this is the first render ---
283
- var isInitialRender = true;
284
- // --- Track if we're in the middle of Arrow Up/Down navigation ---
285
- var isArrowNavigation = false;
286
- // --- Focus a segment by index and restore caret position ---
287
- /**
288
- * Restores focus to the segment at the given index and restores caret position if available.
289
- * @param idx - Index of the segment to focus
290
- * @param caret - Caret offset to restore (null for end)
291
- */
292
- function restoreFocus(idx, caret) {
293
- var _a;
294
- if (caret === void 0) { caret = null; }
295
- try {
296
- var segs = Array.from(container.querySelectorAll('[data-segment]'));
297
- if (segs[idx] && segs[idx].offsetParent !== null) { // Check if element is in DOM
298
- segs.forEach(function (el, i) { return el.setAttribute('tabindex', i === idx ? '0' : '-1'); });
299
- segs[idx].focus();
300
- // Restore caret position (at end if null)
301
- if (segs[idx].isContentEditable) {
302
- var range = document.createRange();
303
- range.selectNodeContents(segs[idx]);
304
- range.collapse(false); // place at end
305
- if (caret !== null && segs[idx].firstChild) {
306
- range.setStart(segs[idx].firstChild, Math.min(caret, ((_a = segs[idx].textContent) === null || _a === void 0 ? void 0 : _a.length) || 0));
307
- range.collapse(true);
308
- }
309
- var sel = window.getSelection();
310
- if (sel) {
311
- sel.removeAllRanges();
312
- sel.addRange(range);
313
- }
314
- }
315
- }
316
- }
317
- catch (error) {
318
- // Fallback: focus first available segment
319
- var segs = Array.from(container.querySelectorAll('[data-segment]'));
320
- if (segs[0])
321
- segs[0].focus();
322
- }
323
- }
324
- // --- Render segments using templates ---
325
- function render() {
326
- // Capture caret position before DOM update
327
- var prevSegs = Array.from(container.querySelectorAll('[data-segment]'));
328
- if (prevSegs[focusedIdx] && document.activeElement === prevSegs[focusedIdx]) {
329
- var sel = window.getSelection();
330
- if (sel && sel.anchorNode === prevSegs[focusedIdx].firstChild) {
331
- caretOffset = sel.anchorOffset;
332
- }
333
- else {
334
- caretOffset = null;
335
- }
336
- }
337
- else {
338
- caretOffset = null;
339
- }
340
- container.innerHTML = '';
341
- container.setAttribute('role', 'group');
342
- container.setAttribute('aria-label', 'Date input');
343
- container.tabIndex = -1;
344
- // Build segments HTML using templates
345
- var segmentsHtml = '';
346
- segments.forEach(function (segment, idx) {
347
- var _a, _b, _c, _d;
348
- var segmentValue = getSegmentValue(segment, currentValue);
349
- var segmentData = {
350
- segmentType: segment,
351
- segmentValue: segmentValue,
352
- ariaLabel: segment.charAt(0).toUpperCase() + segment.slice(1),
353
- ariaValueNow: segmentValue,
354
- ariaValueText: segmentValue,
355
- ariaValueMin: (_b = (_a = getSegmentMin(segment, currentValue)) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '',
356
- ariaValueMax: (_d = (_c = getSegmentMax(segment, currentValue)) === null || _c === void 0 ? void 0 : _c.toString()) !== null && _d !== void 0 ? _d : '',
357
- tabindex: idx === focusedIdx ? '0' : '-1',
358
- contenteditable: (!options.disabled && !options.readOnly).toString(),
359
- };
360
- var segmentHtml = '';
361
- if (typeof segmentTpl === 'function') {
362
- segmentHtml = segmentTpl(segmentData);
363
- }
364
- else if (typeof segmentTpl === 'string') {
365
- segmentHtml = segmentTpl
366
- .replace(/{{segmentType}}/g, segmentData.segmentType)
367
- .replace(/{{segmentValue}}/g, segmentData.segmentValue)
368
- .replace(/{{ariaLabel}}/g, segmentData.ariaLabel)
369
- .replace(/{{ariaValueNow}}/g, segmentData.ariaValueNow)
370
- .replace(/{{ariaValueText}}/g, segmentData.ariaValueText)
371
- .replace(/{{ariaValueMin}}/g, segmentData.ariaValueMin)
372
- .replace(/{{ariaValueMax}}/g, segmentData.ariaValueMax)
373
- .replace(/{{tabindex}}/g, segmentData.tabindex)
374
- .replace(/{{contenteditable}}/g, segmentData.contenteditable)
375
- .replace(/{{class}}/g, ''); // Replace class placeholder with empty string
376
- }
377
- else {
378
- segmentHtml = '';
379
- }
380
- segmentsHtml += segmentHtml;
381
- if (idx < segments.length - 1) {
382
- // Get appropriate separator between these segments
383
- var nextSegment = segments[idx + 1];
384
- var separator = getSeparatorBetweenSegments(segment, nextSegment, options.format);
385
- var sepHtml = '';
386
- if (typeof separatorTpl === 'function') {
387
- sepHtml = separatorTpl({ separator: separator });
388
- }
389
- else if (typeof separatorTpl === 'string') {
390
- sepHtml = separatorTpl
391
- .replace(/{{separator}}/g, separator)
392
- .replace(/{{class}}/g, ''); // Replace class placeholder with empty string
393
- }
394
- else {
395
- sepHtml = '';
396
- }
397
- segmentsHtml += sepHtml;
398
- }
399
- });
400
- // Render segments directly into container (container already has the correct structure from template)
401
- container.innerHTML = segmentsHtml;
402
- // Verify template rendering was successful
403
- var segs = Array.from(container.querySelectorAll('[data-segment]'));
404
- if (segs.length === 0) {
405
- throw new Error('Segmented input template rendering failed');
406
- }
407
- // Re-bind events to all segments
408
- segs.forEach(function (span, idx) {
409
- span.addEventListener('keydown', function (e) {
410
- var _a, _b, _c, _d;
411
- if (options.disabled || options.readOnly)
412
- return;
413
- // Wrapping navigation
414
- if (e.key === 'ArrowRight' || (e.key === 'Tab' && !e.shiftKey)) {
415
- e.preventDefault();
416
- isArrowNavigation = true; // Set flag to prevent blur onChange
417
- focusedIdx = (idx + 1) % segments.length;
418
- caretOffset = null;
419
- // Update tabindex directly instead of full re-render
420
- var segs_1 = Array.from(container.querySelectorAll('[data-segment]'));
421
- segs_1.forEach(function (el, i) { return el.setAttribute('tabindex', i === focusedIdx ? '0' : '-1'); });
422
- restoreFocus(focusedIdx, caretOffset);
423
- // Reset flag after a short delay to allow focus to be restored
424
- setTimeout(function () {
425
- isArrowNavigation = false;
426
- }, 10);
427
- }
428
- else if (e.key === 'ArrowLeft' || (e.key === 'Tab' && e.shiftKey)) {
429
- e.preventDefault();
430
- isArrowNavigation = true; // Set flag to prevent blur onChange
431
- focusedIdx = (idx - 1 + segments.length) % segments.length;
432
- caretOffset = null;
433
- // Update tabindex directly instead of full re-render
434
- var segs_2 = Array.from(container.querySelectorAll('[data-segment]'));
435
- segs_2.forEach(function (el, i) { return el.setAttribute('tabindex', i === focusedIdx ? '0' : '-1'); });
436
- restoreFocus(focusedIdx, caretOffset);
437
- // Reset flag after a short delay to allow focus to be restored
438
- setTimeout(function () {
439
- isArrowNavigation = false;
440
- }, 10);
441
- }
442
- else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
443
- // Increment/decrement value
444
- e.preventDefault();
445
- e.stopPropagation(); // Prevent bubbling to main datepicker
446
- isArrowNavigation = true; // Set flag to prevent blur onChange
447
- var newValue = void 0;
448
- if (segments[idx] === 'ampm') {
449
- // Handle AM/PM toggle
450
- var currentAmPm = span.textContent || 'AM';
451
- if (e.key === 'ArrowUp') {
452
- newValue = currentAmPm === 'AM' ? 'PM' : 'AM';
453
- }
454
- else {
455
- newValue = currentAmPm === 'AM' ? 'PM' : 'AM';
456
- }
457
- }
458
- else {
459
- // Handle numeric segments
460
- var min = (_a = getSegmentMin(segments[idx], currentValue)) !== null && _a !== void 0 ? _a : 0;
461
- var max = (_b = getSegmentMax(segments[idx], currentValue)) !== null && _b !== void 0 ? _b : 9999;
462
- var current = Number(getSegmentValue(segments[idx], currentValue)) || min;
463
- if (e.key === 'ArrowUp') {
464
- current = Math.min(max, current + 1);
465
- }
466
- else if (e.key === 'ArrowDown') {
467
- current = Math.max(min, current - 1);
468
- }
469
- newValue = current.toString();
470
- var shouldPad = shouldPadSegment(segments[idx], options.format);
471
- if (segments[idx] === 'year') {
472
- if (shouldPad) {
473
- newValue = newValue.padStart(4, '0');
474
- }
475
- }
476
- else if (shouldPad) {
477
- newValue = newValue.padStart(2, '0');
478
- }
479
- }
480
- span.textContent = newValue;
481
- currentValue = setSegmentValue(segments[idx], newValue, currentValue);
482
- // Set global flag to prevent unified observer from overriding UI
483
- window.__ktui_segmented_input_arrow_navigation = true;
484
- // Call onChange immediately for Arrow Up/Down to update the main datepicker
485
- if (options.onChange) {
486
- options.onChange(currentValue);
487
- }
488
- // Clear flag after onChange callback
489
- setTimeout(function () {
490
- window.__ktui_segmented_input_arrow_navigation = false;
491
- }, 50);
492
- // Preserve caret position at end of content
493
- if (span.isContentEditable) {
494
- var range = document.createRange();
495
- range.selectNodeContents(span);
496
- range.collapse(false); // place at end
497
- var sel = window.getSelection();
498
- if (sel) {
499
- sel.removeAllRanges();
500
- sel.addRange(range);
501
- }
502
- }
503
- // Reset flag after a short delay to allow focus to be restored
504
- setTimeout(function () {
505
- isArrowNavigation = false;
506
- }, 10);
507
- }
508
- else if (e.key === 'Enter') {
509
- // Move to next segment on Enter
510
- e.preventDefault();
511
- e.stopPropagation(); // Prevent bubbling to main datepicker
512
- isArrowNavigation = true; // Set flag to prevent blur onChange
513
- focusedIdx = (idx + 1) % segments.length;
514
- caretOffset = null;
515
- // Update tabindex directly instead of full re-render
516
- var segs_3 = Array.from(container.querySelectorAll('[data-segment]'));
517
- segs_3.forEach(function (el, i) { return el.setAttribute('tabindex', i === focusedIdx ? '0' : '-1'); });
518
- restoreFocus(focusedIdx, caretOffset);
519
- // Reset flag after a short delay to allow focus to be restored
520
- setTimeout(function () {
521
- isArrowNavigation = false;
522
- }, 10);
523
- }
524
- else if (/^[0-9]$/.test(e.key)) {
525
- // Direct typing, enforce min/max - optimized to avoid focus loss
526
- e.preventDefault();
527
- var newValue = void 0;
528
- var currentText = span.textContent || '';
529
- if (segments[idx] === 'year') {
530
- // For year: shift left and append new digit (e.g., "2024" + "6" = "0246", then becomes "2026" after validation)
531
- // If already 4 digits, shift left by removing first digit and appending new one
532
- if (currentText.length === 4) {
533
- newValue = (currentText.slice(1) + e.key);
534
- }
535
- else {
536
- newValue = (currentText + e.key).slice(-4);
537
- }
538
- }
539
- else {
540
- // For day/month: shift left and append new digit (e.g., "12" + "5" = "25")
541
- // If already 2 digits, shift left by removing first digit and appending new one
542
- if (currentText.length === 2) {
543
- newValue = (currentText.slice(1) + e.key);
544
- }
545
- else {
546
- newValue = (currentText + e.key).slice(-2);
547
- }
548
- }
549
- var min = (_c = getSegmentMin(segments[idx], currentValue)) !== null && _c !== void 0 ? _c : 0;
550
- var max = (_d = getSegmentMax(segments[idx], currentValue)) !== null && _d !== void 0 ? _d : (segments[idx] === 'year' ? 9999 : 99);
551
- var num = Math.max(min, Math.min(max, Number(newValue)));
552
- if (isNaN(num))
553
- num = min;
554
- // Update content directly without re-rendering to preserve focus
555
- var shouldPad = shouldPadSegment(segments[idx], options.format);
556
- if (segments[idx] === 'year') {
557
- span.textContent = shouldPad ? num.toString().padStart(4, '0') : num.toString();
558
- }
559
- else {
560
- span.textContent = shouldPad ? num.toString().padStart(2, '0') : num.toString();
561
- }
562
- // Update internal value
563
- currentValue = setSegmentValue(segments[idx], span.textContent || '', currentValue);
564
- // Debounce onChange to avoid excessive updates during rapid typing
565
- if (options.onChange) {
566
- clearTimeout(span._onChangeTimeout);
567
- span._onChangeTimeout = setTimeout(function () {
568
- options.onChange(currentValue);
569
- }, 150); // 150ms debounce
570
- }
571
- // Preserve caret position at end of content
572
- if (span.isContentEditable) {
573
- var range = document.createRange();
574
- range.selectNodeContents(span);
575
- range.collapse(false); // place at end
576
- var sel = window.getSelection();
577
- if (sel) {
578
- sel.removeAllRanges();
579
- sel.addRange(range);
580
- }
581
- }
582
- }
583
- });
584
- // Focus/blur styling (no classes, just ARIA/tabindex)
585
- span.addEventListener('focus', function () {
586
- span.setAttribute('tabindex', '0');
587
- focusedIdx = idx;
588
- });
589
- span.addEventListener('blur', function () {
590
- span.setAttribute('tabindex', '-1');
591
- // Clear any pending debounced onChange calls
592
- if (span._onChangeTimeout) {
593
- clearTimeout(span._onChangeTimeout);
594
- span._onChangeTimeout = null;
595
- }
596
- // Call onChange when user finishes editing to update the main datepicker
597
- // But not during Arrow Up/Down navigation and only if value actually changed
598
- if (options.onChange && !isArrowNavigation) {
599
- var updatedValue = setSegmentValue(segments[idx], span.textContent || '', currentValue);
600
- // Only call onChange if the value has actually changed
601
- if (updatedValue.getTime() !== currentValue.getTime()) {
602
- options.onChange(updatedValue);
603
- }
604
- }
605
- });
606
- // Mouse click focuses segment (optimized to avoid unnecessary re-rendering)
607
- span.addEventListener('mousedown', function (e) {
608
- e.preventDefault();
609
- focusedIdx = idx;
610
- caretOffset = null;
611
- // Only update tabindex and focus, no full re-render
612
- var segs = Array.from(container.querySelectorAll('[data-segment]'));
613
- segs.forEach(function (el, i) { return el.setAttribute('tabindex', i === focusedIdx ? '0' : '-1'); });
614
- restoreFocus(focusedIdx, caretOffset);
615
- });
616
- });
617
- // After rendering, restore focus to the correct segment and caret (skip on initial render)
618
- if (!isInitialRender) {
619
- restoreFocus(focusedIdx, caretOffset);
620
- }
621
- isInitialRender = false;
622
- }
623
- // --- Initial render ---
624
- render();
625
- // --- Cleanup function ---
626
- return function () {
627
- // Clear any pending debounced onChange calls
628
- var segs = Array.from(container.querySelectorAll('[data-segment]'));
629
- segs.forEach(function (span) {
630
- if (span._onChangeTimeout) {
631
- clearTimeout(span._onChangeTimeout);
632
- }
633
- });
634
- container.innerHTML = '';
635
- };
636
- }
637
- //# sourceMappingURL=segmented-input.js.map