tyrell-components 1.0.0-RC10

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 (344) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +221 -0
  3. package/css/tyrell-brand.css +767 -0
  4. package/css/tyrell.css +1679 -0
  5. package/dist/tyrell-brand.css +767 -0
  6. package/dist/tyrell.css +1679 -0
  7. package/dist/tyrell.js +2 -0
  8. package/lib/base/ty-component.d.ts +133 -0
  9. package/lib/base/ty-component.d.ts.map +1 -0
  10. package/lib/base/ty-component.js +297 -0
  11. package/lib/base/ty-component.js.map +1 -0
  12. package/lib/components/button.d.ts +135 -0
  13. package/lib/components/button.d.ts.map +1 -0
  14. package/lib/components/button.js +277 -0
  15. package/lib/components/button.js.map +1 -0
  16. package/lib/components/calendar-month.d.ts +132 -0
  17. package/lib/components/calendar-month.d.ts.map +1 -0
  18. package/lib/components/calendar-month.js +440 -0
  19. package/lib/components/calendar-month.js.map +1 -0
  20. package/lib/components/calendar-navigation.d.ts +137 -0
  21. package/lib/components/calendar-navigation.d.ts.map +1 -0
  22. package/lib/components/calendar-navigation.js +366 -0
  23. package/lib/components/calendar-navigation.js.map +1 -0
  24. package/lib/components/calendar.d.ts +166 -0
  25. package/lib/components/calendar.d.ts.map +1 -0
  26. package/lib/components/calendar.js +774 -0
  27. package/lib/components/calendar.js.map +1 -0
  28. package/lib/components/checkbox.d.ts +189 -0
  29. package/lib/components/checkbox.d.ts.map +1 -0
  30. package/lib/components/checkbox.js +400 -0
  31. package/lib/components/checkbox.js.map +1 -0
  32. package/lib/components/copy.d.ts +180 -0
  33. package/lib/components/copy.d.ts.map +1 -0
  34. package/lib/components/copy.js +393 -0
  35. package/lib/components/copy.js.map +1 -0
  36. package/lib/components/date-picker.d.ts +379 -0
  37. package/lib/components/date-picker.d.ts.map +1 -0
  38. package/lib/components/date-picker.js +1586 -0
  39. package/lib/components/date-picker.js.map +1 -0
  40. package/lib/components/dropdown.d.ts +424 -0
  41. package/lib/components/dropdown.d.ts.map +1 -0
  42. package/lib/components/dropdown.js +1640 -0
  43. package/lib/components/dropdown.js.map +1 -0
  44. package/lib/components/file-upload.d.ts +121 -0
  45. package/lib/components/file-upload.d.ts.map +1 -0
  46. package/lib/components/file-upload.js +441 -0
  47. package/lib/components/file-upload.js.map +1 -0
  48. package/lib/components/icon.d.ts +118 -0
  49. package/lib/components/icon.d.ts.map +1 -0
  50. package/lib/components/icon.js +245 -0
  51. package/lib/components/icon.js.map +1 -0
  52. package/lib/components/input.d.ts +270 -0
  53. package/lib/components/input.d.ts.map +1 -0
  54. package/lib/components/input.js +721 -0
  55. package/lib/components/input.js.map +1 -0
  56. package/lib/components/modal.d.ts +78 -0
  57. package/lib/components/modal.d.ts.map +1 -0
  58. package/lib/components/modal.js +497 -0
  59. package/lib/components/modal.js.map +1 -0
  60. package/lib/components/multiselect.d.ts +397 -0
  61. package/lib/components/multiselect.d.ts.map +1 -0
  62. package/lib/components/multiselect.js +1595 -0
  63. package/lib/components/multiselect.js.map +1 -0
  64. package/lib/components/option.d.ts +66 -0
  65. package/lib/components/option.d.ts.map +1 -0
  66. package/lib/components/option.js +314 -0
  67. package/lib/components/option.js.map +1 -0
  68. package/lib/components/popup.d.ts +43 -0
  69. package/lib/components/popup.d.ts.map +1 -0
  70. package/lib/components/popup.js +380 -0
  71. package/lib/components/popup.js.map +1 -0
  72. package/lib/components/radio.d.ts +198 -0
  73. package/lib/components/radio.d.ts.map +1 -0
  74. package/lib/components/radio.js +437 -0
  75. package/lib/components/radio.js.map +1 -0
  76. package/lib/components/resize-observer.d.ts +48 -0
  77. package/lib/components/resize-observer.d.ts.map +1 -0
  78. package/lib/components/resize-observer.js +108 -0
  79. package/lib/components/resize-observer.js.map +1 -0
  80. package/lib/components/scroll-container.d.ts +51 -0
  81. package/lib/components/scroll-container.d.ts.map +1 -0
  82. package/lib/components/scroll-container.js +239 -0
  83. package/lib/components/scroll-container.js.map +1 -0
  84. package/lib/components/step.d.ts +26 -0
  85. package/lib/components/step.d.ts.map +1 -0
  86. package/lib/components/step.js +75 -0
  87. package/lib/components/step.js.map +1 -0
  88. package/lib/components/switch.d.ts +111 -0
  89. package/lib/components/switch.d.ts.map +1 -0
  90. package/lib/components/switch.js +240 -0
  91. package/lib/components/switch.js.map +1 -0
  92. package/lib/components/tab.d.ts +23 -0
  93. package/lib/components/tab.d.ts.map +1 -0
  94. package/lib/components/tab.js +76 -0
  95. package/lib/components/tab.js.map +1 -0
  96. package/lib/components/tabs.d.ts +93 -0
  97. package/lib/components/tabs.d.ts.map +1 -0
  98. package/lib/components/tabs.js +653 -0
  99. package/lib/components/tabs.js.map +1 -0
  100. package/lib/components/tag.d.ts +144 -0
  101. package/lib/components/tag.d.ts.map +1 -0
  102. package/lib/components/tag.js +316 -0
  103. package/lib/components/tag.js.map +1 -0
  104. package/lib/components/textarea.d.ts +241 -0
  105. package/lib/components/textarea.d.ts.map +1 -0
  106. package/lib/components/textarea.js +585 -0
  107. package/lib/components/textarea.js.map +1 -0
  108. package/lib/components/tooltip.d.ts +40 -0
  109. package/lib/components/tooltip.d.ts.map +1 -0
  110. package/lib/components/tooltip.js +439 -0
  111. package/lib/components/tooltip.js.map +1 -0
  112. package/lib/components/wizard.d.ts +86 -0
  113. package/lib/components/wizard.d.ts.map +1 -0
  114. package/lib/components/wizard.js +636 -0
  115. package/lib/components/wizard.js.map +1 -0
  116. package/lib/icons/fontawesome/brands.d.ts +557 -0
  117. package/lib/icons/fontawesome/brands.d.ts.map +1 -0
  118. package/lib/icons/fontawesome/brands.js +557 -0
  119. package/lib/icons/fontawesome/brands.js.map +1 -0
  120. package/lib/icons/fontawesome/regular.d.ts +281 -0
  121. package/lib/icons/fontawesome/regular.d.ts.map +1 -0
  122. package/lib/icons/fontawesome/regular.js +281 -0
  123. package/lib/icons/fontawesome/regular.js.map +1 -0
  124. package/lib/icons/fontawesome/solid.d.ts +1992 -0
  125. package/lib/icons/fontawesome/solid.d.ts.map +1 -0
  126. package/lib/icons/fontawesome/solid.js +1992 -0
  127. package/lib/icons/fontawesome/solid.js.map +1 -0
  128. package/lib/icons/heroicons/micro.d.ts +324 -0
  129. package/lib/icons/heroicons/micro.d.ts.map +1 -0
  130. package/lib/icons/heroicons/micro.js +1032 -0
  131. package/lib/icons/heroicons/micro.js.map +1 -0
  132. package/lib/icons/heroicons/mini.d.ts +332 -0
  133. package/lib/icons/heroicons/mini.d.ts.map +1 -0
  134. package/lib/icons/heroicons/mini.js +1038 -0
  135. package/lib/icons/heroicons/mini.js.map +1 -0
  136. package/lib/icons/heroicons/outline.d.ts +332 -0
  137. package/lib/icons/heroicons/outline.d.ts.map +1 -0
  138. package/lib/icons/heroicons/outline.js +993 -0
  139. package/lib/icons/heroicons/outline.js.map +1 -0
  140. package/lib/icons/heroicons/solid.d.ts +332 -0
  141. package/lib/icons/heroicons/solid.d.ts.map +1 -0
  142. package/lib/icons/heroicons/solid.js +1063 -0
  143. package/lib/icons/heroicons/solid.js.map +1 -0
  144. package/lib/icons/lucide.d.ts +1872 -0
  145. package/lib/icons/lucide.d.ts.map +1 -0
  146. package/lib/icons/lucide.js +28212 -0
  147. package/lib/icons/lucide.js.map +1 -0
  148. package/lib/icons/material/filled.d.ts +2180 -0
  149. package/lib/icons/material/filled.d.ts.map +1 -0
  150. package/lib/icons/material/filled.js +14003 -0
  151. package/lib/icons/material/filled.js.map +1 -0
  152. package/lib/icons/material/outlined.d.ts +2142 -0
  153. package/lib/icons/material/outlined.d.ts.map +1 -0
  154. package/lib/icons/material/outlined.js +14545 -0
  155. package/lib/icons/material/outlined.js.map +1 -0
  156. package/lib/icons/material/round.d.ts +2147 -0
  157. package/lib/icons/material/round.d.ts.map +1 -0
  158. package/lib/icons/material/round.js +14779 -0
  159. package/lib/icons/material/round.js.map +1 -0
  160. package/lib/icons/material/sharp.d.ts +2147 -0
  161. package/lib/icons/material/sharp.d.ts.map +1 -0
  162. package/lib/icons/material/sharp.js +14189 -0
  163. package/lib/icons/material/sharp.js.map +1 -0
  164. package/lib/icons/material/two-tone.d.ts +2185 -0
  165. package/lib/icons/material/two-tone.d.ts.map +1 -0
  166. package/lib/icons/material/two-tone.js +17152 -0
  167. package/lib/icons/material/two-tone.js.map +1 -0
  168. package/lib/index.d.ts +86 -0
  169. package/lib/index.d.ts.map +1 -0
  170. package/lib/index.js +78 -0
  171. package/lib/index.js.map +1 -0
  172. package/lib/styles/button.d.ts +14 -0
  173. package/lib/styles/button.d.ts.map +1 -0
  174. package/lib/styles/button.js +498 -0
  175. package/lib/styles/button.js.map +1 -0
  176. package/lib/styles/calendar-month.d.ts +6 -0
  177. package/lib/styles/calendar-month.d.ts.map +1 -0
  178. package/lib/styles/calendar-month.js +275 -0
  179. package/lib/styles/calendar-month.js.map +1 -0
  180. package/lib/styles/calendar-navigation.d.ts +6 -0
  181. package/lib/styles/calendar-navigation.d.ts.map +1 -0
  182. package/lib/styles/calendar-navigation.js +143 -0
  183. package/lib/styles/calendar-navigation.js.map +1 -0
  184. package/lib/styles/calendar.d.ts +6 -0
  185. package/lib/styles/calendar.d.ts.map +1 -0
  186. package/lib/styles/calendar.js +28 -0
  187. package/lib/styles/calendar.js.map +1 -0
  188. package/lib/styles/checkbox.d.ts +9 -0
  189. package/lib/styles/checkbox.d.ts.map +1 -0
  190. package/lib/styles/checkbox.js +19 -0
  191. package/lib/styles/checkbox.js.map +1 -0
  192. package/lib/styles/copy.d.ts +7 -0
  193. package/lib/styles/copy.d.ts.map +1 -0
  194. package/lib/styles/copy.js +94 -0
  195. package/lib/styles/copy.js.map +1 -0
  196. package/lib/styles/custom-scrollbar.d.ts +6 -0
  197. package/lib/styles/custom-scrollbar.d.ts.map +1 -0
  198. package/lib/styles/custom-scrollbar.js +157 -0
  199. package/lib/styles/custom-scrollbar.js.map +1 -0
  200. package/lib/styles/date-picker.d.ts +6 -0
  201. package/lib/styles/date-picker.d.ts.map +1 -0
  202. package/lib/styles/date-picker.js +438 -0
  203. package/lib/styles/date-picker.js.map +1 -0
  204. package/lib/styles/dropdown.d.ts +12 -0
  205. package/lib/styles/dropdown.d.ts.map +1 -0
  206. package/lib/styles/dropdown.js +1081 -0
  207. package/lib/styles/dropdown.js.map +1 -0
  208. package/lib/styles/file-upload.d.ts +2 -0
  209. package/lib/styles/file-upload.d.ts.map +1 -0
  210. package/lib/styles/file-upload.js +241 -0
  211. package/lib/styles/file-upload.js.map +1 -0
  212. package/lib/styles/icon.d.ts +6 -0
  213. package/lib/styles/icon.d.ts.map +1 -0
  214. package/lib/styles/icon.js +241 -0
  215. package/lib/styles/icon.js.map +1 -0
  216. package/lib/styles/input.d.ts +7 -0
  217. package/lib/styles/input.d.ts.map +1 -0
  218. package/lib/styles/input.js +685 -0
  219. package/lib/styles/input.js.map +1 -0
  220. package/lib/styles/modal.d.ts +8 -0
  221. package/lib/styles/modal.d.ts.map +1 -0
  222. package/lib/styles/modal.js +134 -0
  223. package/lib/styles/modal.js.map +1 -0
  224. package/lib/styles/multiselect.d.ts +6 -0
  225. package/lib/styles/multiselect.d.ts.map +1 -0
  226. package/lib/styles/multiselect.js +825 -0
  227. package/lib/styles/multiselect.js.map +1 -0
  228. package/lib/styles/option.d.ts +6 -0
  229. package/lib/styles/option.d.ts.map +1 -0
  230. package/lib/styles/option.js +116 -0
  231. package/lib/styles/option.js.map +1 -0
  232. package/lib/styles/popup.d.ts +8 -0
  233. package/lib/styles/popup.d.ts.map +1 -0
  234. package/lib/styles/popup.js +95 -0
  235. package/lib/styles/popup.js.map +1 -0
  236. package/lib/styles/radio.d.ts +8 -0
  237. package/lib/styles/radio.d.ts.map +1 -0
  238. package/lib/styles/radio.js +160 -0
  239. package/lib/styles/radio.js.map +1 -0
  240. package/lib/styles/resize-observer.d.ts +6 -0
  241. package/lib/styles/resize-observer.d.ts.map +1 -0
  242. package/lib/styles/resize-observer.js +18 -0
  243. package/lib/styles/resize-observer.js.map +1 -0
  244. package/lib/styles/scroll-container.d.ts +6 -0
  245. package/lib/styles/scroll-container.d.ts.map +1 -0
  246. package/lib/styles/scroll-container.js +198 -0
  247. package/lib/styles/scroll-container.js.map +1 -0
  248. package/lib/styles/step.d.ts +5 -0
  249. package/lib/styles/step.d.ts.map +1 -0
  250. package/lib/styles/step.js +50 -0
  251. package/lib/styles/step.js.map +1 -0
  252. package/lib/styles/switch.d.ts +9 -0
  253. package/lib/styles/switch.d.ts.map +1 -0
  254. package/lib/styles/switch.js +100 -0
  255. package/lib/styles/switch.js.map +1 -0
  256. package/lib/styles/tab.d.ts +5 -0
  257. package/lib/styles/tab.d.ts.map +1 -0
  258. package/lib/styles/tab.js +51 -0
  259. package/lib/styles/tab.js.map +1 -0
  260. package/lib/styles/tabs.d.ts +13 -0
  261. package/lib/styles/tabs.d.ts.map +1 -0
  262. package/lib/styles/tabs.js +184 -0
  263. package/lib/styles/tabs.js.map +1 -0
  264. package/lib/styles/tag.d.ts +6 -0
  265. package/lib/styles/tag.d.ts.map +1 -0
  266. package/lib/styles/tag.js +409 -0
  267. package/lib/styles/tag.js.map +1 -0
  268. package/lib/styles/textarea.d.ts +6 -0
  269. package/lib/styles/textarea.d.ts.map +1 -0
  270. package/lib/styles/textarea.js +350 -0
  271. package/lib/styles/textarea.js.map +1 -0
  272. package/lib/styles/tooltip.d.ts +9 -0
  273. package/lib/styles/tooltip.d.ts.map +1 -0
  274. package/lib/styles/tooltip.js +133 -0
  275. package/lib/styles/tooltip.js.map +1 -0
  276. package/lib/styles/wizard.d.ts +25 -0
  277. package/lib/styles/wizard.d.ts.map +1 -0
  278. package/lib/styles/wizard.js +348 -0
  279. package/lib/styles/wizard.js.map +1 -0
  280. package/lib/types/common.d.ts +143 -0
  281. package/lib/types/common.d.ts.map +1 -0
  282. package/lib/types/common.js +5 -0
  283. package/lib/types/common.js.map +1 -0
  284. package/lib/utils/calendar-utils.d.ts +176 -0
  285. package/lib/utils/calendar-utils.d.ts.map +1 -0
  286. package/lib/utils/calendar-utils.js +370 -0
  287. package/lib/utils/calendar-utils.js.map +1 -0
  288. package/lib/utils/custom-scrollbar.d.ts +82 -0
  289. package/lib/utils/custom-scrollbar.d.ts.map +1 -0
  290. package/lib/utils/custom-scrollbar.js +320 -0
  291. package/lib/utils/custom-scrollbar.js.map +1 -0
  292. package/lib/utils/icon-registry.d.ts +78 -0
  293. package/lib/utils/icon-registry.d.ts.map +1 -0
  294. package/lib/utils/icon-registry.js +304 -0
  295. package/lib/utils/icon-registry.js.map +1 -0
  296. package/lib/utils/loader-registry.d.ts +35 -0
  297. package/lib/utils/loader-registry.d.ts.map +1 -0
  298. package/lib/utils/loader-registry.js +43 -0
  299. package/lib/utils/loader-registry.js.map +1 -0
  300. package/lib/utils/locale.d.ts +136 -0
  301. package/lib/utils/locale.d.ts.map +1 -0
  302. package/lib/utils/locale.js +213 -0
  303. package/lib/utils/locale.js.map +1 -0
  304. package/lib/utils/mobile.d.ts +14 -0
  305. package/lib/utils/mobile.d.ts.map +1 -0
  306. package/lib/utils/mobile.js +21 -0
  307. package/lib/utils/mobile.js.map +1 -0
  308. package/lib/utils/number-format.d.ts +83 -0
  309. package/lib/utils/number-format.d.ts.map +1 -0
  310. package/lib/utils/number-format.js +143 -0
  311. package/lib/utils/number-format.js.map +1 -0
  312. package/lib/utils/parse-boolean.d.ts +39 -0
  313. package/lib/utils/parse-boolean.d.ts.map +1 -0
  314. package/lib/utils/parse-boolean.js +58 -0
  315. package/lib/utils/parse-boolean.js.map +1 -0
  316. package/lib/utils/positioning.d.ts +143 -0
  317. package/lib/utils/positioning.d.ts.map +1 -0
  318. package/lib/utils/positioning.js +308 -0
  319. package/lib/utils/positioning.js.map +1 -0
  320. package/lib/utils/property-capture.d.ts +132 -0
  321. package/lib/utils/property-capture.d.ts.map +1 -0
  322. package/lib/utils/property-capture.js +152 -0
  323. package/lib/utils/property-capture.js.map +1 -0
  324. package/lib/utils/property-manager.d.ts +90 -0
  325. package/lib/utils/property-manager.d.ts.map +1 -0
  326. package/lib/utils/property-manager.js +197 -0
  327. package/lib/utils/property-manager.js.map +1 -0
  328. package/lib/utils/resize-observer.d.ts +42 -0
  329. package/lib/utils/resize-observer.d.ts.map +1 -0
  330. package/lib/utils/resize-observer.js +71 -0
  331. package/lib/utils/resize-observer.js.map +1 -0
  332. package/lib/utils/scroll-lock.d.ts +79 -0
  333. package/lib/utils/scroll-lock.d.ts.map +1 -0
  334. package/lib/utils/scroll-lock.js +197 -0
  335. package/lib/utils/scroll-lock.js.map +1 -0
  336. package/lib/utils/styles.d.ts +27 -0
  337. package/lib/utils/styles.d.ts.map +1 -0
  338. package/lib/utils/styles.js +53 -0
  339. package/lib/utils/styles.js.map +1 -0
  340. package/lib/version.d.ts +8 -0
  341. package/lib/version.d.ts.map +1 -0
  342. package/lib/version.js +11 -0
  343. package/lib/version.js.map +1 -0
  344. package/package.json +163 -0
