tyrell-components 1.0.0-RC13 → 1.0.0-RC15

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 (358) hide show
  1. package/README.md +10 -2
  2. package/css/tyrell-theme.css +192 -180
  3. package/css/tyrell.css +23 -0
  4. package/dist/tyrell-colors-static.css +1 -316
  5. package/dist/tyrell-theme.css +1 -1454
  6. package/dist/tyrell.css +1 -1371
  7. package/dist/tyrell.js +1 -1
  8. package/lib/base/ty-component.d.ts +20 -10
  9. package/lib/base/ty-component.js +196 -319
  10. package/lib/components/button.d.ts +3 -17
  11. package/lib/components/button.js +261 -295
  12. package/lib/components/calendar-month.d.ts +4 -50
  13. package/lib/components/calendar-month.js +443 -582
  14. package/lib/components/calendar-navigation.d.ts +3 -48
  15. package/lib/components/calendar-navigation.js +239 -362
  16. package/lib/components/calendar.d.ts +8 -83
  17. package/lib/components/calendar.js +616 -829
  18. package/lib/components/checkbox.d.ts +5 -72
  19. package/lib/components/checkbox.js +273 -416
  20. package/lib/components/copy.d.ts +4 -57
  21. package/lib/components/copy.js +280 -397
  22. package/lib/components/date-picker.d.ts +21 -163
  23. package/lib/components/date-picker.js +1143 -1674
  24. package/lib/components/file-upload.d.ts +9 -15
  25. package/lib/components/file-upload.js +404 -426
  26. package/lib/components/icon.d.ts +3 -24
  27. package/lib/components/icon.js +141 -230
  28. package/lib/components/input.d.ts +8 -108
  29. package/lib/components/input.js +593 -784
  30. package/lib/components/modal.d.ts +17 -26
  31. package/lib/components/modal.js +340 -484
  32. package/lib/components/option.d.ts +2 -9
  33. package/lib/components/option.js +168 -216
  34. package/lib/components/popup.d.ts +2 -17
  35. package/lib/components/popup.js +249 -380
  36. package/lib/components/radio.d.ts +5 -25
  37. package/lib/components/radio.js +407 -461
  38. package/lib/components/resize-observer.d.ts +3 -30
  39. package/lib/components/resize-observer.js +69 -103
  40. package/lib/components/scroll-container.d.ts +2 -12
  41. package/lib/components/scroll-container.js +293 -338
  42. package/lib/components/select.d.ts +87 -159
  43. package/lib/components/select.js +1609 -2090
  44. package/lib/components/selected-tags.d.ts +5 -48
  45. package/lib/components/selected-tags.js +188 -276
  46. package/lib/components/step.d.ts +1 -6
  47. package/lib/components/step.js +62 -68
  48. package/lib/components/switch.d.ts +1 -12
  49. package/lib/components/switch.js +214 -247
  50. package/lib/components/tab.d.ts +1 -6
  51. package/lib/components/tab.js +62 -69
  52. package/lib/components/tabs.d.ts +6 -63
  53. package/lib/components/tabs.js +545 -777
  54. package/lib/components/tag.d.ts +3 -37
  55. package/lib/components/tag.js +236 -335
  56. package/lib/components/textarea.d.ts +7 -87
  57. package/lib/components/textarea.js +457 -627
  58. package/lib/components/tooltip.d.ts +3 -11
  59. package/lib/components/tooltip.js +307 -443
  60. package/lib/components/wizard.d.ts +6 -54
  61. package/lib/components/wizard.js +405 -618
  62. package/lib/icons/fontawesome/brands.d.ts +0 -1
  63. package/lib/icons/fontawesome/brands.js +0 -8
  64. package/lib/icons/fontawesome/regular.d.ts +0 -1
  65. package/lib/icons/fontawesome/regular.js +0 -8
  66. package/lib/icons/fontawesome/solid.d.ts +0 -1
  67. package/lib/icons/fontawesome/solid.js +0 -8
  68. package/lib/icons/heroicons/micro.d.ts +0 -1
  69. package/lib/icons/heroicons/micro.js +392 -400
  70. package/lib/icons/heroicons/mini.d.ts +0 -1
  71. package/lib/icons/heroicons/mini.js +382 -390
  72. package/lib/icons/heroicons/outline.d.ts +0 -1
  73. package/lib/icons/heroicons/outline.js +337 -345
  74. package/lib/icons/heroicons/solid.d.ts +0 -1
  75. package/lib/icons/heroicons/solid.js +407 -415
  76. package/lib/icons/lucide.d.ts +0 -1
  77. package/lib/icons/lucide.js +24479 -26351
  78. package/lib/icons/material/filled.d.ts +0 -1
  79. package/lib/icons/material/filled.js +9653 -9663
  80. package/lib/icons/material/outlined.d.ts +0 -1
  81. package/lib/icons/material/outlined.js +10271 -10281
  82. package/lib/icons/material/round.d.ts +0 -1
  83. package/lib/icons/material/round.js +10495 -10505
  84. package/lib/icons/material/sharp.d.ts +0 -1
  85. package/lib/icons/material/sharp.js +9905 -9915
  86. package/lib/icons/material/two-tone.d.ts +0 -1
  87. package/lib/icons/material/two-tone.js +12792 -12802
  88. package/lib/index.d.ts +0 -1
  89. package/lib/index.js +21 -41
  90. package/lib/styles/button.d.ts +2 -20
  91. package/lib/styles/button.js +202 -404
  92. package/lib/styles/calendar-month.d.ts +1 -5
  93. package/lib/styles/calendar-month.js +127 -244
  94. package/lib/styles/calendar-navigation.d.ts +2 -6
  95. package/lib/styles/calendar-navigation.js +64 -104
  96. package/lib/styles/calendar.d.ts +1 -2
  97. package/lib/styles/calendar.js +9 -22
  98. package/lib/styles/checkbox.d.ts +0 -1
  99. package/lib/styles/checkbox.js +45 -94
  100. package/lib/styles/copy.d.ts +0 -1
  101. package/lib/styles/copy.js +67 -106
  102. package/lib/styles/custom-scrollbar.d.ts +1 -2
  103. package/lib/styles/custom-scrollbar.js +80 -115
  104. package/lib/styles/date-picker.d.ts +1 -5
  105. package/lib/styles/date-picker.js +261 -335
  106. package/lib/styles/field-size.d.ts +6 -0
  107. package/lib/styles/field-size.js +91 -0
  108. package/lib/styles/file-upload.d.ts +1 -2
  109. package/lib/styles/file-upload.js +143 -188
  110. package/lib/styles/icon.d.ts +2 -6
  111. package/lib/styles/icon.js +92 -170
  112. package/lib/styles/input.d.ts +1 -6
  113. package/lib/styles/input.js +125 -290
  114. package/lib/styles/modal.d.ts +1 -2
  115. package/lib/styles/modal.js +71 -108
  116. package/lib/styles/option.d.ts +2 -6
  117. package/lib/styles/option.js +49 -64
  118. package/lib/styles/popup.d.ts +1 -2
  119. package/lib/styles/popup.js +42 -77
  120. package/lib/styles/radio.d.ts +0 -1
  121. package/lib/styles/radio.js +69 -110
  122. package/lib/styles/resize-observer.d.ts +1 -2
  123. package/lib/styles/resize-observer.js +2 -12
  124. package/lib/styles/scroll-container.d.ts +1 -2
  125. package/lib/styles/scroll-container.js +100 -149
  126. package/lib/styles/select-base.d.ts +0 -1
  127. package/lib/styles/select-base.js +597 -785
  128. package/lib/styles/select.d.ts +1 -2
  129. package/lib/styles/select.js +140 -236
  130. package/lib/styles/step.d.ts +1 -2
  131. package/lib/styles/step.js +22 -33
  132. package/lib/styles/switch.d.ts +0 -1
  133. package/lib/styles/switch.js +34 -73
  134. package/lib/styles/tab.d.ts +1 -2
  135. package/lib/styles/tab.js +22 -34
  136. package/lib/styles/tabs.d.ts +3 -11
  137. package/lib/styles/tabs.js +190 -222
  138. package/lib/styles/tag.d.ts +1 -5
  139. package/lib/styles/tag.js +136 -218
  140. package/lib/styles/textarea.d.ts +1 -2
  141. package/lib/styles/textarea.js +210 -319
  142. package/lib/styles/tooltip.d.ts +0 -1
  143. package/lib/styles/tooltip.js +3 -13
  144. package/lib/styles/wizard.d.ts +5 -21
  145. package/lib/styles/wizard.js +170 -306
  146. package/lib/types/common.d.ts +2 -2
  147. package/lib/types/common.js +6 -17
  148. package/lib/utils/calendar-utils.d.ts +4 -21
  149. package/lib/utils/calendar-utils.js +100 -273
  150. package/lib/utils/custom-scrollbar.d.ts +6 -20
  151. package/lib/utils/custom-scrollbar.js +273 -317
  152. package/lib/utils/flavor-sheet.d.ts +0 -1
  153. package/lib/utils/flavor-sheet.js +14 -37
  154. package/lib/utils/icon-registry.d.ts +0 -2
  155. package/lib/utils/icon-registry.js +137 -243
  156. package/lib/utils/icons.d.ts +0 -1
  157. package/lib/utils/icons.js +0 -9
  158. package/lib/utils/loader-registry.d.ts +0 -1
  159. package/lib/utils/loader-registry.js +3 -35
  160. package/lib/utils/locale.d.ts +2 -23
  161. package/lib/utils/locale.js +34 -120
  162. package/lib/utils/mobile.d.ts +0 -1
  163. package/lib/utils/mobile.js +1 -18
  164. package/lib/utils/number-format.d.ts +6 -3
  165. package/lib/utils/number-format.js +54 -110
  166. package/lib/utils/positioning.d.ts +50 -7
  167. package/lib/utils/positioning.js +224 -234
  168. package/lib/utils/property-capture.d.ts +5 -27
  169. package/lib/utils/property-capture.js +36 -144
  170. package/lib/utils/property-manager.d.ts +0 -1
  171. package/lib/utils/property-manager.js +126 -196
  172. package/lib/utils/resize-observer.d.ts +0 -1
  173. package/lib/utils/resize-observer.js +23 -59
  174. package/lib/utils/scroll-lock.d.ts +4 -11
  175. package/lib/utils/scroll-lock.js +65 -172
  176. package/lib/utils/styles.d.ts +0 -1
  177. package/lib/utils/styles.js +17 -41
  178. package/lib/version.d.ts +1 -2
  179. package/lib/version.js +1 -11
  180. package/package.json +4 -4
  181. package/lib/base/ty-component.d.ts.map +0 -1
  182. package/lib/base/ty-component.js.map +0 -1
  183. package/lib/components/button.d.ts.map +0 -1
  184. package/lib/components/button.js.map +0 -1
  185. package/lib/components/calendar-month.d.ts.map +0 -1
  186. package/lib/components/calendar-month.js.map +0 -1
  187. package/lib/components/calendar-navigation.d.ts.map +0 -1
  188. package/lib/components/calendar-navigation.js.map +0 -1
  189. package/lib/components/calendar.d.ts.map +0 -1
  190. package/lib/components/calendar.js.map +0 -1
  191. package/lib/components/checkbox.d.ts.map +0 -1
  192. package/lib/components/checkbox.js.map +0 -1
  193. package/lib/components/copy.d.ts.map +0 -1
  194. package/lib/components/copy.js.map +0 -1
  195. package/lib/components/date-picker.d.ts.map +0 -1
  196. package/lib/components/date-picker.js.map +0 -1
  197. package/lib/components/file-upload.d.ts.map +0 -1
  198. package/lib/components/file-upload.js.map +0 -1
  199. package/lib/components/icon.d.ts.map +0 -1
  200. package/lib/components/icon.js.map +0 -1
  201. package/lib/components/input.d.ts.map +0 -1
  202. package/lib/components/input.js.map +0 -1
  203. package/lib/components/modal.d.ts.map +0 -1
  204. package/lib/components/modal.js.map +0 -1
  205. package/lib/components/option.d.ts.map +0 -1
  206. package/lib/components/option.js.map +0 -1
  207. package/lib/components/popup.d.ts.map +0 -1
  208. package/lib/components/popup.js.map +0 -1
  209. package/lib/components/radio.d.ts.map +0 -1
  210. package/lib/components/radio.js.map +0 -1
  211. package/lib/components/resize-observer.d.ts.map +0 -1
  212. package/lib/components/resize-observer.js.map +0 -1
  213. package/lib/components/scroll-container.d.ts.map +0 -1
  214. package/lib/components/scroll-container.js.map +0 -1
  215. package/lib/components/select.d.ts.map +0 -1
  216. package/lib/components/select.js.map +0 -1
  217. package/lib/components/selected-tags.d.ts.map +0 -1
  218. package/lib/components/selected-tags.js.map +0 -1
  219. package/lib/components/step.d.ts.map +0 -1
  220. package/lib/components/step.js.map +0 -1
  221. package/lib/components/switch.d.ts.map +0 -1
  222. package/lib/components/switch.js.map +0 -1
  223. package/lib/components/tab.d.ts.map +0 -1
  224. package/lib/components/tab.js.map +0 -1
  225. package/lib/components/tabs.d.ts.map +0 -1
  226. package/lib/components/tabs.js.map +0 -1
  227. package/lib/components/tag.d.ts.map +0 -1
  228. package/lib/components/tag.js.map +0 -1
  229. package/lib/components/textarea.d.ts.map +0 -1
  230. package/lib/components/textarea.js.map +0 -1
  231. package/lib/components/tooltip.d.ts.map +0 -1
  232. package/lib/components/tooltip.js.map +0 -1
  233. package/lib/components/wizard.d.ts.map +0 -1
  234. package/lib/components/wizard.js.map +0 -1
  235. package/lib/icons/fontawesome/brands.d.ts.map +0 -1
  236. package/lib/icons/fontawesome/brands.js.map +0 -1
  237. package/lib/icons/fontawesome/regular.d.ts.map +0 -1
  238. package/lib/icons/fontawesome/regular.js.map +0 -1
  239. package/lib/icons/fontawesome/solid.d.ts.map +0 -1
  240. package/lib/icons/fontawesome/solid.js.map +0 -1
  241. package/lib/icons/heroicons/micro.d.ts.map +0 -1
  242. package/lib/icons/heroicons/micro.js.map +0 -1
  243. package/lib/icons/heroicons/mini.d.ts.map +0 -1
  244. package/lib/icons/heroicons/mini.js.map +0 -1
  245. package/lib/icons/heroicons/outline.d.ts.map +0 -1
  246. package/lib/icons/heroicons/outline.js.map +0 -1
  247. package/lib/icons/heroicons/solid.d.ts.map +0 -1
  248. package/lib/icons/heroicons/solid.js.map +0 -1
  249. package/lib/icons/lucide.d.ts.map +0 -1
  250. package/lib/icons/lucide.js.map +0 -1
  251. package/lib/icons/material/filled.d.ts.map +0 -1
  252. package/lib/icons/material/filled.js.map +0 -1
  253. package/lib/icons/material/outlined.d.ts.map +0 -1
  254. package/lib/icons/material/outlined.js.map +0 -1
  255. package/lib/icons/material/round.d.ts.map +0 -1
  256. package/lib/icons/material/round.js.map +0 -1
  257. package/lib/icons/material/sharp.d.ts.map +0 -1
  258. package/lib/icons/material/sharp.js.map +0 -1
  259. package/lib/icons/material/two-tone.d.ts.map +0 -1
  260. package/lib/icons/material/two-tone.js.map +0 -1
  261. package/lib/index.d.ts.map +0 -1
  262. package/lib/index.js.map +0 -1
  263. package/lib/styles/button.d.ts.map +0 -1
  264. package/lib/styles/button.js.map +0 -1
  265. package/lib/styles/calendar-month.d.ts.map +0 -1
  266. package/lib/styles/calendar-month.js.map +0 -1
  267. package/lib/styles/calendar-navigation.d.ts.map +0 -1
  268. package/lib/styles/calendar-navigation.js.map +0 -1
  269. package/lib/styles/calendar.d.ts.map +0 -1
  270. package/lib/styles/calendar.js.map +0 -1
  271. package/lib/styles/checkbox.d.ts.map +0 -1
  272. package/lib/styles/checkbox.js.map +0 -1
  273. package/lib/styles/copy.d.ts.map +0 -1
  274. package/lib/styles/copy.js.map +0 -1
  275. package/lib/styles/custom-scrollbar.d.ts.map +0 -1
  276. package/lib/styles/custom-scrollbar.js.map +0 -1
  277. package/lib/styles/date-picker.d.ts.map +0 -1
  278. package/lib/styles/date-picker.js.map +0 -1
  279. package/lib/styles/file-upload.d.ts.map +0 -1
  280. package/lib/styles/file-upload.js.map +0 -1
  281. package/lib/styles/icon.d.ts.map +0 -1
  282. package/lib/styles/icon.js.map +0 -1
  283. package/lib/styles/input.d.ts.map +0 -1
  284. package/lib/styles/input.js.map +0 -1
  285. package/lib/styles/modal.d.ts.map +0 -1
  286. package/lib/styles/modal.js.map +0 -1
  287. package/lib/styles/multiselect.d.ts +0 -8
  288. package/lib/styles/multiselect.d.ts.map +0 -1
  289. package/lib/styles/multiselect.js +0 -849
  290. package/lib/styles/multiselect.js.map +0 -1
  291. package/lib/styles/option.d.ts.map +0 -1
  292. package/lib/styles/option.js.map +0 -1
  293. package/lib/styles/popup.d.ts.map +0 -1
  294. package/lib/styles/popup.js.map +0 -1
  295. package/lib/styles/radio.d.ts.map +0 -1
  296. package/lib/styles/radio.js.map +0 -1
  297. package/lib/styles/resize-observer.d.ts.map +0 -1
  298. package/lib/styles/resize-observer.js.map +0 -1
  299. package/lib/styles/scroll-container.d.ts.map +0 -1
  300. package/lib/styles/scroll-container.js.map +0 -1
  301. package/lib/styles/select-base.d.ts.map +0 -1
  302. package/lib/styles/select-base.js.map +0 -1
  303. package/lib/styles/select.d.ts.map +0 -1
  304. package/lib/styles/select.js.map +0 -1
  305. package/lib/styles/step.d.ts.map +0 -1
  306. package/lib/styles/step.js.map +0 -1
  307. package/lib/styles/switch.d.ts.map +0 -1
  308. package/lib/styles/switch.js.map +0 -1
  309. package/lib/styles/tab.d.ts.map +0 -1
  310. package/lib/styles/tab.js.map +0 -1
  311. package/lib/styles/tabs.d.ts.map +0 -1
  312. package/lib/styles/tabs.js.map +0 -1
  313. package/lib/styles/tag.d.ts.map +0 -1
  314. package/lib/styles/tag.js.map +0 -1
  315. package/lib/styles/textarea.d.ts.map +0 -1
  316. package/lib/styles/textarea.js.map +0 -1
  317. package/lib/styles/tooltip.d.ts.map +0 -1
  318. package/lib/styles/tooltip.js.map +0 -1
  319. package/lib/styles/wizard.d.ts.map +0 -1
  320. package/lib/styles/wizard.js.map +0 -1
  321. package/lib/types/common.d.ts.map +0 -1
  322. package/lib/types/common.js.map +0 -1
  323. package/lib/utils/calendar-utils.d.ts.map +0 -1
  324. package/lib/utils/calendar-utils.js.map +0 -1
  325. package/lib/utils/custom-scrollbar.d.ts.map +0 -1
  326. package/lib/utils/custom-scrollbar.js.map +0 -1
  327. package/lib/utils/flavor-sheet.d.ts.map +0 -1
  328. package/lib/utils/flavor-sheet.js.map +0 -1
  329. package/lib/utils/icon-registry.d.ts.map +0 -1
  330. package/lib/utils/icon-registry.js.map +0 -1
  331. package/lib/utils/icons.d.ts.map +0 -1
  332. package/lib/utils/icons.js.map +0 -1
  333. package/lib/utils/loader-registry.d.ts.map +0 -1
  334. package/lib/utils/loader-registry.js.map +0 -1
  335. package/lib/utils/locale.d.ts.map +0 -1
  336. package/lib/utils/locale.js.map +0 -1
  337. package/lib/utils/mobile.d.ts.map +0 -1
  338. package/lib/utils/mobile.js.map +0 -1
  339. package/lib/utils/number-format.d.ts.map +0 -1
  340. package/lib/utils/number-format.js.map +0 -1
  341. package/lib/utils/parse-boolean.d.ts +0 -39
  342. package/lib/utils/parse-boolean.d.ts.map +0 -1
  343. package/lib/utils/parse-boolean.js +0 -58
  344. package/lib/utils/parse-boolean.js.map +0 -1
  345. package/lib/utils/positioning.d.ts.map +0 -1
  346. package/lib/utils/positioning.js.map +0 -1
  347. package/lib/utils/property-capture.d.ts.map +0 -1
  348. package/lib/utils/property-capture.js.map +0 -1
  349. package/lib/utils/property-manager.d.ts.map +0 -1
  350. package/lib/utils/property-manager.js.map +0 -1
  351. package/lib/utils/resize-observer.d.ts.map +0 -1
  352. package/lib/utils/resize-observer.js.map +0 -1
  353. package/lib/utils/scroll-lock.d.ts.map +0 -1
  354. package/lib/utils/scroll-lock.js.map +0 -1
  355. package/lib/utils/styles.d.ts.map +0 -1
  356. package/lib/utils/styles.js.map +0 -1
  357. package/lib/version.d.ts.map +0 -1
  358. package/lib/version.js.map +0 -1
