svelte-ag 0.0.2-dev.72

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 (313) hide show
  1. package/README.md +6 -0
  2. package/dist/app.css +209 -0
  3. package/dist/app.d.ts +13 -0
  4. package/dist/app.html +12 -0
  5. package/dist/icons.css +6 -0
  6. package/dist/lib/bits/internal/arrays.d.ts +95 -0
  7. package/dist/lib/bits/internal/arrays.js +250 -0
  8. package/dist/lib/bits/internal/arrays.test.d.ts +1 -0
  9. package/dist/lib/bits/internal/arrays.test.js +366 -0
  10. package/dist/lib/bits/internal/attrs.d.ts +22 -0
  11. package/dist/lib/bits/internal/attrs.js +69 -0
  12. package/dist/lib/bits/internal/box-auto-reset.svelte.d.ts +8 -0
  13. package/dist/lib/bits/internal/box-auto-reset.svelte.js +31 -0
  14. package/dist/lib/bits/internal/box.svelte.d.ts +21 -0
  15. package/dist/lib/bits/internal/box.svelte.js +26 -0
  16. package/dist/lib/bits/internal/clamp.d.ts +4 -0
  17. package/dist/lib/bits/internal/clamp.js +6 -0
  18. package/dist/lib/bits/internal/clamp.test.d.ts +1 -0
  19. package/dist/lib/bits/internal/clamp.test.js +31 -0
  20. package/dist/lib/bits/internal/create-event-hook.svelte.d.ts +18 -0
  21. package/dist/lib/bits/internal/create-event-hook.svelte.js +29 -0
  22. package/dist/lib/bits/internal/create-shared-hook.svelte.d.ts +2 -0
  23. package/dist/lib/bits/internal/create-shared-hook.svelte.js +27 -0
  24. package/dist/lib/bits/internal/date-time/announcer.d.ts +7 -0
  25. package/dist/lib/bits/internal/date-time/announcer.js +82 -0
  26. package/dist/lib/bits/internal/date-time/calendar-helpers.svelte.d.ts +201 -0
  27. package/dist/lib/bits/internal/date-time/calendar-helpers.svelte.js +510 -0
  28. package/dist/lib/bits/internal/date-time/field/helpers.d.ts +76 -0
  29. package/dist/lib/bits/internal/date-time/field/helpers.js +378 -0
  30. package/dist/lib/bits/internal/date-time/field/parts.d.ts +6 -0
  31. package/dist/lib/bits/internal/date-time/field/parts.js +9 -0
  32. package/dist/lib/bits/internal/date-time/field/segments.d.ts +51 -0
  33. package/dist/lib/bits/internal/date-time/field/segments.js +128 -0
  34. package/dist/lib/bits/internal/date-time/field/types.d.ts +25 -0
  35. package/dist/lib/bits/internal/date-time/field/types.js +1 -0
  36. package/dist/lib/bits/internal/date-time/formatter.d.ts +24 -0
  37. package/dist/lib/bits/internal/date-time/formatter.js +97 -0
  38. package/dist/lib/bits/internal/date-time/placeholders.d.ts +8 -0
  39. package/dist/lib/bits/internal/date-time/placeholders.js +129 -0
  40. package/dist/lib/bits/internal/date-time/utils.d.ts +69 -0
  41. package/dist/lib/bits/internal/date-time/utils.js +212 -0
  42. package/dist/lib/bits/internal/debounce.d.ts +4 -0
  43. package/dist/lib/bits/internal/debounce.js +19 -0
  44. package/dist/lib/bits/internal/debounce.test.d.ts +1 -0
  45. package/dist/lib/bits/internal/debounce.test.js +50 -0
  46. package/dist/lib/bits/internal/dom.d.ts +10 -0
  47. package/dist/lib/bits/internal/dom.js +38 -0
  48. package/dist/lib/bits/internal/elements.d.ts +2 -0
  49. package/dist/lib/bits/internal/elements.js +6 -0
  50. package/dist/lib/bits/internal/events.d.ts +21 -0
  51. package/dist/lib/bits/internal/events.js +49 -0
  52. package/dist/lib/bits/internal/floating-svelte/floating-utils.svelte.d.ts +7 -0
  53. package/dist/lib/bits/internal/floating-svelte/floating-utils.svelte.js +24 -0
  54. package/dist/lib/bits/internal/floating-svelte/types.d.ts +85 -0
  55. package/dist/lib/bits/internal/floating-svelte/types.js +1 -0
  56. package/dist/lib/bits/internal/floating-svelte/use-floating.svelte.d.ts +2 -0
  57. package/dist/lib/bits/internal/floating-svelte/use-floating.svelte.js +112 -0
  58. package/dist/lib/bits/internal/focus.d.ts +46 -0
  59. package/dist/lib/bits/internal/focus.js +109 -0
  60. package/dist/lib/bits/internal/get-directional-keys.d.ts +21 -0
  61. package/dist/lib/bits/internal/get-directional-keys.js +37 -0
  62. package/dist/lib/bits/internal/get-directional-keys.test.d.ts +1 -0
  63. package/dist/lib/bits/internal/get-directional-keys.test.js +46 -0
  64. package/dist/lib/bits/internal/is.d.ts +25 -0
  65. package/dist/lib/bits/internal/is.js +62 -0
  66. package/dist/lib/bits/internal/is.test.d.ts +1 -0
  67. package/dist/lib/bits/internal/is.test.js +34 -0
  68. package/dist/lib/bits/internal/kbd-constants.d.ts +40 -0
  69. package/dist/lib/bits/internal/kbd-constants.js +40 -0
  70. package/dist/lib/bits/internal/kbd.d.ts +1 -0
  71. package/dist/lib/bits/internal/kbd.js +1 -0
  72. package/dist/lib/bits/internal/locale.d.ts +6 -0
  73. package/dist/lib/bits/internal/locale.js +9 -0
  74. package/dist/lib/bits/internal/math.d.ts +5 -0
  75. package/dist/lib/bits/internal/math.js +43 -0
  76. package/dist/lib/bits/internal/math.test.d.ts +1 -0
  77. package/dist/lib/bits/internal/math.test.js +71 -0
  78. package/dist/lib/bits/internal/noop.d.ts +4 -0
  79. package/dist/lib/bits/internal/noop.js +4 -0
  80. package/dist/lib/bits/internal/should-trap-focus.d.ts +6 -0
  81. package/dist/lib/bits/internal/should-trap-focus.js +6 -0
  82. package/dist/lib/bits/internal/sleep.d.ts +1 -0
  83. package/dist/lib/bits/internal/sleep.js +3 -0
  84. package/dist/lib/bits/internal/tabbable.d.ts +10 -0
  85. package/dist/lib/bits/internal/tabbable.js +66 -0
  86. package/dist/lib/bits/internal/types.d.ts +92 -0
  87. package/dist/lib/bits/internal/types.js +1 -0
  88. package/dist/lib/bits/internal/use-after-animations.svelte.d.ts +5 -0
  89. package/dist/lib/bits/internal/use-after-animations.svelte.js +27 -0
  90. package/dist/lib/bits/internal/use-arrow-navigation.d.ts +62 -0
  91. package/dist/lib/bits/internal/use-arrow-navigation.js +76 -0
  92. package/dist/lib/bits/internal/use-body-scroll-lock.svelte.d.ts +6 -0
  93. package/dist/lib/bits/internal/use-body-scroll-lock.svelte.js +106 -0
  94. package/dist/lib/bits/internal/use-data-typeahead.svelte.d.ts +14 -0
  95. package/dist/lib/bits/internal/use-data-typeahead.svelte.js +31 -0
  96. package/dist/lib/bits/internal/use-dom-typeahead.svelte.d.ts +11 -0
  97. package/dist/lib/bits/internal/use-dom-typeahead.svelte.js +30 -0
  98. package/dist/lib/bits/internal/use-form-control.svelte.d.ts +4 -0
  99. package/dist/lib/bits/internal/use-form-control.svelte.js +16 -0
  100. package/dist/lib/bits/internal/use-grace-area.svelte.d.ts +12 -0
  101. package/dist/lib/bits/internal/use-grace-area.svelte.js +197 -0
  102. package/dist/lib/bits/internal/use-id.d.ts +4 -0
  103. package/dist/lib/bits/internal/use-id.js +8 -0
  104. package/dist/lib/bits/internal/use-resize-observer.svelte.d.ts +2 -0
  105. package/dist/lib/bits/internal/use-resize-observer.svelte.js +17 -0
  106. package/dist/lib/bits/internal/use-roving-focus.svelte.d.ts +38 -0
  107. package/dist/lib/bits/internal/use-roving-focus.svelte.js +91 -0
  108. package/dist/lib/bits/internal/use-size.svelte.d.ts +7 -0
  109. package/dist/lib/bits/internal/use-size.svelte.js +54 -0
  110. package/dist/lib/bits/internal/use-state-machine.svelte.d.ts +24 -0
  111. package/dist/lib/bits/internal/use-state-machine.svelte.js +28 -0
  112. package/dist/lib/bits/internal/use-timeout-fn.svelte.d.ts +25 -0
  113. package/dist/lib/bits/internal/use-timeout-fn.svelte.js +39 -0
  114. package/dist/lib/components/Typeahead.svelte.d.ts +47 -0
  115. package/dist/lib/components/Typeahead.svelte.js +150 -0
  116. package/dist/lib/components/animated/animated.svelte +244 -0
  117. package/dist/lib/components/animated/animated.svelte.d.ts +61 -0
  118. package/dist/lib/components/animated/index.d.ts +2 -0
  119. package/dist/lib/components/animated/index.js +2 -0
  120. package/dist/lib/components/combo/combo.svelte +186 -0
  121. package/dist/lib/components/combo/combo.svelte.d.ts +21 -0
  122. package/dist/lib/components/combo/index.d.ts +2 -0
  123. package/dist/lib/components/combo/index.js +2 -0
  124. package/dist/lib/components/dnd/Droppable.svelte +25 -0
  125. package/dist/lib/components/dnd/Droppable.svelte.d.ts +10 -0
  126. package/dist/lib/components/dnd/context.svelte.d.ts +22 -0
  127. package/dist/lib/components/dnd/context.svelte.js +25 -0
  128. package/dist/lib/components/dnd/dnd-context.svelte +45 -0
  129. package/dist/lib/components/dnd/dnd-context.svelte.d.ts +30 -0
  130. package/dist/lib/components/dnd/dnd-drag-overlay.svelte +44 -0
  131. package/dist/lib/components/dnd/dnd-drag-overlay.svelte.d.ts +27 -0
  132. package/dist/lib/components/dnd/dnd-drag-placeholder.svelte +24 -0
  133. package/dist/lib/components/dnd/dnd-drag-placeholder.svelte.d.ts +9 -0
  134. package/dist/lib/components/dnd/dnd-draghandle.svelte +30 -0
  135. package/dist/lib/components/dnd/dnd-draghandle.svelte.d.ts +6 -0
  136. package/dist/lib/components/dnd/dnd-overlay.svelte +0 -0
  137. package/dist/lib/components/dnd/dnd-overlay.svelte.d.ts +26 -0
  138. package/dist/lib/components/dnd/dnd-sortable-context.svelte +18 -0
  139. package/dist/lib/components/dnd/dnd-sortable-context.svelte.d.ts +8 -0
  140. package/dist/lib/components/dnd/dnd-sortable-item.svelte +68 -0
  141. package/dist/lib/components/dnd/dnd-sortable-item.svelte.d.ts +23 -0
  142. package/dist/lib/components/dnd/example.svelte +109 -0
  143. package/dist/lib/components/dnd/example.svelte.d.ts +3 -0
  144. package/dist/lib/components/dnd/exports.d.ts +9 -0
  145. package/dist/lib/components/dnd/exports.js +9 -0
  146. package/dist/lib/components/dnd/index.d.ts +1 -0
  147. package/dist/lib/components/dnd/index.js +1 -0
  148. package/dist/lib/components/dnd/sortable.svelte.d.ts +13 -0
  149. package/dist/lib/components/dnd/sortable.svelte.js +70 -0
  150. package/dist/lib/components/dnd/utils.svelte.d.ts +20 -0
  151. package/dist/lib/components/dnd/utils.svelte.js +20 -0
  152. package/dist/lib/components/search/combinations/searchPopover.svelte +68 -0
  153. package/dist/lib/components/search/combinations/searchPopover.svelte.d.ts +22 -0
  154. package/dist/lib/components/search/components/search-empty.svelte +28 -0
  155. package/dist/lib/components/search/components/search-empty.svelte.d.ts +4 -0
  156. package/dist/lib/components/search/components/search-input.svelte +53 -0
  157. package/dist/lib/components/search/components/search-input.svelte.d.ts +4 -0
  158. package/dist/lib/components/search/components/search-list.svelte +46 -0
  159. package/dist/lib/components/search/components/search-list.svelte.d.ts +4 -0
  160. package/dist/lib/components/search/components/search-pagnation.svelte +68 -0
  161. package/dist/lib/components/search/components/search-pagnation.svelte.d.ts +8 -0
  162. package/dist/lib/components/search/components/search.svelte +47 -0
  163. package/dist/lib/components/search/components/search.svelte.d.ts +4 -0
  164. package/dist/lib/components/search/exports.d.ts +6 -0
  165. package/dist/lib/components/search/exports.js +5 -0
  166. package/dist/lib/components/search/index.d.ts +2 -0
  167. package/dist/lib/components/search/index.js +2 -0
  168. package/dist/lib/components/search/search.svelte.d.ts +102 -0
  169. package/dist/lib/components/search/search.svelte.js +202 -0
  170. package/dist/lib/components/search/types.d.ts +28 -0
  171. package/dist/lib/components/search/types.js +1 -0
  172. package/dist/lib/components/utilities/arrow/arrow.svelte +23 -0
  173. package/dist/lib/components/utilities/arrow/arrow.svelte.d.ts +3 -0
  174. package/dist/lib/components/utilities/arrow/index.d.ts +2 -0
  175. package/dist/lib/components/utilities/arrow/index.js +1 -0
  176. package/dist/lib/components/utilities/arrow/types.d.ts +17 -0
  177. package/dist/lib/components/utilities/arrow/types.js +1 -0
  178. package/dist/lib/components/utilities/floating-layer/components/floating-layer-anchor.svelte +15 -0
  179. package/dist/lib/components/utilities/floating-layer/components/floating-layer-anchor.svelte.d.ts +4 -0
  180. package/dist/lib/components/utilities/floating-layer/components/floating-layer-arrow.svelte +20 -0
  181. package/dist/lib/components/utilities/floating-layer/components/floating-layer-arrow.svelte.d.ts +3 -0
  182. package/dist/lib/components/utilities/floating-layer/components/floating-layer-content-static.svelte +19 -0
  183. package/dist/lib/components/utilities/floating-layer/components/floating-layer-content-static.svelte.d.ts +13 -0
  184. package/dist/lib/components/utilities/floating-layer/components/floating-layer-content.svelte +61 -0
  185. package/dist/lib/components/utilities/floating-layer/components/floating-layer-content.svelte.d.ts +4 -0
  186. package/dist/lib/components/utilities/floating-layer/components/floating-layer.svelte +10 -0
  187. package/dist/lib/components/utilities/floating-layer/components/floating-layer.svelte.d.ts +7 -0
  188. package/dist/lib/components/utilities/floating-layer/components/index.d.ts +6 -0
  189. package/dist/lib/components/utilities/floating-layer/components/index.js +5 -0
  190. package/dist/lib/components/utilities/floating-layer/index.d.ts +1 -0
  191. package/dist/lib/components/utilities/floating-layer/index.js +1 -0
  192. package/dist/lib/components/utilities/floating-layer/types.d.ts +115 -0
  193. package/dist/lib/components/utilities/floating-layer/types.js +1 -0
  194. package/dist/lib/components/utilities/floating-layer/use-floating-layer.svelte.d.ts +118 -0
  195. package/dist/lib/components/utilities/floating-layer/use-floating-layer.svelte.js +311 -0
  196. package/dist/lib/index.d.ts +1 -0
  197. package/dist/lib/index.js +1 -0
  198. package/dist/lib/utils/asyncDerived.svelte.d.ts +12 -0
  199. package/dist/lib/utils/asyncDerived.svelte.js +26 -0
  200. package/dist/lib/utils/bits.d.ts +39 -0
  201. package/dist/lib/utils/bits.js +69 -0
  202. package/dist/lib/utils/index.d.ts +3 -0
  203. package/dist/lib/utils/index.js +3 -0
  204. package/dist/lib/utils/utils.d.ts +21 -0
  205. package/dist/lib/utils/utils.js +72 -0
  206. package/dist/routes/+layout.svelte +0 -0
  207. package/dist/routes/+layout.svelte.d.ts +26 -0
  208. package/package.json +79 -0
  209. package/src/app.css +209 -0
  210. package/src/app.d.ts +13 -0
  211. package/src/app.html +12 -0
  212. package/src/icons.css +6 -0
  213. package/src/lib/bits/internal/arrays.test.ts +460 -0
  214. package/src/lib/bits/internal/arrays.ts +301 -0
  215. package/src/lib/bits/internal/attrs.ts +97 -0
  216. package/src/lib/bits/internal/box-auto-reset.svelte.ts +40 -0
  217. package/src/lib/bits/internal/box.svelte.ts +60 -0
  218. package/src/lib/bits/internal/clamp.test.ts +37 -0
  219. package/src/lib/bits/internal/clamp.ts +6 -0
  220. package/src/lib/bits/internal/create-event-hook.svelte.ts +64 -0
  221. package/src/lib/bits/internal/create-shared-hook.svelte.ts +33 -0
  222. package/src/lib/bits/internal/date-time/announcer.ts +88 -0
  223. package/src/lib/bits/internal/date-time/calendar-helpers.svelte.ts +815 -0
  224. package/src/lib/bits/internal/date-time/field/helpers.ts +441 -0
  225. package/src/lib/bits/internal/date-time/field/parts.ts +9 -0
  226. package/src/lib/bits/internal/date-time/field/segments.ts +126 -0
  227. package/src/lib/bits/internal/date-time/field/types.ts +35 -0
  228. package/src/lib/bits/internal/date-time/formatter.ts +116 -0
  229. package/src/lib/bits/internal/date-time/placeholders.ts +143 -0
  230. package/src/lib/bits/internal/date-time/utils.ts +261 -0
  231. package/src/lib/bits/internal/debounce.test.ts +67 -0
  232. package/src/lib/bits/internal/debounce.ts +22 -0
  233. package/src/lib/bits/internal/dom.ts +47 -0
  234. package/src/lib/bits/internal/elements.ts +7 -0
  235. package/src/lib/bits/internal/events.ts +89 -0
  236. package/src/lib/bits/internal/floating-svelte/floating-utils.svelte.ts +28 -0
  237. package/src/lib/bits/internal/floating-svelte/types.ts +108 -0
  238. package/src/lib/bits/internal/floating-svelte/use-floating.svelte.ts +133 -0
  239. package/src/lib/bits/internal/focus.ts +111 -0
  240. package/src/lib/bits/internal/get-directional-keys.test.ts +51 -0
  241. package/src/lib/bits/internal/get-directional-keys.ts +43 -0
  242. package/src/lib/bits/internal/is.test.ts +40 -0
  243. package/src/lib/bits/internal/is.ts +78 -0
  244. package/src/lib/bits/internal/kbd-constants.ts +40 -0
  245. package/src/lib/bits/internal/kbd.ts +1 -0
  246. package/src/lib/bits/internal/locale.ts +13 -0
  247. package/src/lib/bits/internal/math.test.ts +88 -0
  248. package/src/lib/bits/internal/math.ts +50 -0
  249. package/src/lib/bits/internal/noop.ts +4 -0
  250. package/src/lib/bits/internal/should-trap-focus.ts +16 -0
  251. package/src/lib/bits/internal/sleep.ts +3 -0
  252. package/src/lib/bits/internal/tabbable.ts +76 -0
  253. package/src/lib/bits/internal/types.ts +91 -0
  254. package/src/lib/bits/internal/use-after-animations.svelte.ts +30 -0
  255. package/src/lib/bits/internal/use-arrow-navigation.ts +168 -0
  256. package/src/lib/bits/internal/use-body-scroll-lock.svelte.ts +138 -0
  257. package/src/lib/bits/internal/use-data-typeahead.svelte.ts +44 -0
  258. package/src/lib/bits/internal/use-dom-typeahead.svelte.ts +44 -0
  259. package/src/lib/bits/internal/use-form-control.svelte.ts +17 -0
  260. package/src/lib/bits/internal/use-grace-area.svelte.ts +229 -0
  261. package/src/lib/bits/internal/use-id.ts +9 -0
  262. package/src/lib/bits/internal/use-resize-observer.svelte.ts +19 -0
  263. package/src/lib/bits/internal/use-roving-focus.svelte.ts +141 -0
  264. package/src/lib/bits/internal/use-size.svelte.ts +60 -0
  265. package/src/lib/bits/internal/use-state-machine.svelte.ts +46 -0
  266. package/src/lib/bits/internal/use-timeout-fn.svelte.ts +80 -0
  267. package/src/lib/components/Typeahead.svelte.ts +200 -0
  268. package/src/lib/components/animated/animated.svelte +244 -0
  269. package/src/lib/components/animated/index.ts +3 -0
  270. package/src/lib/components/combo/combo.svelte +186 -0
  271. package/src/lib/components/combo/index.ts +3 -0
  272. package/src/lib/components/dnd/Droppable.svelte +25 -0
  273. package/src/lib/components/dnd/context.svelte.ts +30 -0
  274. package/src/lib/components/dnd/dnd-context.svelte +45 -0
  275. package/src/lib/components/dnd/dnd-drag-overlay.svelte +44 -0
  276. package/src/lib/components/dnd/dnd-drag-placeholder.svelte +24 -0
  277. package/src/lib/components/dnd/dnd-draghandle.svelte +30 -0
  278. package/src/lib/components/dnd/dnd-overlay.svelte +0 -0
  279. package/src/lib/components/dnd/dnd-sortable-context.svelte +18 -0
  280. package/src/lib/components/dnd/dnd-sortable-item.svelte +68 -0
  281. package/src/lib/components/dnd/example.svelte +109 -0
  282. package/src/lib/components/dnd/exports.ts +12 -0
  283. package/src/lib/components/dnd/index.ts +1 -0
  284. package/src/lib/components/dnd/sortable.svelte.ts +82 -0
  285. package/src/lib/components/dnd/utils.svelte.ts +29 -0
  286. package/src/lib/components/search/combinations/searchPopover.svelte +68 -0
  287. package/src/lib/components/search/components/search-empty.svelte +28 -0
  288. package/src/lib/components/search/components/search-input.svelte +53 -0
  289. package/src/lib/components/search/components/search-list.svelte +46 -0
  290. package/src/lib/components/search/components/search-pagnation.svelte +68 -0
  291. package/src/lib/components/search/components/search.svelte +47 -0
  292. package/src/lib/components/search/exports.ts +13 -0
  293. package/src/lib/components/search/index.ts +2 -0
  294. package/src/lib/components/search/search.svelte.ts +286 -0
  295. package/src/lib/components/search/types.ts +48 -0
  296. package/src/lib/components/utilities/arrow/arrow.svelte +23 -0
  297. package/src/lib/components/utilities/arrow/index.ts +2 -0
  298. package/src/lib/components/utilities/arrow/types.ts +20 -0
  299. package/src/lib/components/utilities/floating-layer/components/floating-layer-anchor.svelte +15 -0
  300. package/src/lib/components/utilities/floating-layer/components/floating-layer-arrow.svelte +20 -0
  301. package/src/lib/components/utilities/floating-layer/components/floating-layer-content-static.svelte +19 -0
  302. package/src/lib/components/utilities/floating-layer/components/floating-layer-content.svelte +61 -0
  303. package/src/lib/components/utilities/floating-layer/components/floating-layer.svelte +10 -0
  304. package/src/lib/components/utilities/floating-layer/components/index.ts +11 -0
  305. package/src/lib/components/utilities/floating-layer/index.ts +1 -0
  306. package/src/lib/components/utilities/floating-layer/types.ts +133 -0
  307. package/src/lib/components/utilities/floating-layer/use-floating-layer.svelte.ts +406 -0
  308. package/src/lib/index.ts +1 -0
  309. package/src/lib/utils/asyncDerived.svelte.ts +38 -0
  310. package/src/lib/utils/bits.ts +93 -0
  311. package/src/lib/utils/index.ts +3 -0
  312. package/src/lib/utils/utils.ts +97 -0
  313. package/src/routes/+layout.svelte +0 -0