@@ -0,0 +1,774 @@
1
+ /**
2
+ * TyCalendar Web Component
3
+ * PORTED FROM: cljs/ty/components/calendar.cljs
4
+ *
5
+ * A complete calendar orchestration component that combines navigation and month display.
6
+ * Manages selection state, form participation, and event coordination.
7
+ *
8
+ * Architecture:
9
+ * - year/month/day HTML attributes for intuitive API
10
+ * - Internal state management (private properties)
11
+ * - Distributes properties to child components (navigation + month)
12
+ * - Single 'change' event with complete day context
13
+ * - Form participation via ElementInternals
14
+ *
15
+ * Features:
16
+ * - Combines ty-calendar-navigation + ty-calendar-month
17
+ * - Date selection with visual feedback
18
+ * - Form integration (works with FormData)
19
+ * - Custom render functions (dayContentFn)
20
+ * - Custom CSS injection
21
+ * - Automatic date validation
22
+ * - Event coordination between components
23
+ *
24
+ * @example
25
+ * ```html
26
+ * <!-- Basic usage -->
27
+ * <ty-calendar
28
+ * year="2025"
29
+ * month="10"
30
+ * day="15">
31
+ * </ty-calendar>
32
+ *
33
+ * <!-- Form integration -->
34
+ * <form>
35
+ * <ty-calendar name="booking-date"></ty-calendar>
36
+ * <button type="submit">Submit</button>
37
+ * </form>
38
+ *
39
+ * <!-- With custom rendering -->
40
+ * <ty-calendar id="custom"></ty-calendar>
41
+ * <script type="module">
42
+ * const cal = document.getElementById('custom');
43
+ * cal.dayContentFn = (ctx) => {
44
+ * const el = document.createElement('div');
45
+ * el.textContent = ctx.dayInMonth;
46
+ * if (ctx.today) el.style.fontWeight = 'bold';
47
+ * return el;
48
+ * };
49
+ * </script>
50
+ * ```
51
+ */
52
+ import { ensureStyles } from '../utils/styles.js';
53
+ import { calendarStyles } from '../styles/calendar.js';
54
+ import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
55
+ // ============================================================================
56
+ // Helper Functions
57
+ // ============================================================================
58
+ /**
59
+ * Get current date for defaults
60
+ */
61
+ function getCurrentDate() {
62
+ const now = new Date();
63
+ return {
64
+ year: now.getFullYear(),
65
+ month: now.getMonth() + 1, // 1-based
66
+ day: now.getDate(),
67
+ };
68
+ }
69
+ /**
70
+ * Validate and parse year from string
71
+ */
72
+ function parseYear(yearStr) {
73
+ if (!yearStr || !/^\d{4}$/.test(yearStr))
74
+ return null;
75
+ return parseInt(yearStr, 10);
76
+ }
77
+ /**
78
+ * Validate and parse month from string (1-12)
79
+ */
80
+ function parseMonth(monthStr) {
81
+ if (!monthStr || !/^\d{1,2}$/.test(monthStr))
82
+ return null;
83
+ const month = parseInt(monthStr, 10);
84
+ return month >= 1 && month <= 12 ? month : null;
85
+ }
86
+ /**
87
+ * Validate and parse day from string (1-31, validated against month)
88
+ */
89
+ function parseDay(dayStr, year, month) {
90
+ if (!dayStr || !/^\d{1,2}$/.test(dayStr))
91
+ return null;
92
+ const day = parseInt(dayStr, 10);
93
+ // Get days in month for validation
94
+ const daysInMonth = new Date(year, month, 0).getDate();
95
+ return day >= 1 && day <= daysInMonth ? day : null;
96
+ }
97
+ /**
98
+ * Format date as ISO string (YYYY-MM-DD) for form submission
99
+ */
100
+ function formatDateISO(year, month, day) {
101
+ const monthStr = month.toString().padStart(2, '0');
102
+ const dayStr = day.toString().padStart(2, '0');
103
+ return `${year}-${monthStr}-${dayStr}`;
104
+ }
105
+ // ============================================================================
106
+ // Component Implementation
107
+ // ============================================================================
108
+ /**
109
+ * TyCalendar Web Component
110
+ */
111
+ export class TyCalendar extends HTMLElement {
112
+ /**
113
+ * Observed attributes
114
+ */
115
+ static get observedAttributes() {
116
+ return ['year', 'month', 'day', 'show-navigation', 'stateless', 'locale', 'name', 'size', 'width'];
117
+ }
118
+ constructor() {
119
+ super();
120
+ this._locale = 'en-US';
121
+ this._showNavigation = true;
122
+ this._stateless = false;
123
+ this._size = 'md';
124
+ // Initialize state with current date
125
+ const current = getCurrentDate();
126
+ this._state = {
127
+ displayYear: current.year,
128
+ displayMonth: current.month,
129
+ };
130
+ this.attachShadow({ mode: 'open' });
131
+ // Attach ElementInternals for form participation
132
+ if ('attachInternals' in this) {
133
+ this._internals = this.attachInternals();
134
+ }
135
+ }
136
+ // ==========================================================================
137
+ // Lifecycle Methods
138
+ // ==========================================================================
139
+ connectedCallback() {
140
+ // Parse initial attributes
141
+ this.initializeFromAttributes();
142
+ // ✅ CHECK: Properties set before custom element upgrade
143
+ // When scripts run before element is fully upgraded, properties are set as plain properties
144
+ // without triggering setters. We need to migrate them to private fields.
145
+ // Check for dayContentFn set before upgrade
146
+ const plainDayContentFn = this.dayContentFn;
147
+ if (plainDayContentFn && !this._dayContentFn) {
148
+ console.log('📦 Migrating calendar dayContentFn set before upgrade');
149
+ this._dayContentFn = plainDayContentFn;
150
+ delete this.dayContentFn; // Clean up plain property
151
+ }
152
+ // Check for customCSS set before upgrade
153
+ const plainCustomCSS = this.customCSS;
154
+ if (plainCustomCSS && !this._customCSS) {
155
+ console.log('📦 Migrating calendar customCSS set before upgrade');
156
+ this._customCSS = plainCustomCSS;
157
+ delete this.customCSS; // Clean up plain property
158
+ }
159
+ // Check for value set before upgrade
160
+ const plainValue = this.value;
161
+ if (plainValue && typeof plainValue === 'string' && plainValue !== this.value) {
162
+ console.log('📦 Migrating calendar value set before upgrade:', plainValue);
163
+ // Parse and set the value properly
164
+ const match = plainValue.match(/^(\d{4})-(\d{2})-(\d{2})$/);
165
+ if (match) {
166
+ const year = parseInt(match[1], 10);
167
+ const month = parseInt(match[2], 10);
168
+ const day = parseInt(match[3], 10);
169
+ this._state.selectedYear = year;
170
+ this._state.selectedMonth = month;
171
+ this._state.selectedDay = day;
172
+ this._state.displayYear = year;
173
+ this._state.displayMonth = month;
174
+ // Update attributes if not in stateless mode
175
+ if (!this._stateless) {
176
+ this.setAttribute('year', year.toString());
177
+ this.setAttribute('month', month.toString());
178
+ this.setAttribute('day', day.toString());
179
+ }
180
+ }
181
+ delete this.value; // Clean up plain property
182
+ }
183
+ // Render the calendar
184
+ this.render();
185
+ // Set initial form value if date is selected
186
+ this.updateFormValue();
187
+ // Setup locale observer to watch for ancestor lang changes
188
+ this._localeObserver = observeLocaleChanges(this, () => {
189
+ this.render();
190
+ });
191
+ }
192
+ disconnectedCallback() {
193
+ // Cleanup locale observer
194
+ if (this._localeObserver) {
195
+ this._localeObserver();
196
+ this._localeObserver = undefined;
197
+ }
198
+ // Cleanup references
199
+ this._navigation = undefined;
200
+ this._monthDisplay = undefined;
201
+ }
202
+ attributeChangedCallback(name, oldValue, newValue) {
203
+ if (oldValue === newValue)
204
+ return;
205
+ switch (name) {
206
+ case 'year':
207
+ case 'month':
208
+ case 'day':
209
+ this.syncStateFromAttributes();
210
+ this.syncChildComponents();
211
+ this.updateFormValue();
212
+ break;
213
+ case 'show-navigation':
214
+ this._showNavigation = newValue !== 'false';
215
+ this.render();
216
+ break;
217
+ case 'stateless':
218
+ this._stateless = newValue !== null && newValue !== 'false';
219
+ // No re-render needed, just changes behavior
220
+ break;
221
+ case 'locale':
222
+ this._locale = newValue || 'en-US';
223
+ this.syncChildComponents();
224
+ break;
225
+ case 'size':
226
+ if (newValue === 'sm' || newValue === 'md' || newValue === 'lg') {
227
+ this._size = newValue;
228
+ // Clear width when size is set (mutually exclusive)
229
+ this._width = undefined;
230
+ this.removeAttribute('width');
231
+ this.syncChildComponents();
232
+ }
233
+ break;
234
+ case 'width':
235
+ this._width = newValue || undefined;
236
+ // Clear size when width is set (mutually exclusive)
237
+ if (newValue) {
238
+ this._size = 'md'; // Reset to default
239
+ this.removeAttribute('size');
240
+ }
241
+ this.syncChildComponents();
242
+ break;
243
+ case 'name':
244
+ // Name change triggers form value update
245
+ this.updateFormValue();
246
+ break;
247
+ }
248
+ }
249
+ // ==========================================================================
250
+ // Property Getters/Setters
251
+ // ==========================================================================
252
+ get year() {
253
+ return this._state.selectedYear;
254
+ }
255
+ set year(value) {
256
+ if (value !== undefined) {
257
+ // In stateless mode, only update display
258
+ if (this._stateless) {
259
+ this._state.displayYear = value;
260
+ this.setAttribute('year', value.toString());
261
+ }
262
+ else {
263
+ // In stateful mode, update both selection and display
264
+ this._state.selectedYear = value;
265
+ this._state.displayYear = value;
266
+ this.setAttribute('year', value.toString());
267
+ }
268
+ }
269
+ else {
270
+ if (!this._stateless) {
271
+ delete this._state.selectedYear;
272
+ }
273
+ this.removeAttribute('year');
274
+ }
275
+ this.syncChildComponents();
276
+ if (!this._stateless) {
277
+ this.updateFormValue();
278
+ }
279
+ }
280
+ get month() {
281
+ return this._state.selectedMonth;
282
+ }
283
+ set month(value) {
284
+ if (value !== undefined) {
285
+ // In stateless mode, only update display
286
+ if (this._stateless) {
287
+ this._state.displayMonth = value;
288
+ this.setAttribute('month', value.toString());
289
+ }
290
+ else {
291
+ // In stateful mode, update both selection and display
292
+ this._state.selectedMonth = value;
293
+ this._state.displayMonth = value;
294
+ this.setAttribute('month', value.toString());
295
+ }
296
+ }
297
+ else {
298
+ if (!this._stateless) {
299
+ delete this._state.selectedMonth;
300
+ }
301
+ this.removeAttribute('month');
302
+ }
303
+ this.syncChildComponents();
304
+ if (!this._stateless) {
305
+ this.updateFormValue();
306
+ }
307
+ }
308
+ get day() {
309
+ return this._state.selectedDay;
310
+ }
311
+ set day(value) {
312
+ if (value !== undefined) {
313
+ this._state.selectedDay = value;
314
+ this.setAttribute('day', value.toString());
315
+ }
316
+ else {
317
+ delete this._state.selectedDay;
318
+ this.removeAttribute('day');
319
+ }
320
+ this.syncChildComponents();
321
+ this.updateFormValue();
322
+ }
323
+ get locale() {
324
+ return getEffectiveLocale(this, this.getAttribute('locale'));
325
+ }
326
+ set locale(value) {
327
+ this._locale = value;
328
+ this.setAttribute('locale', value);
329
+ }
330
+ get showNavigation() {
331
+ return this._showNavigation;
332
+ }
333
+ set showNavigation(value) {
334
+ this._showNavigation = value;
335
+ this.setAttribute('show-navigation', value.toString());
336
+ }
337
+ get stateless() {
338
+ return this._stateless;
339
+ }
340
+ set stateless(value) {
341
+ this._stateless = value;
342
+ if (value) {
343
+ this.setAttribute('stateless', '');
344
+ }
345
+ else {
346
+ this.removeAttribute('stateless');
347
+ }
348
+ }
349
+ get size() {
350
+ return this._size;
351
+ }
352
+ set size(value) {
353
+ if (this._size !== value) {
354
+ this._size = value;
355
+ // Clear width when size is set (mutually exclusive)
356
+ this._width = undefined;
357
+ this.removeAttribute('width');
358
+ this.setAttribute('size', value);
359
+ this.syncChildComponents();
360
+ }
361
+ }
362
+ get width() {
363
+ return this._width;
364
+ }
365
+ set width(value) {
366
+ if (this._width !== value) {
367
+ this._width = value;
368
+ if (value) {
369
+ // Clear size when width is set (mutually exclusive)
370
+ this._size = 'md'; // Reset to default
371
+ this.removeAttribute('size');
372
+ this.setAttribute('width', value);
373
+ }
374
+ else {
375
+ this.removeAttribute('width');
376
+ }
377
+ this.syncChildComponents();
378
+ }
379
+ }
380
+ get dayContentFn() {
381
+ return this._dayContentFn;
382
+ }
383
+ set dayContentFn(fn) {
384
+ this._dayContentFn = fn;
385
+ this.syncChildComponents();
386
+ }
387
+ get customCSS() {
388
+ return this._customCSS;
389
+ }
390
+ set customCSS(sheet) {
391
+ this._customCSS = sheet;
392
+ this.syncChildComponents();
393
+ }
394
+ get value() {
395
+ const { selectedYear, selectedMonth, selectedDay } = this._state;
396
+ if (selectedYear && selectedMonth && selectedDay) {
397
+ return formatDateISO(selectedYear, selectedMonth, selectedDay);
398
+ }
399
+ return '';
400
+ }
401
+ set value(isoDate) {
402
+ if (!isoDate) {
403
+ // Clear selection
404
+ delete this._state.selectedYear;
405
+ delete this._state.selectedMonth;
406
+ delete this._state.selectedDay;
407
+ // In normal mode, also clear attributes
408
+ if (!this._stateless) {
409
+ this.removeAttribute('year');
410
+ this.removeAttribute('month');
411
+ this.removeAttribute('day');
412
+ }
413
+ }
414
+ else {
415
+ // Parse ISO date (YYYY-MM-DD)
416
+ const match = isoDate.match(/^(\d{4})-(\d{2})-(\d{2})$/);
417
+ if (match) {
418
+ const year = parseInt(match[1], 10);
419
+ const month = parseInt(match[2], 10);
420
+ const day = parseInt(match[3], 10);
421
+ // Update internal selection state
422
+ this._state.selectedYear = year;
423
+ this._state.selectedMonth = month;
424
+ this._state.selectedDay = day;
425
+ // Update display to show this month
426
+ this._state.displayYear = year;
427
+ this._state.displayMonth = month;
428
+ // In normal mode, update attributes
429
+ // In stateless mode, skip attribute updates (parent controls attributes)
430
+ if (!this._stateless) {
431
+ this.setAttribute('year', year.toString());
432
+ this.setAttribute('month', month.toString());
433
+ this.setAttribute('day', day.toString());
434
+ }
435
+ }
436
+ }
437
+ this.syncChildComponents();
438
+ // In normal mode, update form value
439
+ if (!this._stateless) {
440
+ this.updateFormValue();
441
+ }
442
+ }
443
+ // ==========================================================================
444
+ // State Management
445
+ // ==========================================================================
446
+ /**
447
+ * Initialize state from HTML attributes on first load
448
+ */
449
+ initializeFromAttributes() {
450
+ const yearStr = this.getAttribute('year');
451
+ const monthStr = this.getAttribute('month');
452
+ const dayStr = this.getAttribute('day');
453
+ const localeStr = this.getAttribute('locale');
454
+ const showNavStr = this.getAttribute('show-navigation');
455
+ const statelessStr = this.getAttribute('stateless');
456
+ const sizeStr = this.getAttribute('size');
457
+ const widthStr = this.getAttribute('width');
458
+ // Parse year
459
+ const year = parseYear(yearStr);
460
+ if (year) {
461
+ this._state.displayYear = year;
462
+ }
463
+ // Parse month
464
+ const month = parseMonth(monthStr);
465
+ if (month) {
466
+ this._state.displayMonth = month;
467
+ }
468
+ // Parse day (only if year and month are valid)
469
+ if (year && month) {
470
+ const day = parseDay(dayStr, year, month);
471
+ if (day) {
472
+ this._state.selectedYear = year;
473
+ this._state.selectedMonth = month;
474
+ this._state.selectedDay = day;
475
+ }
476
+ }
477
+ // Locale
478
+ if (localeStr) {
479
+ this._locale = localeStr;
480
+ }
481
+ // Show navigation
482
+ if (showNavStr) {
483
+ this._showNavigation = showNavStr !== 'false';
484
+ }
485
+ // Stateless mode
486
+ if (statelessStr !== null) {
487
+ this._stateless = statelessStr !== 'false';
488
+ }
489
+ // Size
490
+ if (sizeStr && (sizeStr === 'sm' || sizeStr === 'md' || sizeStr === 'lg')) {
491
+ this._size = sizeStr;
492
+ }
493
+ // Width
494
+ if (widthStr) {
495
+ this._width = widthStr;
496
+ }
497
+ }
498
+ /**
499
+ * Sync state from changed attributes
500
+ */
501
+ syncStateFromAttributes() {
502
+ const yearStr = this.getAttribute('year');
503
+ const monthStr = this.getAttribute('month');
504
+ const dayStr = this.getAttribute('day');
505
+ const year = parseYear(yearStr);
506
+ const month = parseMonth(monthStr);
507
+ // Update display year/month
508
+ if (year)
509
+ this._state.displayYear = year;
510
+ if (month)
511
+ this._state.displayMonth = month;
512
+ // Update selection (only if all three are valid)
513
+ if (year && month) {
514
+ const day = parseDay(dayStr, year, month);
515
+ if (day) {
516
+ this._state.selectedYear = year;
517
+ this._state.selectedMonth = month;
518
+ this._state.selectedDay = day;
519
+ }
520
+ else {
521
+ // Clear selection if day is invalid
522
+ delete this._state.selectedYear;
523
+ delete this._state.selectedMonth;
524
+ delete this._state.selectedDay;
525
+ }
526
+ }
527
+ else {
528
+ // Clear selection if year or month is invalid
529
+ delete this._state.selectedYear;
530
+ delete this._state.selectedMonth;
531
+ delete this._state.selectedDay;
532
+ }
533
+ }
534
+ /**
535
+ * Update child components with current state
536
+ */
537
+ syncChildComponents() {
538
+ // Update navigation
539
+ if (this._navigation) {
540
+ this._navigation.displayMonth = this._state.displayMonth;
541
+ this._navigation.displayYear = this._state.displayYear;
542
+ this._navigation.locale = this.locale;
543
+ this._navigation.size = this._size;
544
+ // Always sync width (set or clear)
545
+ this._navigation.width = this._width;
546
+ }
547
+ // Update month display
548
+ if (this._monthDisplay) {
549
+ this._monthDisplay.displayMonth = this._state.displayMonth;
550
+ this._monthDisplay.displayYear = this._state.displayYear;
551
+ this._monthDisplay.locale = this.locale;
552
+ this._monthDisplay.size = this._size;
553
+ // Always sync width (set or clear)
554
+ this._monthDisplay.width = this._width;
555
+ // Pass render functions
556
+ if (this._dayContentFn) {
557
+ this._monthDisplay.dayContentFn = this._dayContentFn;
558
+ }
559
+ // Pass custom CSS
560
+ if (this._customCSS) {
561
+ this._monthDisplay.customCSS = this._customCSS;
562
+ }
563
+ // Update selection value (convert to Date timestamp)
564
+ const { selectedYear, selectedMonth, selectedDay } = this._state;
565
+ if (selectedYear && selectedMonth && selectedDay) {
566
+ const date = new Date(selectedYear, selectedMonth - 1, selectedDay);
567
+ this._monthDisplay.value = date.getTime();
568
+ }
569
+ else {
570
+ this._monthDisplay.value = null;
571
+ }
572
+ }
573
+ }
574
+ /**
575
+ * Update form value using ElementInternals
576
+ */
577
+ updateFormValue() {
578
+ // Skip form participation in stateless mode
579
+ if (this._stateless)
580
+ return;
581
+ if (!this._internals)
582
+ return;
583
+ const elementName = this.getAttribute('name');
584
+ const { selectedYear, selectedMonth, selectedDay } = this._state;
585
+ if (elementName && selectedYear && selectedMonth && selectedDay) {
586
+ const isoDate = formatDateISO(selectedYear, selectedMonth, selectedDay);
587
+ this._internals.setFormValue(isoDate);
588
+ }
589
+ else {
590
+ this._internals.setFormValue('');
591
+ }
592
+ }
593
+ // ==========================================================================
594
+ // Event Handlers
595
+ // ==========================================================================
596
+ /**
597
+ * Handle navigation change (month/year navigation)
598
+ */
599
+ handleNavigationChange(event) {
600
+ event.preventDefault();
601
+ event.stopPropagation();
602
+ const { month, year } = event.detail;
603
+ // Update display state
604
+ this._state.displayMonth = month;
605
+ this._state.displayYear = year;
606
+ // Sync child components
607
+ this.syncChildComponents();
608
+ // Emit navigate event
609
+ const navigateDetail = {
610
+ month,
611
+ year,
612
+ action: 'navigate',
613
+ source: 'navigation',
614
+ };
615
+ this.dispatchEvent(new CustomEvent('navigate', {
616
+ detail: navigateDetail,
617
+ bubbles: true,
618
+ composed: true,
619
+ cancelable: false,
620
+ }));
621
+ }
622
+ /**
623
+ * Handle day click (day selection)
624
+ */
625
+ handleDayClick(event) {
626
+ event.preventDefault();
627
+ event.stopPropagation();
628
+ const { dayContext, year, month, day } = event.detail;
629
+ // In stateless mode, just re-dispatch the event without updating internal state
630
+ if (this._stateless) {
631
+ // Re-dispatch day-click event for parent to handle
632
+ this.dispatchEvent(new CustomEvent('day-click', {
633
+ detail: event.detail,
634
+ bubbles: true,
635
+ composed: true,
636
+ cancelable: true,
637
+ }));
638
+ return;
639
+ }
640
+ // Normal mode: update selection state
641
+ this._state.selectedYear = year;
642
+ this._state.selectedMonth = month;
643
+ this._state.selectedDay = day;
644
+ this._state.displayYear = year;
645
+ this._state.displayMonth = month;
646
+ // Update HTML attributes
647
+ this.setAttribute('year', year.toString());
648
+ this.setAttribute('month', month.toString());
649
+ this.setAttribute('day', day.toString());
650
+ // Update form value
651
+ this.updateFormValue();
652
+ // Sync child components
653
+ this.syncChildComponents();
654
+ // Emit change event
655
+ const changeDetail = {
656
+ year,
657
+ month,
658
+ day,
659
+ action: 'select',
660
+ source: 'day-click',
661
+ dayContext,
662
+ };
663
+ this.dispatchEvent(new CustomEvent('change', {
664
+ detail: changeDetail,
665
+ bubbles: true,
666
+ composed: true,
667
+ cancelable: false,
668
+ }));
669
+ }
670
+ // ==========================================================================
671
+ // Public Methods
672
+ // ==========================================================================
673
+ /**
674
+ * Force re-render of the calendar
675
+ * Useful after updating dayContentFn or other dynamic properties
676
+ */
677
+ refresh() {
678
+ this.syncChildComponents();
679
+ // Force month display to re-render (for async data updates)
680
+ if (this._monthDisplay && typeof this._monthDisplay.refresh === 'function') {
681
+ this._monthDisplay.refresh();
682
+ }
683
+ }
684
+ // ==========================================================================
685
+ // Rendering
686
+ // ==========================================================================
687
+ /**
688
+ * Create navigation element
689
+ */
690
+ createNavigation() {
691
+ const nav = document.createElement('ty-calendar-navigation');
692
+ // Set properties
693
+ nav.displayMonth = this._state.displayMonth;
694
+ nav.displayYear = this._state.displayYear;
695
+ nav.locale = this.locale;
696
+ nav.size = this._size;
697
+ // Only set width if explicitly provided
698
+ if (this._width) {
699
+ nav.width = this._width;
700
+ }
701
+ // Listen for change events
702
+ nav.addEventListener('change', (e) => this.handleNavigationChange(e));
703
+ // Store reference
704
+ this._navigation = nav;
705
+ return nav;
706
+ }
707
+ /**
708
+ * Create month display element
709
+ */
710
+ createMonthDisplay() {
711
+ const month = document.createElement('ty-calendar-month');
712
+ // Set properties
713
+ month.displayMonth = this._state.displayMonth;
714
+ month.displayYear = this._state.displayYear;
715
+ month.locale = this.locale;
716
+ month.size = this._size;
717
+ // Only set width if explicitly provided
718
+ if (this._width) {
719
+ month.width = this._width;
720
+ }
721
+ // Set selection value if exists
722
+ const { selectedYear, selectedMonth, selectedDay } = this._state;
723
+ if (selectedYear && selectedMonth && selectedDay) {
724
+ const date = new Date(selectedYear, selectedMonth - 1, selectedDay);
725
+ month.value = date.getTime();
726
+ }
727
+ // Pass render functions
728
+ if (this._dayContentFn) {
729
+ month.dayContentFn = this._dayContentFn;
730
+ }
731
+ // Pass custom CSS
732
+ if (this._customCSS) {
733
+ month.customCSS = this._customCSS;
734
+ }
735
+ // Listen for day-click events
736
+ month.addEventListener('day-click', (e) => this.handleDayClick(e));
737
+ // Store reference
738
+ this._monthDisplay = month;
739
+ return month;
740
+ }
741
+ /**
742
+ * Main render function
743
+ */
744
+ render() {
745
+ const root = this.shadowRoot;
746
+ if (!root)
747
+ return;
748
+ // Ensure styles are loaded
749
+ ensureStyles(root, { css: calendarStyles, id: 'ty-calendar' });
750
+ // Clear and rebuild
751
+ root.innerHTML = '';
752
+ // Create main container
753
+ const container = document.createElement('div');
754
+ container.className = 'calendar-container';
755
+ // Add navigation if requested
756
+ if (this._showNavigation) {
757
+ const nav = this.createNavigation();
758
+ container.appendChild(nav);
759
+ }
760
+ // Add month display
761
+ const month = this.createMonthDisplay();
762
+ container.appendChild(month);
763
+ root.appendChild(container);
764
+ }
765
+ }
766
+ /**
767
+ * Form-associated custom element
768
+ */
769
+ TyCalendar.formAssociated = true;
770
+ // Register the custom element
771
+ if (!customElements.get('ty-calendar')) {
772
+ customElements.define('ty-calendar', TyCalendar);
773
+ }
774
+ //# sourceMappingURL=calendar.js.map