@@ -1,597 +1,458 @@
1
- /**
2
- * TyCalendarMonth Web Component
3
- * PORTED FROM: cljs/ty/components/calendar_month.cljs
4
- *
5
- * A stateless, property-driven calendar month renderer with customizable
6
- * day content and styling.
7
- *
8
- * Features:
9
- * - 42-day grid (6 weeks × 7 days) with Monday-first ordering
10
- * - Localized weekday headers
11
- * - Custom day content rendering via dayContentFn
12
- * - Custom day classes via dayClassesFn
13
- * - Custom CSS injection via customCSS
14
- * - Responsive width system (width, minWidth, maxWidth)
15
- * - Rich day-click events with full context
16
- *
17
- * @example
18
- * ```html
19
- * <!-- Basic usage -->
20
- * <ty-calendar-month
21
- * display-year="2025"
22
- * display-month="10"
23
- * locale="en-US">
24
- * </ty-calendar-month>
25
- *
26
- * <!-- With custom rendering -->
27
- * <ty-calendar-month id="custom"></ty-calendar-month>
28
- * <script type="module">
29
- * const cal = document.getElementById('custom');
30
- * cal.dayContentFn = (ctx) => {
31
- * const el = document.createElement('div');
32
- * el.textContent = ctx.dayInMonth;
33
- * return el;
34
- * };
35
- * </script>
36
- * ```
37
- */
38
1
  import { ensureStyles } from '../utils/styles.js';