@@ -0,0 +1,815 @@
1
+ import {
2
+ type DateValue,
3
+ endOfMonth,
4
+ isSameDay,
5
+ isSameMonth,
6
+ startOfMonth,
7
+ } from "@internationalized/date";
8
+ import { type ReadableBox, type WritableBox, afterTick, styleToString } from "svelte-toolbelt";
9
+ import { untrack } from "svelte";
10
+ import {
11
+ getDaysInMonth,
12
+ getLastFirstDayOfWeek,
13
+ getNextLastDayOfWeek,
14
+ hasTime,
15
+ isAfter,
16
+ isBefore,
17
+ parseAnyDateValue,
18
+ parseStringToDateValue,
19
+ toDate,
20
+ } from "./utils.js";
21
+ import type { Formatter } from "./formatter.js";
22
+ import { getDataDisabled, getDataInvalid, getDataReadonly } from "$lib/internal/attrs.js";
23
+ import { chunk, isValidIndex } from "$lib/internal/arrays.js";
24
+ import { isBrowser, isHTMLElement } from "$lib/internal/is.js";
25
+ import { kbd } from "$lib/internal/kbd.js";
26
+ import type { DateMatcher, Month } from "$lib/shared/index.js";
27
+ import { watch } from "runed";
28
+
29
+ /**
30
+ * Checks if a given node is a calendar cell element.
31
+ *
32
+ * @param node - The node to check.
33
+ */
34
+ export function isCalendarDayNode(node: unknown): node is HTMLElement {
35
+ if (!isHTMLElement(node)) return false;
36
+ if (!node.hasAttribute("data-bits-day")) return false;
37
+ return true;
38
+ }
39
+
40
+ /**
41
+ * Retrieves an array of date values representing the days between
42
+ * the provided start and end dates.
43
+ */
44
+ export function getDaysBetween(start: DateValue, end: DateValue) {
45
+ const days: DateValue[] = [];
46
+ let dCurrent = start.add({ days: 1 });
47
+ const dEnd = end;
48
+ while (dCurrent.compare(dEnd) < 0) {
49
+ days.push(dCurrent);
50
+ dCurrent = dCurrent.add({ days: 1 });
51
+ }
52
+ return days;
53
+ }
54
+
55
+ export type CreateMonthProps = {
56
+ /**
57
+ * The date object representing the month's date (usually the first day of the month).
58
+ */
59
+ dateObj: DateValue;
60
+
61
+ /**
62
+ * The day of the week to start the calendar on (0 for Sunday, 1 for Monday, etc.).
63
+ */
64
+ weekStartsOn: number;
65
+
66
+ /**
67
+ * Whether to always render 6 weeks in the calendar, even if the month doesn't
68
+ * span 6 weeks.
69
+ */
70
+ fixedWeeks: boolean;
71
+
72
+ /**
73
+ * The locale to use when creating the calendar month.
74
+ */
75
+ locale: string;
76
+ };
77
+
78
+ /**
79
+ * Creates a calendar month object.
80
+ *
81
+ * @remarks
82
+ * Given a date, this function returns an object containing
83
+ * the necessary values to render a calendar month, including
84
+ * the month's date (the first day of that month), which can be
85
+ * used to render the name of the month, an array of all dates
86
+ * in that month, and an array of weeks. Each week is an array
87
+ * of dates, useful for rendering an accessible calendar grid
88
+ * using a loop and table elements.
89
+ *
90
+ */
91
+ function createMonth(props: CreateMonthProps): Month<DateValue> {
92
+ const { dateObj, weekStartsOn, fixedWeeks, locale } = props;
93
+ const daysInMonth = getDaysInMonth(dateObj);
94
+
95
+ const datesArray = Array.from({ length: daysInMonth }, (_, i) => dateObj.set({ day: i + 1 }));
96
+
97
+ const firstDayOfMonth = startOfMonth(dateObj);
98
+ const lastDayOfMonth = endOfMonth(dateObj);
99
+
100
+ const lastSunday = getLastFirstDayOfWeek(firstDayOfMonth, weekStartsOn, locale);
101
+ const nextSaturday = getNextLastDayOfWeek(lastDayOfMonth, weekStartsOn, locale);
102
+
103
+ const lastMonthDays = getDaysBetween(lastSunday.subtract({ days: 1 }), firstDayOfMonth);
104
+ const nextMonthDays = getDaysBetween(lastDayOfMonth, nextSaturday.add({ days: 1 }));
105
+
106
+ const totalDays = lastMonthDays.length + datesArray.length + nextMonthDays.length;
107
+
108
+ if (fixedWeeks && totalDays < 42) {
109
+ const extraDays = 42 - totalDays;
110
+
111
+ let startFrom = nextMonthDays[nextMonthDays.length - 1];
112
+
113
+ if (!startFrom) {
114
+ startFrom = dateObj.add({ months: 1 }).set({ day: 1 });
115
+ }
116
+
117
+ let length = extraDays;
118
+ if (nextMonthDays.length === 0) {
119
+ length = extraDays - 1;
120
+ nextMonthDays.push(startFrom);
121
+ }
122
+
123
+ const extraDaysArray = Array.from({ length }, (_, i) => {
124
+ const incr = i + 1;
125
+ return startFrom.add({ days: incr });
126
+ });
127
+ nextMonthDays.push(...extraDaysArray);
128
+ }
129
+
130
+ const allDays = lastMonthDays.concat(datesArray, nextMonthDays);
131
+
132
+ const weeks = chunk(allDays, 7);
133
+
134
+ return {
135
+ value: dateObj,
136
+ dates: allDays,
137
+ weeks,
138
+ };
139
+ }
140
+
141
+ type SetMonthProps = CreateMonthProps & {
142
+ numberOfMonths: number | undefined;
143
+ currentMonths?: Month<DateValue>[];
144
+ };
145
+
146
+ export function createMonths(props: SetMonthProps) {
147
+ const { numberOfMonths, dateObj, ...monthProps } = props;
148
+
149
+ const months: Month<DateValue>[] = [];
150
+
151
+ if (!numberOfMonths || numberOfMonths === 1) {
152
+ months.push(
153
+ createMonth({
154
+ ...monthProps,
155
+ dateObj,
156
+ })
157
+ );
158
+ return months;
159
+ }
160
+
161
+ months.push(
162
+ createMonth({
163
+ ...monthProps,
164
+ dateObj,
165
+ })
166
+ );
167
+
168
+ // Create all the months, starting with the current month
169
+ for (let i = 1; i < numberOfMonths; i++) {
170
+ const nextMonth = dateObj.add({ months: i });
171
+ months.push(
172
+ createMonth({
173
+ ...monthProps,
174
+ dateObj: nextMonth,
175
+ })
176
+ );
177
+ }
178
+
179
+ return months;
180
+ }
181
+
182
+ export function getSelectableCells(calendarNode: HTMLElement | null) {
183
+ if (!calendarNode) return [];
184
+ const selectableSelector = `[data-bits-day]:not([data-disabled]):not([data-outside-visible-months])`;
185
+
186
+ return Array.from(calendarNode.querySelectorAll(selectableSelector)).filter(
187
+ (el): el is HTMLElement => isHTMLElement(el)
188
+ );
189
+ }
190
+
191
+ /**
192
+ * A helper function to extract the date from the `data-value`
193
+ * attribute of a date cell and set it as the placeholder value.
194
+ *
195
+ * Shared between the calendar and range calendar builders.
196
+ *
197
+ * @param node - The node to extract the date from.
198
+ * @param placeholder - The placeholder value store which will be set to the extracted date.
199
+ */
200
+ export function setPlaceholderToNodeValue(node: HTMLElement, placeholder: WritableBox<DateValue>) {
201
+ const cellValue = node.getAttribute("data-value");
202
+ if (!cellValue) return;
203
+ placeholder.current = parseStringToDateValue(cellValue, placeholder.current);
204
+ }
205
+
206
+ type ShiftCalendarFocusProps = {
207
+ /**
208
+ * The day node with current focus.
209
+ */
210
+ node: HTMLElement;
211
+
212
+ /**
213
+ * The number of days to shift the focus by.
214
+ */
215
+ add: number;
216
+
217
+ /**
218
+ * The `placeholder` value box
219
+ */
220
+ placeholder: WritableBox<DateValue>;
221
+
222
+ /**
223
+ * The calendar node.
224
+ */
225
+ calendarNode: HTMLElement | null;
226
+
227
+ /**
228
+ * Whether the previous button is disabled.
229
+ */
230
+ isPrevButtonDisabled: boolean;
231
+
232
+ /**
233
+ * Whether the next button is disabled.
234
+ */
235
+ isNextButtonDisabled: boolean;
236
+
237
+ /**
238
+ * The months array of the calendar.
239
+ */
240
+ months: Month<DateValue>[];
241
+
242
+ /**
243
+ * The number of months being displayed in the calendar.
244
+ */
245
+ numberOfMonths: number;
246
+ };
247
+
248
+ /**
249
+ * Shared logic for shifting focus between cells in the
250
+ * calendar and range calendar.
251
+ */
252
+ export function shiftCalendarFocus({
253
+ node,
254
+ add,
255
+ placeholder,
256
+ calendarNode,
257
+ isPrevButtonDisabled,
258
+ isNextButtonDisabled,
259
+ months,
260
+ numberOfMonths,
261
+ }: ShiftCalendarFocusProps) {
262
+ const candidateCells = getSelectableCells(calendarNode);
263
+ if (!candidateCells.length) return;
264
+
265
+ const index = candidateCells.indexOf(node);
266
+ const nextIndex = index + add;
267
+
268
+ /**
269
+ * If the next cell is within the bounds of the displayed cells,
270
+ * easy day, we just focus it.
271
+ */
272
+ if (isValidIndex(nextIndex, candidateCells)) {
273
+ const nextCell = candidateCells[nextIndex]!;
274
+ setPlaceholderToNodeValue(nextCell, placeholder);
275
+ return nextCell.focus();
276
+ }
277
+
278
+ /**
279
+ * When the next cell falls outside the displayed cells range,
280
+ * we update the focus to the previous or next month based on the
281
+ * direction, and then focus on the relevant cell.
282
+ */
283
+
284
+ if (nextIndex < 0) {
285
+ /**
286
+ * To handle negative indices, we rewind by one month,
287
+ * retrieve candidate cells for that month, and shift focus
288
+ * by the difference between the nextIndex starting from the end
289
+ * of the array.
290
+ */
291
+
292
+ // shift the calendar back a month unless prev month is disabled
293
+ if (isPrevButtonDisabled) return;
294
+
295
+ const firstMonth = months[0]?.value;
296
+ if (!firstMonth) return;
297
+ placeholder.current = firstMonth.subtract({ months: numberOfMonths });
298
+
299
+ // Without a tick here, it seems to be too quick for the DOM to update
300
+
301
+ afterTick(() => {
302
+ const newCandidateCells = getSelectableCells(calendarNode);
303
+ if (!newCandidateCells.length) return;
304
+
305
+ /**
306
+ * Starting at the end of the array, shift focus by the diff
307
+ * between the nextIndex and the length of the array, since the
308
+ * nextIndex is negative.
309
+ */
310
+ const newIndex = newCandidateCells.length - Math.abs(nextIndex);
311
+ if (isValidIndex(newIndex, newCandidateCells)) {
312
+ const newCell = newCandidateCells[newIndex]!;
313
+ setPlaceholderToNodeValue(newCell, placeholder);
314
+ return newCell.focus();
315
+ }
316
+ });
317
+ }
318
+
319
+ if (nextIndex >= candidateCells.length) {
320
+ /**
321
+ * Since we're in the positive index range, we need to go forward
322
+ * a month, refetch the candidate cells within that month, and then
323
+ * starting at the beginning of the array, shift focus by the nextIndex
324
+ * amount.
325
+ */
326
+
327
+ // shift the calendar forward a month unless next month is disabled
328
+ if (isNextButtonDisabled) return;
329
+
330
+ const firstMonth = months[0]?.value;
331
+ if (!firstMonth) return;
332
+ placeholder.current = firstMonth.add({ months: numberOfMonths });
333
+
334
+ afterTick(() => {
335
+ const newCandidateCells = getSelectableCells(calendarNode);
336
+ if (!newCandidateCells.length) return;
337
+
338
+ /**
339
+ * We need to determine how far into the next month we need to go
340
+ * to get the next index. So if we only went over the previous month
341
+ * by one, we need to go into the next month by 1 to get the right index.
342
+ */
343
+ const newIndex = nextIndex - candidateCells.length;
344
+
345
+ if (isValidIndex(newIndex, newCandidateCells)) {
346
+ const nextCell = newCandidateCells[newIndex]!;
347
+ return nextCell.focus();
348
+ }
349
+ });
350
+ }
351
+ }
352
+
353
+ type HandleCalendarKeydownProps = {
354
+ event: KeyboardEvent;
355
+ handleCellClick: (event: Event, date: DateValue) => void;
356
+ shiftFocus: (node: HTMLElement, add: number) => void;
357
+ placeholderValue: DateValue;
358
+ };
359
+ const ARROW_KEYS = [kbd.ARROW_DOWN, kbd.ARROW_UP, kbd.ARROW_LEFT, kbd.ARROW_RIGHT] as const;
360
+ const SELECT_KEYS = [kbd.ENTER, kbd.SPACE];
361
+
362
+ /**
363
+ * Shared keyboard event handler for the calendar and range calendar.
364
+ */
365
+ export function handleCalendarKeydown({
366
+ event,
367
+ handleCellClick,
368
+ shiftFocus,
369
+ placeholderValue,
370
+ }: HandleCalendarKeydownProps) {
371
+ const currentCell = event.target;
372
+ if (!isCalendarDayNode(currentCell)) return;
373
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
374
+ if (!ARROW_KEYS.includes(event.key as any) && !SELECT_KEYS.includes(event.key)) return;
375
+
376
+ event.preventDefault();
377
+
378
+ const kbdFocusMap: Record<(typeof ARROW_KEYS)[number], number> = {
379
+ [kbd.ARROW_DOWN]: 7,
380
+ [kbd.ARROW_UP]: -7,
381
+ [kbd.ARROW_LEFT]: -1,
382
+ [kbd.ARROW_RIGHT]: 1,
383
+ };
384
+
385
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
386
+ if (ARROW_KEYS.includes(event.key as any)) {
387
+ const add = kbdFocusMap[event.key as (typeof ARROW_KEYS)[number]];
388
+ if (add !== undefined) {
389
+ shiftFocus(currentCell, add);
390
+ }
391
+ }
392
+
393
+ if (SELECT_KEYS.includes(event.key)) {
394
+ const cellValue = currentCell.getAttribute("data-value");
395
+ if (!cellValue) return;
396
+ handleCellClick(event, parseStringToDateValue(cellValue, placeholderValue));
397
+ }
398
+ }
399
+
400
+ type HandleCalendarPageProps = {
401
+ months: Month<DateValue>[];
402
+ setMonths: (months: Month<DateValue>[]) => void;
403
+ numberOfMonths: number;
404
+ pagedNavigation: boolean;
405
+ weekStartsOn: number;
406
+ locale: string;
407
+ fixedWeeks: boolean;
408
+ setPlaceholder: (date: DateValue) => void;
409
+ };
410
+
411
+ export function handleCalendarNextPage({
412
+ months,
413
+ setMonths,
414
+ numberOfMonths,
415
+ pagedNavigation,
416
+ weekStartsOn,
417
+ locale,
418
+ fixedWeeks,
419
+ setPlaceholder,
420
+ }: HandleCalendarPageProps) {
421
+ const firstMonth = months[0]?.value;
422
+ if (!firstMonth) return;
423
+ if (pagedNavigation) {
424
+ setPlaceholder(firstMonth.add({ months: numberOfMonths }));
425
+ } else {
426
+ const newMonths = createMonths({
427
+ dateObj: firstMonth.add({ months: 1 }),
428
+ weekStartsOn,
429
+ locale,
430
+ fixedWeeks,
431
+ numberOfMonths,
432
+ });
433
+ setMonths(newMonths);
434
+
435
+ const firstNewMonth = newMonths[0];
436
+ if (!firstNewMonth) return;
437
+ setPlaceholder(firstNewMonth.value.set({ day: 1 }));
438
+ }
439
+ }
440
+
441
+ export function handleCalendarPrevPage({
442
+ months,
443
+ setMonths,
444
+ numberOfMonths,
445
+ pagedNavigation,
446
+ weekStartsOn,
447
+ locale,
448
+ fixedWeeks,
449
+ setPlaceholder,
450
+ }: HandleCalendarPageProps) {
451
+ const firstMonth = months[0]?.value;
452
+ if (!firstMonth) return;
453
+ if (pagedNavigation) {
454
+ setPlaceholder(firstMonth.subtract({ months: numberOfMonths }));
455
+ } else {
456
+ const newMonths = createMonths({
457
+ dateObj: firstMonth.subtract({ months: 1 }),
458
+ weekStartsOn,
459
+ locale,
460
+ fixedWeeks,
461
+ numberOfMonths,
462
+ });
463
+
464
+ setMonths(newMonths);
465
+ const firstNewMonth = newMonths[0];
466
+ if (!firstNewMonth) return;
467
+ setPlaceholder(firstNewMonth.value.set({ day: 1 }));
468
+ }
469
+ }
470
+
471
+ type GetWeekdaysProps = {
472
+ months: Month<DateValue>[];
473
+ weekdayFormat: Intl.DateTimeFormatOptions["weekday"];
474
+ formatter: Formatter;
475
+ };
476
+
477
+ export function getWeekdays({ months, formatter, weekdayFormat }: GetWeekdaysProps) {
478
+ if (!months.length) return [];
479
+ const firstMonth = months[0]!;
480
+ const firstWeek = firstMonth.weeks[0];
481
+ if (!firstWeek) return [];
482
+ return firstWeek.map((date) => formatter.dayOfWeek(toDate(date), weekdayFormat));
483
+ }
484
+
485
+ type UseMonthViewSyncProps = {
486
+ weekStartsOn: ReadableBox<number>;
487
+ locale: ReadableBox<string>;
488
+ fixedWeeks: ReadableBox<boolean>;
489
+ numberOfMonths: ReadableBox<number>;
490
+ placeholder: WritableBox<DateValue>;
491
+ setMonths: (months: Month<DateValue>[]) => void;
492
+ };
493
+
494
+ /**
495
+ * Updates the displayed months based on changes in the options values,
496
+ * which determines the month to show in the calendar.
497
+ */
498
+ export function useMonthViewOptionsSync(props: UseMonthViewSyncProps) {
499
+ const weekStartsOn = props.weekStartsOn.current;
500
+ const locale = props.locale.current;
501
+ const fixedWeeks = props.fixedWeeks.current;
502
+ const numberOfMonths = props.numberOfMonths.current;
503
+
504
+ untrack(() => {
505
+ const placeholder = props.placeholder.current;
506
+ if (!placeholder) return;
507
+ const defaultMonthProps = {
508
+ weekStartsOn,
509
+ locale,
510
+ fixedWeeks,
511
+ numberOfMonths,
512
+ };
513
+
514
+ props.setMonths(createMonths({ ...defaultMonthProps, dateObj: placeholder }));
515
+ });
516
+ }
517
+
518
+ type CreateAccessibleHeadingProps = {
519
+ calendarNode: HTMLElement;
520
+ label: string;
521
+ accessibleHeadingId: string;
522
+ };
523
+
524
+ /**
525
+ * Creates an accessible heading element for the calendar.
526
+ * Returns a function that removes the heading element.
527
+ */
528
+ export function createAccessibleHeading({
529
+ calendarNode,
530
+ label,
531
+ accessibleHeadingId,
532
+ }: CreateAccessibleHeadingProps) {
533
+ const div = document.createElement("div");
534
+ div.style.cssText = styleToString({
535
+ border: "0px",
536
+ clip: "rect(0px, 0px, 0px, 0px)",
537
+ clipPath: "inset(50%)",
538
+ height: "1px",
539
+ margin: "-1px",
540
+ overflow: "hidden",
541
+ padding: "0px",
542
+ position: "absolute",
543
+ whiteSpace: "nowrap",
544
+ width: "1px",
545
+ });
546
+ const h2 = document.createElement("div");
547
+ h2.textContent = label;
548
+ h2.id = accessibleHeadingId;
549
+ h2.role = "heading";
550
+ h2.ariaLevel = "2";
551
+ calendarNode.insertBefore(div, calendarNode.firstChild);
552
+ div.appendChild(h2);
553
+
554
+ return () => {
555
+ const h2 = document.getElementById(accessibleHeadingId);
556
+ if (!h2) return;
557
+ div.parentElement?.removeChild(div);
558
+ h2.remove();
559
+ };
560
+ }
561
+
562
+ type UseMonthViewPlaceholderSyncProps = {
563
+ placeholder: WritableBox<DateValue>;
564
+ getVisibleMonths: () => DateValue[];
565
+ weekStartsOn: ReadableBox<number>;
566
+ locale: ReadableBox<string>;
567
+ fixedWeeks: ReadableBox<boolean>;
568
+ numberOfMonths: ReadableBox<number>;
569
+ setMonths: (months: Month<DateValue>[]) => void;
570
+ };
571
+
572
+ export function useMonthViewPlaceholderSync({
573
+ placeholder,
574
+ getVisibleMonths,
575
+ weekStartsOn,
576
+ locale,
577
+ fixedWeeks,
578
+ numberOfMonths,
579
+ setMonths,
580
+ }: UseMonthViewPlaceholderSyncProps) {
581
+ $effect(() => {
582
+ placeholder.current;
583
+ untrack(() => {
584
+ /**
585
+ * If the placeholder's month is already in this visible months,
586
+ * we don't need to do anything.
587
+ */
588
+ if (getVisibleMonths().some((month) => isSameMonth(month, placeholder.current))) {
589
+ return;
590
+ }
591
+
592
+ const defaultMonthProps = {
593
+ weekStartsOn: weekStartsOn.current,
594
+ locale: locale.current,
595
+ fixedWeeks: fixedWeeks.current,
596
+ numberOfMonths: numberOfMonths.current,
597
+ };
598
+
599
+ setMonths(createMonths({ ...defaultMonthProps, dateObj: placeholder.current }));
600
+ });
601
+ });
602
+ }
603
+
604
+ type GetIsNextButtonDisabledProps = {
605
+ maxValue: DateValue | undefined;
606
+ months: Month<DateValue>[];
607
+ disabled: boolean;
608
+ };
609
+
610
+ export function getIsNextButtonDisabled({
611
+ maxValue,
612
+ months,
613
+ disabled,
614
+ }: GetIsNextButtonDisabledProps) {
615
+ if (!maxValue || !months.length) return false;
616
+ if (disabled) return true;
617
+ const lastMonthInView = months[months.length - 1]?.value;
618
+ if (!lastMonthInView) return false;
619
+ const firstMonthOfNextPage = lastMonthInView
620
+ .add({
621
+ months: 1,
622
+ })
623
+ .set({ day: 1 });
624
+ return isAfter(firstMonthOfNextPage, maxValue);
625
+ }
626
+
627
+ type GetIsPrevButtonDisabledProps = {
628
+ minValue: DateValue | undefined;
629
+ months: Month<DateValue>[];
630
+ disabled: boolean;
631
+ };
632
+
633
+ export function getIsPrevButtonDisabled({
634
+ minValue,
635
+ months,
636
+ disabled,
637
+ }: GetIsPrevButtonDisabledProps) {
638
+ if (!minValue || !months.length) return false;
639
+ if (disabled) return true;
640
+ const firstMonthInView = months[0]?.value;
641
+ if (!firstMonthInView) return false;
642
+ const lastMonthOfPrevPage = firstMonthInView
643
+ .subtract({
644
+ months: 1,
645
+ })
646
+ .set({ day: 35 });
647
+ return isBefore(lastMonthOfPrevPage, minValue);
648
+ }
649
+
650
+ type GetCalendarHeadingValueProps = {
651
+ months: Month<DateValue>[];
652
+ formatter: Formatter;
653
+ locale: string;
654
+ };
655
+
656
+ export function getCalendarHeadingValue({
657
+ months,
658
+ locale,
659
+ formatter,
660
+ }: GetCalendarHeadingValueProps) {
661
+ if (!months.length) return "";
662
+ if (locale !== formatter.getLocale()) {
663
+ formatter.setLocale(locale);
664
+ }
665
+ if (months.length === 1) {
666
+ const month = toDate(months[0]!.value);
667
+ return `${formatter.fullMonthAndYear(month)}`;
668
+ }
669
+
670
+ const startMonth = toDate(months[0]!.value);
671
+ const endMonth = toDate(months[months.length - 1]!.value);
672
+
673
+ const startMonthName = formatter.fullMonth(startMonth);
674
+ const endMonthName = formatter.fullMonth(endMonth);
675
+
676
+ const startMonthYear = formatter.fullYear(startMonth);
677
+ const endMonthYear = formatter.fullYear(endMonth);
678
+
679
+ const content =
680
+ startMonthYear === endMonthYear
681
+ ? `${startMonthName} - ${endMonthName} ${endMonthYear}`
682
+ : `${startMonthName} ${startMonthYear} - ${endMonthName} ${endMonthYear}`;
683
+
684
+ return content;
685
+ }
686
+
687
+ type GetCalendarElementProps = {
688
+ fullCalendarLabel: string;
689
+ id: string;
690
+ isInvalid: boolean;
691
+ disabled: boolean;
692
+ readonly: boolean;
693
+ };
694
+
695
+ export function getCalendarElementProps({
696
+ fullCalendarLabel,
697
+ id,
698
+ isInvalid,
699
+ disabled,
700
+ readonly,
701
+ }: GetCalendarElementProps) {
702
+ return {
703
+ id,
704
+ role: "application",
705
+ "aria-label": fullCalendarLabel,
706
+ "data-invalid": getDataInvalid(isInvalid),
707
+ "data-disabled": getDataDisabled(disabled),
708
+ "data-readonly": getDataReadonly(readonly),
709
+ } as const;
710
+ }
711
+
712
+ export type CalendarParts =
713
+ | "root"
714
+ | "grid"
715
+ | "cell"
716
+ | "next-button"
717
+ | "prev-button"
718
+ | "day"
719
+ | "grid-body"
720
+ | "grid-head"
721
+ | "grid-row"
722
+ | "head-cell"
723
+ | "header"
724
+ | "heading";
725
+
726
+ export function pickerOpenFocus(e: Event) {
727
+ const nodeToFocus = document.querySelector<HTMLElement>("[data-bits-day][data-focused]");
728
+ if (nodeToFocus) {
729
+ e.preventDefault();
730
+ nodeToFocus?.focus();
731
+ }
732
+ }
733
+
734
+ export function getFirstNonDisabledDateInView(calendarRef: HTMLElement): DateValue | undefined {
735
+ if (!isBrowser) return;
736
+ const daysInView = Array.from(
737
+ calendarRef.querySelectorAll<HTMLElement>("[data-bits-day]:not([aria-disabled=true])")
738
+ );
739
+ if (daysInView.length === 0) return;
740
+ const element = daysInView[0];
741
+ const value = element?.getAttribute("data-value");
742
+ const type = element?.getAttribute("data-type");
743
+ if (!value || !type) return;
744
+ return parseAnyDateValue(value, type);
745
+ }
746
+
747
+ /**
748
+ * Ensures the placeholder is not set to a disabled date,
749
+ * which would prevent the user from entering the Calendar
750
+ * via the keyboard.
751
+ */
752
+ export function useEnsureNonDisabledPlaceholder({
753
+ ref,
754
+ placeholder,
755
+ defaultPlaceholder,
756
+ minValue,
757
+ maxValue,
758
+ isDateDisabled,
759
+ }: {
760
+ ref: WritableBox<HTMLElement | null>;
761
+ placeholder: WritableBox<DateValue | undefined>;
762
+ isDateDisabled: ReadableBox<DateMatcher>;
763
+ minValue: ReadableBox<DateValue | undefined>;
764
+ maxValue: ReadableBox<DateValue | undefined>;
765
+ defaultPlaceholder: DateValue;
766
+ }) {
767
+ function isDisabled(date: DateValue) {
768
+ if (isDateDisabled.current(date)) return true;
769
+ if (minValue.current && isBefore(date, minValue.current)) return true;
770
+ if (maxValue.current && isBefore(maxValue.current, date)) return true;
771
+ return false;
772
+ }
773
+
774
+ watch(
775
+ () => ref.current,
776
+ () => {
777
+ if (!ref.current) return;
778
+ /**
779
+ * If the placeholder is still the default placeholder and it's a disabled date, find
780
+ * the first available date in the calendar view and set it as the placeholder.
781
+ *
782
+ * This prevents the placeholder from being a disabled date and no date being tabbable
783
+ * preventing the user from entering the Calendar. If all dates in the view are
784
+ * disabled, currently that is considered an error on the developer's part and should
785
+ * be handled by them.
786
+ *
787
+ * Perhaps in the future we can introduce a dev-only log message to prevent this from
788
+ * being a silent error.
789
+ */
790
+ if (
791
+ placeholder.current &&
792
+ isSameDay(placeholder.current, defaultPlaceholder) &&
793
+ isDisabled(defaultPlaceholder)
794
+ ) {
795
+ placeholder.current =
796
+ getFirstNonDisabledDateInView(ref.current) ?? defaultPlaceholder;
797
+ }
798
+ }
799
+ );
800
+ }
801
+
802
+ export function getDateWithPreviousTime(date: DateValue | undefined, prev: DateValue | undefined) {
803
+ if (!date || !prev) return date;
804
+
805
+ if (hasTime(date) && hasTime(prev)) {
806
+ return date.set({
807
+ hour: prev.hour,
808
+ minute: prev.minute,
809
+ millisecond: prev.millisecond,
810
+ second: prev.second,
811
+ });
812
+ }
813
+
814
+ return date;
815
+ }