39
2
  import { syncCustomFlavorSheet } from '../utils/flavor-sheet.js';
40
3
  import { calendarMonthStyles, calendarMonthCustomFlavorCss } from '../styles/calendar-month.js';
41
4
  import { getCalendarMonthDays, getLocalizedWeekdays, parseISODate } from '../utils/calendar-utils.js';
42
5
  import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
43
- // ============================================================================
44
- // Default Render Functions
45
- // ============================================================================
46
- /**
47
- * Default day content renderer - just the day number
48
- */
49
6
  function defaultDayContent(dayContext) {
50
- return dayContext.dayInMonth.toString();
7
+ return dayContext.dayInMonth.toString();
51
8
  }
52
- /**
53
- * Default day classes based on day context
54
- */
55
9
  function getDefaultDayClasses(dayContext) {
56
- const classes = ['calendar-day'];
57
- if (dayContext.today)
58
- classes.push('today');
59
- if (dayContext.weekend)
60
- classes.push('weekend');
61
- if (dayContext.otherMonth)
62
- classes.push('other-month');
63
- if (dayContext.isSelected)
64
- classes.push('selected');
65
- return classes;
66
- }
67
- // ============================================================================
68
- // Helper Functions
69
- // ============================================================================
70
- /**
71
- * Check if value is a DOM element
72
- */
10
+ const classes = ['calendar-day'];
11
+ if (dayContext.today)
12
+ classes.push('today');
13
+ if (dayContext.weekend)
14
+ classes.push('weekend');
15
+ if (dayContext.otherMonth)
16
+ classes.push('other-month');
17
+ if (dayContext.isSelected)
18
+ classes.push('selected');
19
+ return classes;
20
+ }
73
21
  function isDOMElement(value) {
74
- return value instanceof HTMLElement;
22
+ return value instanceof HTMLElement;
75
23
  }
76
- /**
77
- * ISO date string ("YYYY-MM-DD") → UTC-midnight timestamp, matching
78
- * DayContext.value semantics. null when unset/invalid.
79
- */
80
24
  function isoToUTCTimestamp(iso) {
81
- if (!iso)
82
- return null;
83
- const parsed = parseISODate(iso);
84
- if (!parsed)
85
- return null;
86
- return Date.UTC(parsed.year, parsed.month - 1, parsed.day);
87
- }
88
- /**
89
- * Normalize size value - add 'px' if just a number
90
- */
25
+ if (!iso)
26
+ return null;
27
+ const parsed = parseISODate(iso);
28
+ if (!parsed)
29
+ return null;
30
+ return Date.UTC(parsed.year, parsed.month - 1, parsed.day);
31
+ }
91
32
  function normalizeSizeValue(value) {
92
- if (!value || value === '')
93
- return null;
94
- const strValue = String(value);
95
- // If it's just a number, add 'px'
96
- if (/^\d+(\.\d+)?$/.test(strValue)) {
97
- return `${strValue}px`;
98
- }
99
- // Otherwise use as-is (supports %, rem, vw, etc.)
100
- return strValue;
101
- }
102
- // ============================================================================
103
- // Component Implementation
104
- // ============================================================================
105
- /**
106
- * TyCalendarMonth Web Component
107
- */
33
+ if (!value || value === '')
34
+ return null;
35
+ const strValue = String(value);
36
+ if (/^\d+(\.\d+)?$/.test(strValue)) {
37
+ return `${strValue}px`;
38
+ }
39
+ return strValue;
40
+ }
108
41
  export class TyCalendarMonth extends HTMLElement {
109
- /**
110
- * Observed attributes (minimal - mainly for debugging)
111
- * Properties are the primary API
112
- */
113
- static get observedAttributes() {
114
- return ['locale', 'size', 'min', 'max', 'flavor'];
115
- }
116
- constructor() {
117
- super();
118
- this._locale = 'en-US';
119
- this._size = 'md';
120
- this._customFlavorSheet = null;
121
- // ARIA grid roving-tabindex state — index into the flat 42-day array.
122
- // null means "not yet resolved"; render() picks a sensible default
123
- // (selected day, else today, else the 1st rendered day) and persists it
124
- // across re-renders so keyboard focus survives unrelated prop changes.
125
- // Reset to null on month/year navigation (see the property setters below)
126
- // so the new month gets a fresh default instead of a stale index.
127
- this._focusedIndex = null;
128
- this._cellElements = [];
129
- // Initialize with current date
130
- const today = new Date();
131
- this._displayYear = today.getFullYear();
132
- this._displayMonth = today.getMonth() + 1; // 1-based month
133
- this.attachShadow({ mode: 'open' });
134
- }
135
- // ==========================================================================
136
- // Lifecycle Methods
137
- // ==========================================================================
138
- connectedCallback() {
139
- // Set defaults if not already set
140
- if (!this._displayMonth) {
141
- this._displayMonth = new Date().getMonth() + 1;
142
- }
143
- if (!this._displayYear) {
144
- this._displayYear = new Date().getFullYear();
145
- }
146
- if (!this._locale) {
147
- this._locale = this.getAttribute('locale') || 'en-US';
148
- }
149
- // Read size attribute if present
150
- const sizeAttr = this.getAttribute('size');
151
- if (sizeAttr && (sizeAttr === 'sm' || sizeAttr === 'md' || sizeAttr === 'lg')) {
152
- this._size = sizeAttr;
153
- }
154
- // Check for dayContentFn set before upgrade
155
- const plainDayContentFn = this.dayContentFn;
156
- if (plainDayContentFn && !this._dayContentFn) {
157
- this._dayContentFn = plainDayContentFn;
158
- delete this.dayContentFn; // Clean up plain property
159
- }
160
- // Check for customCSS set before upgrade
161
- const plainCustomCSS = this.customCSS;
162
- if (plainCustomCSS && !this._customCSS) {
163
- this._customCSS = plainCustomCSS;
164
- delete this.customCSS; // Clean up plain property
165
- }
166
- // Setup locale observer to watch for ancestor lang changes
167
- this._localeObserver = observeLocaleChanges(this, () => {
168
- this.render();
169
- });
170
- this._syncCustomFlavor();
171
- this.render();
172
- }
173
- disconnectedCallback() {
174
- // Cleanup locale observer
175
- if (this._localeObserver) {
176
- this._localeObserver();
177
- this._localeObserver = undefined;
178
- }
179
- }
180
- attributeChangedCallback(name, _oldValue, newValue) {
181
- if (name === 'locale') {
182
- this._locale = newValue || 'en-US';
183
- this.render();
184
- }
185
- else if (name === 'size') {
186
- if (newValue === 'sm' || newValue === 'md' || newValue === 'lg') {
187
- this._size = newValue;
188
- this.render();
189
- }
190
- }
191
- else if (name === 'min' || name === 'max') {
192
- this[name === 'min' ? '_min' : '_max'] = newValue ?? undefined;
193
- this.render();
194
- }
195
- else if (name === 'flavor') {
196
- this._syncCustomFlavor();
197
- }
198
- }
199
- /**
200
- * Custom (non-built-in) flavors — the `flavor` attribute itself drives
201
- * built-in flavors directly via `:host([flavor="X"])` CSS, no JS needed.
202
- * See utils/flavor-sheet.ts.
203
- */
204
- _syncCustomFlavor() {
205
- this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.getAttribute('flavor'), ({ base }) => calendarMonthCustomFlavorCss(base));
206
- }
207
- // ==========================================================================
208
- // Property Getters/Setters
209
- // ==========================================================================
210
- get displayYear() {
211
- return this._displayYear;
212
- }
213
- set displayYear(value) {
214
- if (this._displayYear !== value) {
215
- this._displayYear = value;
216
- this._focusedIndex = null;
217
- this.render();
218
- }
219
- }
220
- get displayMonth() {
221
- return this._displayMonth;
222
- }
223
- set displayMonth(value) {
224
- if (this._displayMonth !== value) {
225
- this._displayMonth = value;
226
- this._focusedIndex = null;
227
- this.render();
228
- }
229
- }
230
- get locale() {
231
- return getEffectiveLocale(this, this.getAttribute('locale'));
232
- }
233
- set locale(value) {
234
- if (this._locale !== value) {
235
- this._locale = value;
236
- this.render();
237
- }
238
- }
239
- get size() {
240
- return this._size;
241
- }
242
- set size(value) {
243
- if (this._size !== value) {
244
- this._size = value;
245
- this.render();
246
- }
247
- }
248
- get flavor() {
249
- return this.getAttribute('flavor') || 'primary';
250
- }
251
- set flavor(value) {
252
- if (value)
253
- this.setAttribute('flavor', value);
254
- else
255
- this.removeAttribute('flavor');
256
- }
257
- get width() {
258
- return this._width;
259
- }
260
- set width(value) {
261
- if (this._width !== value) {
262
- this._width = value;
263
- this.applyWidthProperties();
264
- }
265
- }
266
- get minWidth() {
267
- return this._minWidth;
268
- }
269
- set minWidth(value) {
270
- if (this._minWidth !== value) {
271
- this._minWidth = value;
272
- this.applyWidthProperties();
273
- }
274
- }
275
- get maxWidth() {
276
- return this._maxWidth;
277
- }
278
- set maxWidth(value) {
279
- if (this._maxWidth !== value) {
280
- this._maxWidth = value;
281
- this.applyWidthProperties();
282
- }
283
- }
284
- get dayContentFn() {
285
- return this._dayContentFn;
286
- }
287
- set dayContentFn(fn) {
288
- if (this._dayContentFn !== fn) {
289
- this._dayContentFn = fn;
290
- this.render();
291
- }
292
- }
293
- get customCSS() {
294
- return this._customCSS;
295
- }
296
- set customCSS(sheet) {
297
- if (this._customCSS !== sheet) {
298
- this._customCSS = sheet;
299
- this.applyCustomCSS();
300
- }
301
- }
302
- get min() {
303
- return this._min;
304
- }
305
- set min(value) {
306
- if (this._min !== value) {
307
- this._min = value;
308
- this.render();
309
- }
310
- }
311
- get max() {
312
- return this._max;
313
- }
314
- set max(value) {
315
- if (this._max !== value) {
316
- this._max = value;
317
- this.render();
318
- }
319
- }
320
- get value() {
321
- return this._value;
322
- }
323
- set value(timestamp) {
324
- const newValue = timestamp ?? undefined;
325
- if (this._value !== newValue) {
326
- this._value = newValue;
327
- this.render();
328
- }
329
- }
330
- // ==========================================================================
331
- // Width Property System
332
- // ==========================================================================
333
- /**
334
- * Apply width-related properties as CSS custom properties
335
- */
336
- applyWidthProperties() {
337
- const normalizedWidth = normalizeSizeValue(this._width);
338
- const normalizedMinWidth = normalizeSizeValue(this._minWidth);
339
- const normalizedMaxWidth = normalizeSizeValue(this._maxWidth);
340
- if (normalizedWidth) {
341
- this.style.setProperty('--calendar-width', normalizedWidth);
342
- }
343
- else {
344
- this.style.removeProperty('--calendar-width');
345
- }
346
- if (normalizedMinWidth) {
347
- this.style.setProperty('--calendar-min-width', normalizedMinWidth);
348
- }
349
- else {
350
- this.style.removeProperty('--calendar-min-width');
351
- }
352
- if (normalizedMaxWidth) {
353
- this.style.setProperty('--calendar-max-width', normalizedMaxWidth);
354
- }
355
- else {
356
- this.style.removeProperty('--calendar-max-width');
357
- }
358
- }
359
- /**
360
- * Apply custom CSS to shadow root
361
- */
362
- applyCustomCSS() {
363
- if (!this._customCSS || !this.shadowRoot)
364
- return;
365
- const existing = this.shadowRoot.adoptedStyleSheets;
366
- if (!existing.includes(this._customCSS)) {
367
- this.shadowRoot.adoptedStyleSheets = [...existing, this._customCSS];
368
- }
369
- }
370
- // ==========================================================================
371
- // Public Methods
372
- // ==========================================================================
373
- /**
374
- * Force re-render of the calendar month
375
- * Useful when external data changes (e.g., async event loading)
376
- */
377
- refresh() {
378
- this.render();
379
- }
380
- // ==========================================================================
381
- // Event Dispatching
382
- // ==========================================================================
383
- /**
384
- * Dispatch day-click custom event with day context
385
- */
386
- dispatchDayClick(dayContext, _domEvent) {
387
- const detail = {
388
- dayContext,
389
- value: dayContext.value,
390
- year: dayContext.year,
391
- month: dayContext.month,
392
- day: dayContext.dayInMonth,
393
- isHoliday: dayContext.holiday,
394
- isToday: dayContext.today,
395
- isWeekend: dayContext.weekend,
396
- isOtherMonth: dayContext.otherMonth,
397
- };
398
- // Dispatch both 'day-click' and 'day-select' (alias)
399
- for (const eventType of ['day-click', 'day-select']) {
400
- const event = new CustomEvent(eventType, {
401
- detail,
402
- bubbles: true,
403
- cancelable: true,
404
- });
405
- this.dispatchEvent(event);
406
- }
407
- }
408
- // ==========================================================================
409
- // Rendering
410
- // ==========================================================================
411
- /**
412
- * Render a single day cell
413
- */
414
- /**
415
- * WAI-ARIA grid pattern: gridcell + roving tabindex (exactly one cell in
416
- * the whole grid is tabindex="0" at a time — the rest are -1, so Tab
417
- * enters/exits the grid in one hop and arrow keys move focus inside it).
418
- * Disabled days are excluded from the roving set entirely.
419
- */
420
- renderDayCell(dayContext, disabled, isRoving) {
421
- const dayElement = document.createElement('div');
422
- // Get content using custom or default function
423
- const content = this._dayContentFn
424
- ? this._dayContentFn(dayContext)
425
- : defaultDayContent(dayContext);
426
- // STRICT DOM-ONLY CHECK for custom functions
427
- if (this._dayContentFn && content && !isDOMElement(content) && typeof content !== 'string') {
428
- throw new Error(`Custom dayContentFn must return a DOM element or string, got: ${typeof content}. ` +
429
- `Use document.createElement() to create DOM elements.`);
430
- }
431
- // Apply default classes
432
- const classes = getDefaultDayClasses(dayContext);
433
- if (disabled)
434
- classes.push('disabled');
435
- dayElement.className = classes.join(' ');
436
- // Set content
437
- if (typeof content === 'string') {
438
- dayElement.textContent = content;
439
- }
440
- else if (isDOMElement(content)) {
441
- dayElement.appendChild(content);
442
- }
443
- dayElement.setAttribute('role', 'gridcell');
444
- dayElement.setAttribute('aria-label', new Intl.DateTimeFormat(this._locale, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })
445
- .format(new Date(dayContext.localValue)));
446
- dayElement.setAttribute('aria-selected', String(!!dayContext.isSelected));
447
- if (dayContext.today)
448
- dayElement.setAttribute('aria-current', 'date');
449
- if (disabled) {
450
- dayElement.setAttribute('aria-disabled', 'true');
451
- dayElement.setAttribute('tabindex', '-1');
452
- }
453
- else {
454
- dayElement.setAttribute('tabindex', isRoving ? '0' : '-1');
455
- // Add click handler (disabled days don't emit)
456
- dayElement.addEventListener('pointerdown', (event) => {
457
- event.preventDefault();
458
- this.dispatchDayClick(dayContext, event);
459
- });
460
- dayElement.addEventListener('keydown', (event) => {
461
- const ke = event;
462
- if (ke.key === 'Enter' || ke.key === ' ') {
463
- ke.preventDefault();
464
- this.dispatchDayClick(dayContext, event);
465
- }
466
- });
467
- }
468
- return dayElement;
469
- }
470
- /**
471
- * Main render function - property-based approach
472
- */
473
- render() {
474
- const root = this.shadowRoot;
475
- if (!root)
476
- return;
477
- // Ensure styles are loaded
478
- ensureStyles(root, { css: calendarMonthStyles, id: 'ty-calendar-month' });
479
- // Apply custom CSS if provided
480
- this.applyCustomCSS();
481
- // Apply width properties
482
- this.applyWidthProperties();
483
- // Convert timestamp to selection info
484
- let selection;
485
- if (this._value) {
486
- const date = new Date(this._value);
487
- selection = {
488
- year: date.getFullYear(),
489
- month: date.getMonth() + 1, // Convert to 1-based
490
- day: date.getDate()
491
- };
492
- }
493
- // Generate calendar days with selection info
494
- const days = getCalendarMonthDays(this._displayYear, this._displayMonth, selection);
495
- // Get localized weekday names
496
- const weekdays = getLocalizedWeekdays(this._locale, 'short');
497
- // Clear and rebuild
498
- root.innerHTML = '';
499
- // Create unified flex container
500
- const calendarContainer = document.createElement('div');
501
- calendarContainer.className = `calendar-flex-container calendar-size-${this._size}`;
502
- calendarContainer.setAttribute('role', 'grid');
503
- calendarContainer.setAttribute('aria-label', new Intl.DateTimeFormat(this._locale, { month: 'long', year: 'numeric' })
504
- .format(new Date(this._displayYear, this._displayMonth - 1, 1)));
505
- // Create header row with weekday names
506
- const headerRow = document.createElement('div');
507
- headerRow.className = 'calendar-row calendar-header-row';
508
- headerRow.setAttribute('role', 'row');
509
- weekdays.forEach(weekday => {
510
- const headerCell = document.createElement('div');
511
- headerCell.className = 'calendar-cell calendar-header-cell';
512
- headerCell.setAttribute('role', 'columnheader');
513
- headerCell.textContent = weekday;
514
- headerRow.appendChild(headerCell);
515
- });
516
- calendarContainer.appendChild(headerRow);
517
- // Create day rows (6 weeks of 7 days each)
518
- const dayWeeks = [];
519
- for (let i = 0; i < 42; i += 7) {
520
- dayWeeks.push(days.slice(i, i + 7));
521
- }
522
- // Bounds for disabling days (UTC-midnight, same space as DayContext.value)
523
- const minTs = isoToUTCTimestamp(this._min);
524
- const maxTs = isoToUTCTimestamp(this._max);
525
- // Roving-tabindex target: keep the persisted index if still valid and
526
- // not disabled, else fall back to selected day, else today, else the
527
- // first rendered day — always something focusable exists in the grid.
528
- const disabledAt = (i) => {
529
- const v = days[i].value;
530
- return (minTs !== null && v < minTs) || (maxTs !== null && v > maxTs);
531
- };
532
- let defaultIndex = days.findIndex(d => d.isSelected);
533
- if (defaultIndex === -1)
534
- defaultIndex = days.findIndex(d => d.today);
535
- if (defaultIndex === -1)
536
- defaultIndex = 0;
537
- if (this._focusedIndex === null ||
538
- this._focusedIndex < 0 ||
539
- this._focusedIndex > 41 ||
540
- disabledAt(this._focusedIndex)) {
541
- this._focusedIndex = defaultIndex;
542
- }
543
- this._cellElements = [];
544
- let flatIndex = 0;
545
- dayWeeks.forEach(week => {
546
- const dayRow = document.createElement('div');
547
- dayRow.className = 'calendar-row calendar-day-row';
548
- dayRow.setAttribute('role', 'row');
549
- week.forEach(dayContext => {
550
- const disabled = (minTs !== null && dayContext.value < minTs) ||
551
- (maxTs !== null && dayContext.value > maxTs);
552
- const dayCell = this.renderDayCell(dayContext, disabled, flatIndex === this._focusedIndex);
553
- // Add unified cell classes for flex layout
554
- dayCell.className = `${dayCell.className} calendar-cell calendar-day-cell`;
555
- dayRow.appendChild(dayCell);
556
- this._cellElements[flatIndex] = dayCell;
557
- flatIndex++;
558
- });
559
- calendarContainer.appendChild(dayRow);
560
- });
561
- // Arrow-key roving-tabindex navigation: Left/Right move a day,
562
- // Up/Down move a week — pure index arithmetic works because `days` is a
563
- // flat, chronologically-ordered 42-cell array (Monday-first weeks), so
564
- // index+1 is always the next calendar day and index+7 the next week.
565
- // Delegated on the grid (one listener, not 42) — this component is
566
- // stateless about which month is shown, so moving past the rendered
567
- // 42-day range just clamps at the edge rather than crossing months.
568
- calendarContainer.addEventListener('keydown', (event) => {
569
- const ke = event;
570
- const delta = ke.key === 'ArrowLeft' ? -1 :
571
- ke.key === 'ArrowRight' ? 1 :
572
- ke.key === 'ArrowUp' ? -7 :
573
- ke.key === 'ArrowDown' ? 7 :
574
- 0;
575
- if (delta === 0)
576
- return;
577
- const currentIndex = this._focusedIndex ?? defaultIndex;
578
- const nextIndex = currentIndex + delta;
579
- if (nextIndex < 0 || nextIndex > 41 || disabledAt(nextIndex))
580
- return;
581
- ke.preventDefault();
582
- this._cellElements[currentIndex]?.setAttribute('tabindex', '-1');
583
- this._focusedIndex = nextIndex;
584
- const nextCell = this._cellElements[nextIndex];
585
- if (nextCell) {
586
- nextCell.setAttribute('tabindex', '0');
587
- nextCell.focus();
588
- }
589
- });
590
- root.appendChild(calendarContainer);
591
- }
592
- }
593
- // Register the custom element
42
+ static get observedAttributes() {
43
+ return ['display-year', 'display-month', 'locale', 'size', 'min', 'max', 'flavor'];
44
+ }
45
+ constructor() {
46
+ super();
47
+ this._locale = 'en-US';
48
+ this._size = 'md';
49
+ this._customFlavorSheet = null;
50
+ this._focusedIndex = null;
51
+ this._cellElements = [];
52
+ const today = new Date();
53
+ this._displayYear = today.getFullYear();
54
+ this._displayMonth = today.getMonth() + 1;
55
+ this.attachShadow({ mode: 'open' });
56
+ }
57
+ connectedCallback() {
58
+ if (!this._displayMonth) {
59
+ this._displayMonth = new Date().getMonth() + 1;
60
+ }
61
+ if (!this._displayYear) {
62
+ this._displayYear = new Date().getFullYear();
63
+ }
64
+ if (!this._locale) {
65
+ this._locale = this.getAttribute('locale') || 'en-US';
66
+ }
67
+ const sizeAttr = this.getAttribute('size');
68
+ if (sizeAttr && (sizeAttr === 'sm' || sizeAttr === 'md' || sizeAttr === 'lg')) {
69
+ this._size = sizeAttr;
70
+ }
71
+ const plainDayContentFn = this.dayContentFn;
72
+ if (plainDayContentFn && !this._dayContentFn) {
73
+ this._dayContentFn = plainDayContentFn;
74
+ delete this.dayContentFn;
75
+ }
76
+ const plainCustomCSS = this.customCSS;
77
+ if (plainCustomCSS && !this._customCSS) {
78
+ this._customCSS = plainCustomCSS;
79
+ delete this.customCSS;
80
+ }
81
+ this._localeObserver = observeLocaleChanges(this, () => {
82
+ this.render();
83
+ });
84
+ this._syncCustomFlavor();
85
+ this.render();
86
+ }
87
+ disconnectedCallback() {
88
+ if (this._localeObserver) {
89
+ this._localeObserver();
90
+ this._localeObserver = undefined;
91
+ }
92
+ }
93
+ attributeChangedCallback(name, _oldValue, newValue) {
94
+ if (name === 'display-year' || name === 'display-month') {
95
+ const n = Number(newValue);
96
+ const valid = Number.isInteger(n) && (name === 'display-year' ? n > 0 : n >= 1 && n <= 12);
97
+ if (valid) {
98
+ if (name === 'display-year')
99
+ this.displayYear = n;
100
+ else
101
+ this.displayMonth = n;
102
+ }
103
+ }
104
+ else if (name === 'locale') {
105
+ this._locale = newValue || 'en-US';
106
+ this.render();
107
+ }
108
+ else if (name === 'size') {
109
+ if (newValue === 'sm' || newValue === 'md' || newValue === 'lg') {
110
+ this._size = newValue;
111
+ this.render();
112
+ }
113
+ }
114
+ else if (name === 'min' || name === 'max') {
115
+ this[name === 'min' ? '_min' : '_max'] = newValue ?? undefined;
116
+ this.render();
117
+ }
118
+ else if (name === 'flavor') {
119
+ this._syncCustomFlavor();
120
+ }
121
+ }
122
+ _syncCustomFlavor() {
123
+ this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.getAttribute('flavor'), ({ base }) => calendarMonthCustomFlavorCss(base));
124
+ }
125
+ get displayYear() {
126
+ return this._displayYear;
127
+ }
128
+ set displayYear(value) {
129
+ if (this._displayYear !== value) {
130
+ this._displayYear = value;
131
+ this._focusedIndex = null;
132
+ this.render();
133
+ }
134
+ }
135
+ get displayMonth() {
136
+ return this._displayMonth;
137
+ }
138
+ set displayMonth(value) {
139
+ if (this._displayMonth !== value) {
140
+ this._displayMonth = value;
141
+ this._focusedIndex = null;
142
+ this.render();
143
+ }
144
+ }
145
+ get locale() {
146
+ return getEffectiveLocale(this, this.getAttribute('locale'));
147
+ }
148
+ set locale(value) {
149
+ if (this._locale !== value) {
150
+ this._locale = value;
151
+ this.render();
152
+ }
153
+ }
154
+ get size() {
155
+ return this._size;
156
+ }
157
+ set size(value) {
158
+ if (this._size !== value) {
159
+ this._size = value;
160
+ this.render();
161
+ }
162
+ }
163
+ get flavor() {
164
+ return this.getAttribute('flavor') || 'primary';
165
+ }
166
+ set flavor(value) {
167
+ if (value)
168
+ this.setAttribute('flavor', value);
169
+ else
170
+ this.removeAttribute('flavor');
171
+ }
172
+ get width() {
173
+ return this._width;
174
+ }
175
+ set width(value) {
176
+ if (this._width !== value) {
177
+ this._width = value;
178
+ this.applyWidthProperties();
179
+ }
180
+ }
181
+ get minWidth() {
182
+ return this._minWidth;
183
+ }
184
+ set minWidth(value) {
185
+ if (this._minWidth !== value) {
186
+ this._minWidth = value;
187
+ this.applyWidthProperties();
188
+ }
189
+ }
190
+ get maxWidth() {
191
+ return this._maxWidth;
192
+ }
193
+ set maxWidth(value) {
194
+ if (this._maxWidth !== value) {
195
+ this._maxWidth = value;
196
+ this.applyWidthProperties();
197
+ }
198
+ }
199
+ get dayContentFn() {
200
+ return this._dayContentFn;
201
+ }
202
+ set dayContentFn(fn) {
203
+ if (this._dayContentFn !== fn) {
204
+ this._dayContentFn = fn;
205
+ this.render();
206
+ }
207
+ }
208
+ get customCSS() {
209
+ return this._customCSS;
210
+ }
211
+ set customCSS(sheet) {
212
+ if (this._customCSS !== sheet) {
213
+ this._customCSS = sheet;
214
+ this.applyCustomCSS();
215
+ }
216
+ }
217
+ get min() {
218
+ return this._min;
219
+ }
220
+ set min(value) {
221
+ if (this._min !== value) {
222
+ this._min = value;
223
+ this.render();
224
+ }
225
+ }
226
+ get max() {
227
+ return this._max;
228
+ }
229
+ set max(value) {
230
+ if (this._max !== value) {
231
+ this._max = value;
232
+ this.render();
233
+ }
234
+ }
235
+ get value() {
236
+ return this._value;
237
+ }
238
+ set value(timestamp) {
239
+ const newValue = timestamp ?? undefined;
240
+ if (this._value !== newValue) {
241
+ this._value = newValue;
242
+ this.render();
243
+ }
244
+ }
245
+ applyWidthProperties() {
246
+ const normalizedWidth = normalizeSizeValue(this._width);
247
+ const normalizedMinWidth = normalizeSizeValue(this._minWidth);
248
+ const normalizedMaxWidth = normalizeSizeValue(this._maxWidth);
249
+ if (normalizedWidth) {
250
+ this.style.setProperty('--calendar-width', normalizedWidth);
251
+ }
252
+ else {
253
+ this.style.removeProperty('--calendar-width');
254
+ }
255
+ if (normalizedMinWidth) {
256
+ this.style.setProperty('--calendar-min-width', normalizedMinWidth);
257
+ }
258
+ else {
259
+ this.style.removeProperty('--calendar-min-width');
260
+ }
261
+ if (normalizedMaxWidth) {
262
+ this.style.setProperty('--calendar-max-width', normalizedMaxWidth);
263
+ }
264
+ else {
265
+ this.style.removeProperty('--calendar-max-width');
266
+ }
267
+ }
268
+ applyCustomCSS() {
269
+ if (!this._customCSS || !this.shadowRoot)
270
+ return;
271
+ const existing = this.shadowRoot.adoptedStyleSheets;
272
+ if (!existing.includes(this._customCSS)) {
273
+ this.shadowRoot.adoptedStyleSheets = [...existing, this._customCSS];
274
+ }
275
+ }
276
+ refresh() {
277
+ this.render();
278
+ }
279
+ dispatchDayClick(dayContext, _domEvent) {
280
+ const detail = {
281
+ dayContext,
282
+ value: dayContext.value,
283
+ year: dayContext.year,
284
+ month: dayContext.month,
285
+ day: dayContext.dayInMonth,
286
+ isHoliday: dayContext.holiday,
287
+ isToday: dayContext.today,
288
+ isWeekend: dayContext.weekend,
289
+ isOtherMonth: dayContext.otherMonth,
290
+ };
291
+ for (const eventType of ['day-click', 'day-select']) {
292
+ const event = new CustomEvent(eventType, {
293
+ detail,
294
+ bubbles: true,
295
+ cancelable: true,
296
+ });
297
+ this.dispatchEvent(event);
298
+ }
299
+ }
300
+ renderDayCell(dayContext, disabled, isRoving) {
301
+ const dayElement = document.createElement('div');
302
+ const content = this._dayContentFn
303
+ ? this._dayContentFn(dayContext)
304
+ : defaultDayContent(dayContext);
305
+ if (this._dayContentFn && content && !isDOMElement(content) && typeof content !== 'string') {
306
+ throw new Error(`Custom dayContentFn must return a DOM element or string, got: ${typeof content}. ` +
307
+ `Use document.createElement() to create DOM elements.`);
308
+ }
309
+ const classes = getDefaultDayClasses(dayContext);
310
+ if (disabled)
311
+ classes.push('disabled');
312
+ dayElement.className = classes.join(' ');
313
+ if (typeof content === 'string') {
314
+ dayElement.textContent = content;
315
+ }
316
+ else if (isDOMElement(content)) {
317
+ dayElement.appendChild(content);
318
+ }
319
+ dayElement.setAttribute('role', 'gridcell');
320
+ dayElement.setAttribute('aria-label', new Intl.DateTimeFormat(this._locale, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })
321
+ .format(new Date(dayContext.localValue)));
322
+ dayElement.setAttribute('aria-selected', String(!!dayContext.isSelected));
323
+ if (dayContext.today)
324
+ dayElement.setAttribute('aria-current', 'date');
325
+ if (disabled) {
326
+ dayElement.setAttribute('aria-disabled', 'true');
327
+ dayElement.setAttribute('tabindex', '-1');
328
+ }
329
+ else {
330
+ dayElement.setAttribute('tabindex', isRoving ? '0' : '-1');
331
+ dayElement.addEventListener('pointerdown', (event) => {
332
+ event.preventDefault();
333
+ this.dispatchDayClick(dayContext, event);
334
+ });
335
+ dayElement.addEventListener('keydown', (event) => {
336
+ const ke = event;
337
+ if (ke.key === 'Enter' || ke.key === ' ') {
338
+ ke.preventDefault();
339
+ this.dispatchDayClick(dayContext, event);
340
+ }
341
+ });
342
+ }
343
+ return dayElement;
344
+ }
345
+ render() {
346
+ const root = this.shadowRoot;
347
+ if (!root)
348
+ return;
349
+ ensureStyles(root, { css: calendarMonthStyles, id: 'ty-calendar-month' });
350
+ this.applyCustomCSS();
351
+ this.applyWidthProperties();
352
+ let selection;
353
+ if (this._value) {
354
+ const date = new Date(this._value);
355
+ selection = {
356
+ year: date.getFullYear(),
357
+ month: date.getMonth() + 1,
358
+ day: date.getDate()
359
+ };
360
+ }
361
+ const days = getCalendarMonthDays(this._displayYear, this._displayMonth, selection);
362
+ const weekdays = getLocalizedWeekdays(this._locale, 'short');
363
+ root.innerHTML = '';
364
+ const calendarContainer = document.createElement('div');
365
+ calendarContainer.className = `calendar-flex-container calendar-size-${this._size}`;
366
+ calendarContainer.setAttribute('role', 'grid');
367
+ calendarContainer.setAttribute('aria-label', new Intl.DateTimeFormat(this._locale, { month: 'long', year: 'numeric' })
368
+ .format(new Date(this._displayYear, this._displayMonth - 1, 1)));
369
+ const headerRow = document.createElement('div');
370
+ headerRow.className = 'calendar-row calendar-header-row';
371
+ headerRow.setAttribute('role', 'row');
372
+ weekdays.forEach(weekday => {
373
+ const headerCell = document.createElement('div');
374
+ headerCell.className = 'calendar-cell calendar-header-cell';
375
+ headerCell.setAttribute('role', 'columnheader');
376
+ headerCell.textContent = weekday;
377
+ headerRow.appendChild(headerCell);
378
+ });
379
+ calendarContainer.appendChild(headerRow);
380
+ const dayWeeks = [];
381
+ for (let i = 0; i < 42; i += 7) {
382
+ dayWeeks.push(days.slice(i, i + 7));
383
+ }
384
+ const minTs = isoToUTCTimestamp(this._min);
385
+ const maxTs = isoToUTCTimestamp(this._max);
386
+ const disabledAt = (i) => {
387
+ const v = days[i].value;
388
+ return (minTs !== null && v < minTs) || (maxTs !== null && v > maxTs);
389
+ };
390
+ const firstEnabled = (pred) => {
391
+ for (let i = 0; i < 42; i++) {
392
+ if (pred(i) && !disabledAt(i))
393
+ return i;
394
+ }
395
+ return -1;
396
+ };
397
+ let defaultIndex = firstEnabled(i => !!days[i].isSelected);
398
+ let provisional = false;
399
+ if (defaultIndex === -1)
400
+ defaultIndex = firstEnabled(i => !!days[i].today);
401
+ if (defaultIndex === -1) {
402
+ defaultIndex = firstEnabled(() => true);
403
+ provisional = true;
404
+ }
405
+ if (defaultIndex === -1)
406
+ defaultIndex = 0;
407
+ if (this._focusedIndex === null ||
408
+ this._focusedIndex < 0 ||
409
+ this._focusedIndex > 41 ||
410
+ disabledAt(this._focusedIndex)) {
411
+ this._focusedIndex = provisional ? null : defaultIndex;
412
+ }
413
+ const effectiveFocusedIndex = this._focusedIndex ?? defaultIndex;
414
+ this._cellElements = [];
415
+ let flatIndex = 0;
416
+ dayWeeks.forEach(week => {
417
+ const dayRow = document.createElement('div');
418
+ dayRow.className = 'calendar-row calendar-day-row';
419
+ dayRow.setAttribute('role', 'row');
420
+ week.forEach(dayContext => {
421
+ const disabled = (minTs !== null && dayContext.value < minTs) ||
422
+ (maxTs !== null && dayContext.value > maxTs);
423
+ const dayCell = this.renderDayCell(dayContext, disabled, flatIndex === effectiveFocusedIndex);
424
+ dayCell.className = `${dayCell.className} calendar-cell calendar-day-cell`;
425
+ dayRow.appendChild(dayCell);
426
+ this._cellElements[flatIndex] = dayCell;
427
+ flatIndex++;
428
+ });
429
+ calendarContainer.appendChild(dayRow);
430
+ });
431
+ calendarContainer.addEventListener('keydown', (event) => {
432
+ const ke = event;
433
+ const delta = ke.key === 'ArrowLeft' ? -1 :
434
+ ke.key === 'ArrowRight' ? 1 :
435
+ ke.key === 'ArrowUp' ? -7 :
436
+ ke.key === 'ArrowDown' ? 7 :
437
+ 0;
438
+ if (delta === 0)
439
+ return;
440
+ const currentIndex = this._focusedIndex ?? defaultIndex;
441
+ const nextIndex = currentIndex + delta;
442
+ if (nextIndex < 0 || nextIndex > 41 || disabledAt(nextIndex))
443
+ return;
444
+ ke.preventDefault();
445
+ this._cellElements[currentIndex]?.setAttribute('tabindex', '-1');
446
+ this._focusedIndex = nextIndex;
447
+ const nextCell = this._cellElements[nextIndex];
448
+ if (nextCell) {
449
+ nextCell.setAttribute('tabindex', '0');
450
+ nextCell.focus();
451
+ }
452
+ });
453
+ root.appendChild(calendarContainer);
454
+ }
455
+ }
594
456
  if (!customElements.get('ty-calendar-month')) {
595
- customElements.define('ty-calendar-month', TyCalendarMonth);
457
+ customElements.define('ty-calendar-month', TyCalendarMonth);
596
458
  }
597
- //# sourceMappingURL=calendar-month.js.map