@yewdiel21/motion-kit 0.9.1 → 0.14.0

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 (458) hide show
  1. package/README.md +72 -0
  2. package/dist/auto/index.d.ts +72 -3
  3. package/dist/auto/index.js +139 -3
  4. package/dist/auto/index.js.map +1 -1
  5. package/dist/{chunk-BFC27F2G.js → chunk-6P4P7NU6.js} +2 -218
  6. package/dist/chunk-6P4P7NU6.js.map +1 -0
  7. package/dist/chunk-CU6SXW3E.js +20334 -0
  8. package/dist/chunk-CU6SXW3E.js.map +1 -0
  9. package/dist/{chunk-XDWBVFHQ.js → chunk-EY4BJKC4.js} +1203 -29
  10. package/dist/chunk-EY4BJKC4.js.map +1 -0
  11. package/dist/chunk-QP4QJOEN.js +72 -0
  12. package/dist/chunk-QP4QJOEN.js.map +1 -0
  13. package/dist/css/components/activity-feed.css +240 -0
  14. package/dist/css/components/attachment.css +266 -0
  15. package/dist/css/components/breadcrumb.css +110 -0
  16. package/dist/css/components/calendar.css +159 -0
  17. package/dist/css/components/chart-tooltip.css +168 -0
  18. package/dist/css/components/chart.css +296 -0
  19. package/dist/css/components/code-block.css +264 -0
  20. package/dist/css/components/color-area.css +82 -0
  21. package/dist/css/components/color-field.css +67 -0
  22. package/dist/css/components/color-picker.css +131 -0
  23. package/dist/css/components/color-slider.css +113 -0
  24. package/dist/css/components/command-search.css +252 -0
  25. package/dist/css/components/context-menu.css +23 -0
  26. package/dist/css/components/credit-card.css +288 -0
  27. package/dist/css/components/data-table.css +591 -0
  28. package/dist/css/components/date-range-picker.css +168 -0
  29. package/dist/css/components/date-time-picker.css +178 -0
  30. package/dist/css/components/drawer.css +58 -0
  31. package/dist/css/components/editable-table.css +201 -0
  32. package/dist/css/components/empty-state.css +61 -0
  33. package/dist/css/components/file-explorer.css +633 -0
  34. package/dist/css/components/filter-builder.css +232 -0
  35. package/dist/css/components/hold-confirm.css +87 -0
  36. package/dist/css/components/hover-card.css +20 -0
  37. package/dist/css/components/image-preview.css +195 -0
  38. package/dist/css/components/input-otp.css +10 -0
  39. package/dist/css/components/json-viewer.css +272 -0
  40. package/dist/css/components/like-button.css +78 -0
  41. package/dist/css/components/link-preview.css +275 -0
  42. package/dist/css/components/loading-button.css +77 -0
  43. package/dist/css/components/multi-select.css +267 -0
  44. package/dist/css/components/notification-bell.css +127 -0
  45. package/dist/css/components/pagination.css +11 -0
  46. package/dist/css/components/password-strength.css +183 -0
  47. package/dist/css/components/popover.css +25 -0
  48. package/dist/css/components/progress.css +140 -0
  49. package/dist/css/components/quantity.css +16 -0
  50. package/dist/css/components/questionnaire.css +217 -0
  51. package/dist/css/components/range-calendar.css +186 -0
  52. package/dist/css/components/range-slider.css +192 -0
  53. package/dist/css/components/resizable-panel.css +243 -0
  54. package/dist/css/components/rich-text-editor.css +284 -0
  55. package/dist/css/components/scratch-card.css +75 -0
  56. package/dist/css/components/skeleton.css +108 -0
  57. package/dist/css/components/slot-picker.css +126 -0
  58. package/dist/css/components/split-view.css +19 -0
  59. package/dist/css/components/star-rating.css +80 -0
  60. package/dist/css/components/stat-card.css +164 -0
  61. package/dist/css/components/studio-dock.css +35 -0
  62. package/dist/css/components/swipe-actions.css +105 -0
  63. package/dist/css/components/tag-input.css +244 -0
  64. package/dist/css/components/time-field.css +88 -0
  65. package/dist/css/components/timeline.css +219 -0
  66. package/dist/css/components/tooltip.css +79 -0
  67. package/dist/css/components/trailer.css +37 -0
  68. package/dist/css/components/tree-view.css +210 -0
  69. package/dist/css/components/virtual-list.css +195 -0
  70. package/dist/css/controls.css +65 -0
  71. package/dist/css/tokens.css +185 -181
  72. package/dist/elements/index.d.ts +742 -5
  73. package/dist/elements/index.js +1527 -8
  74. package/dist/elements/index.js.map +1 -1
  75. package/dist/{engine-GDGwQnJE.d.ts → engine-Cn6IhE3z.d.ts} +1 -1
  76. package/dist/engine-QECQnGz1.d.ts +33 -0
  77. package/dist/index.css +10113 -181
  78. package/dist/index.d.ts +89 -5
  79. package/dist/index.js +285 -9
  80. package/dist/index.js.map +1 -1
  81. package/dist/react/index.d.ts +678 -23
  82. package/dist/react/index.js +2389 -7
  83. package/dist/react/index.js.map +1 -1
  84. package/dist/{reduced-B98f8LPs.d.ts → reduced-BBUusld6.d.ts} +206 -2
  85. package/dist/reveal-DEvp6U6T.d.ts +2381 -0
  86. package/dist/{store-pHiUvxP8.d.ts → store-DjkTBX7f.d.ts} +2 -2
  87. package/package.json +1 -1
  88. package/registry.json +583 -104
  89. package/skills/motion-kit/SKILL.md +209 -189
  90. package/src/auto/auto-init.ts +136 -0
  91. package/src/components/activity-feed/README.md +56 -0
  92. package/src/components/activity-feed/activity-feed.css +239 -0
  93. package/src/components/activity-feed/element.ts +36 -0
  94. package/src/components/activity-feed/engine.ts +351 -0
  95. package/src/components/activity-feed/react.tsx +32 -0
  96. package/src/components/attachment/README.md +27 -0
  97. package/src/components/attachment/attachment.css +265 -0
  98. package/src/components/attachment/element.ts +50 -0
  99. package/src/components/attachment/engine.ts +469 -0
  100. package/src/components/attachment/react.tsx +65 -0
  101. package/src/components/breadcrumb/README.md +38 -0
  102. package/src/components/breadcrumb/breadcrumb.css +109 -0
  103. package/src/components/breadcrumb/element.ts +27 -0
  104. package/src/components/breadcrumb/engine.ts +249 -0
  105. package/src/components/breadcrumb/react.tsx +30 -0
  106. package/src/components/calendar/README.md +82 -0
  107. package/src/components/calendar/calendar.css +158 -0
  108. package/src/components/calendar/element.ts +27 -0
  109. package/src/components/calendar/engine.ts +412 -0
  110. package/src/components/calendar/react.tsx +69 -0
  111. package/src/components/chart/README.md +24 -0
  112. package/src/components/chart/chart.css +295 -0
  113. package/src/components/chart/element.ts +41 -0
  114. package/src/components/chart/engine.ts +811 -0
  115. package/src/components/chart/react.tsx +50 -0
  116. package/src/components/chart-tooltip/README.md +53 -0
  117. package/src/components/chart-tooltip/chart-tooltip.css +167 -0
  118. package/src/components/chart-tooltip/element.ts +37 -0
  119. package/src/components/chart-tooltip/engine.ts +442 -0
  120. package/src/components/chart-tooltip/react.tsx +47 -0
  121. package/src/components/code-block/README.md +19 -0
  122. package/src/components/code-block/code-block.css +263 -0
  123. package/src/components/code-block/element.ts +34 -0
  124. package/src/components/code-block/engine.ts +254 -0
  125. package/src/components/code-block/highlight.ts +244 -0
  126. package/src/components/code-block/react.tsx +94 -0
  127. package/src/components/color-area/README.md +50 -0
  128. package/src/components/color-area/color-area.css +81 -0
  129. package/src/components/color-area/element.ts +26 -0
  130. package/src/components/color-area/engine.ts +246 -0
  131. package/src/components/color-area/react.tsx +42 -0
  132. package/src/components/color-field/README.md +43 -0
  133. package/src/components/color-field/color-field.css +66 -0
  134. package/src/components/color-field/element.ts +26 -0
  135. package/src/components/color-field/engine.ts +174 -0
  136. package/src/components/color-field/react.tsx +42 -0
  137. package/src/components/color-picker/README.md +45 -0
  138. package/src/components/color-picker/color-picker.css +130 -0
  139. package/src/components/color-picker/element.ts +45 -0
  140. package/src/components/color-picker/engine.ts +314 -0
  141. package/src/components/color-picker/react.tsx +47 -0
  142. package/src/components/color-slider/README.md +55 -0
  143. package/src/components/color-slider/color-slider.css +112 -0
  144. package/src/components/color-slider/element.ts +26 -0
  145. package/src/components/color-slider/engine.ts +234 -0
  146. package/src/components/color-slider/react.tsx +45 -0
  147. package/src/components/command-search/README.md +15 -0
  148. package/src/components/command-search/command-search.css +251 -0
  149. package/src/components/command-search/element.ts +36 -0
  150. package/src/components/command-search/engine.ts +356 -0
  151. package/src/components/command-search/match.ts +53 -0
  152. package/src/components/command-search/react.tsx +66 -0
  153. package/src/components/context-menu/README.md +31 -0
  154. package/src/components/context-menu/context-menu.css +22 -0
  155. package/src/components/context-menu/element.ts +40 -0
  156. package/src/components/context-menu/engine.ts +153 -0
  157. package/src/components/context-menu/react.tsx +57 -0
  158. package/src/components/credit-card/README.md +20 -0
  159. package/src/components/credit-card/card.ts +77 -0
  160. package/src/components/credit-card/credit-card.css +287 -0
  161. package/src/components/credit-card/element.ts +33 -0
  162. package/src/components/credit-card/engine.ts +410 -0
  163. package/src/components/credit-card/react.tsx +36 -0
  164. package/src/components/data-table/README.md +83 -0
  165. package/src/components/data-table/data-table.css +590 -0
  166. package/src/components/data-table/element.ts +54 -0
  167. package/src/components/data-table/engine.ts +1059 -0
  168. package/src/components/data-table/react.tsx +94 -0
  169. package/src/components/date-range-picker/README.md +51 -0
  170. package/src/components/date-range-picker/date-range-picker.css +167 -0
  171. package/src/components/date-range-picker/element.ts +52 -0
  172. package/src/components/date-range-picker/engine.ts +352 -0
  173. package/src/components/date-range-picker/react.tsx +57 -0
  174. package/src/components/date-time-picker/README.md +48 -0
  175. package/src/components/date-time-picker/date-time-picker.css +177 -0
  176. package/src/components/date-time-picker/element.ts +47 -0
  177. package/src/components/date-time-picker/engine.ts +350 -0
  178. package/src/components/date-time-picker/react.tsx +54 -0
  179. package/src/components/dialog/README.md +103 -102
  180. package/src/components/dialog/element.ts +123 -118
  181. package/src/components/dialog/engine.ts +449 -432
  182. package/src/components/drawer/README.md +28 -0
  183. package/src/components/drawer/drawer.css +57 -0
  184. package/src/components/drawer/element.ts +21 -0
  185. package/src/components/drawer/engine.ts +56 -0
  186. package/src/components/drawer/react.tsx +85 -0
  187. package/src/components/editable-table/README.md +67 -0
  188. package/src/components/editable-table/editable-table.css +200 -0
  189. package/src/components/editable-table/element.ts +45 -0
  190. package/src/components/editable-table/engine.ts +775 -0
  191. package/src/components/editable-table/react.tsx +79 -0
  192. package/src/components/empty-state/README.md +41 -0
  193. package/src/components/empty-state/element.ts +23 -0
  194. package/src/components/empty-state/empty-state.css +60 -0
  195. package/src/components/empty-state/engine.ts +232 -0
  196. package/src/components/empty-state/react.tsx +34 -0
  197. package/src/components/file-explorer/README.md +67 -0
  198. package/src/components/file-explorer/element.ts +39 -0
  199. package/src/components/file-explorer/engine.ts +1307 -0
  200. package/src/components/file-explorer/file-explorer.css +632 -0
  201. package/src/components/file-explorer/react.tsx +77 -0
  202. package/src/components/filter-builder/README.md +47 -0
  203. package/src/components/filter-builder/element.ts +34 -0
  204. package/src/components/filter-builder/engine.ts +365 -0
  205. package/src/components/filter-builder/filter-builder.css +231 -0
  206. package/src/components/filter-builder/react.tsx +36 -0
  207. package/src/components/hold-confirm/README.md +13 -0
  208. package/src/components/hold-confirm/element.ts +24 -0
  209. package/src/components/hold-confirm/engine.ts +216 -0
  210. package/src/components/hold-confirm/hold-confirm.css +86 -0
  211. package/src/components/hold-confirm/react.tsx +28 -0
  212. package/src/components/hover-card/README.md +20 -0
  213. package/src/components/hover-card/element.ts +8 -0
  214. package/src/components/hover-card/engine.ts +131 -0
  215. package/src/components/hover-card/hover-card.css +19 -0
  216. package/src/components/hover-card/react.tsx +19 -0
  217. package/src/components/image-preview/README.md +16 -0
  218. package/src/components/image-preview/element.ts +26 -0
  219. package/src/components/image-preview/engine.ts +325 -0
  220. package/src/components/image-preview/image-preview.css +194 -0
  221. package/src/components/image-preview/react.tsx +43 -0
  222. package/src/components/input-otp/README.md +18 -0
  223. package/src/components/input-otp/element.ts +14 -0
  224. package/src/components/input-otp/engine.ts +106 -0
  225. package/src/components/input-otp/input-otp.css +9 -0
  226. package/src/components/input-otp/react.tsx +21 -0
  227. package/src/components/json-viewer/README.md +56 -0
  228. package/src/components/json-viewer/element.ts +37 -0
  229. package/src/components/json-viewer/engine.ts +440 -0
  230. package/src/components/json-viewer/json-viewer.css +271 -0
  231. package/src/components/json-viewer/react.tsx +56 -0
  232. package/src/components/like-button/README.md +13 -0
  233. package/src/components/like-button/element.ts +47 -0
  234. package/src/components/like-button/engine.ts +145 -0
  235. package/src/components/like-button/like-button.css +77 -0
  236. package/src/components/like-button/react.tsx +39 -0
  237. package/src/components/link-preview/README.md +19 -0
  238. package/src/components/link-preview/element.ts +29 -0
  239. package/src/components/link-preview/engine.ts +192 -0
  240. package/src/components/link-preview/link-preview.css +274 -0
  241. package/src/components/link-preview/react.tsx +101 -0
  242. package/src/components/loading-button/README.md +45 -0
  243. package/src/components/loading-button/element.ts +34 -0
  244. package/src/components/loading-button/engine.ts +201 -0
  245. package/src/components/loading-button/loading-button.css +76 -0
  246. package/src/components/loading-button/react.tsx +43 -0
  247. package/src/components/multi-select/README.md +48 -0
  248. package/src/components/multi-select/element.ts +28 -0
  249. package/src/components/multi-select/engine.ts +294 -0
  250. package/src/components/multi-select/multi-select.css +266 -0
  251. package/src/components/multi-select/react.tsx +39 -0
  252. package/src/components/notification-bell/README.md +14 -0
  253. package/src/components/notification-bell/element.ts +48 -0
  254. package/src/components/notification-bell/engine.ts +202 -0
  255. package/src/components/notification-bell/notification-bell.css +126 -0
  256. package/src/components/notification-bell/react.tsx +57 -0
  257. package/src/components/pagination/README.md +17 -0
  258. package/src/components/pagination/element.ts +14 -0
  259. package/src/components/pagination/engine.ts +102 -0
  260. package/src/components/pagination/pagination.css +10 -0
  261. package/src/components/pagination/react.tsx +22 -0
  262. package/src/components/password-strength/README.md +17 -0
  263. package/src/components/password-strength/element.ts +22 -0
  264. package/src/components/password-strength/engine.ts +212 -0
  265. package/src/components/password-strength/password-strength.css +182 -0
  266. package/src/components/password-strength/react.tsx +36 -0
  267. package/src/components/password-strength/score.ts +62 -0
  268. package/src/components/popover/README.md +44 -0
  269. package/src/components/popover/element.ts +31 -0
  270. package/src/components/popover/engine.ts +114 -0
  271. package/src/components/popover/popover.css +24 -0
  272. package/src/components/popover/react.tsx +50 -0
  273. package/src/components/progress/README.md +17 -0
  274. package/src/components/progress/element.ts +30 -0
  275. package/src/components/progress/engine.ts +173 -0
  276. package/src/components/progress/progress.css +139 -0
  277. package/src/components/progress/react.tsx +35 -0
  278. package/src/components/quantity/README.md +20 -0
  279. package/src/components/quantity/element.ts +14 -0
  280. package/src/components/quantity/engine.ts +85 -0
  281. package/src/components/quantity/quantity.css +15 -0
  282. package/src/components/quantity/react.tsx +21 -0
  283. package/src/components/questionnaire/README.md +32 -0
  284. package/src/components/questionnaire/element.ts +118 -0
  285. package/src/components/questionnaire/engine.ts +255 -0
  286. package/src/components/questionnaire/questionnaire.css +216 -0
  287. package/src/components/questionnaire/react.tsx +140 -0
  288. package/src/components/range-calendar/README.md +58 -0
  289. package/src/components/range-calendar/element.ts +42 -0
  290. package/src/components/range-calendar/engine.ts +390 -0
  291. package/src/components/range-calendar/range-calendar.css +185 -0
  292. package/src/components/range-calendar/react.tsx +56 -0
  293. package/src/components/range-slider/README.md +23 -0
  294. package/src/components/range-slider/element.ts +53 -0
  295. package/src/components/range-slider/engine.ts +316 -0
  296. package/src/components/range-slider/range-slider.css +191 -0
  297. package/src/components/range-slider/react.tsx +66 -0
  298. package/src/components/resizable-panel/README.md +61 -0
  299. package/src/components/resizable-panel/element.ts +36 -0
  300. package/src/components/resizable-panel/engine.ts +551 -0
  301. package/src/components/resizable-panel/react.tsx +74 -0
  302. package/src/components/resizable-panel/resizable-panel.css +242 -0
  303. package/src/components/rich-text-editor/README.md +57 -0
  304. package/src/components/rich-text-editor/element.ts +34 -0
  305. package/src/components/rich-text-editor/engine.ts +673 -0
  306. package/src/components/rich-text-editor/react.tsx +60 -0
  307. package/src/components/rich-text-editor/rich-text-editor.css +283 -0
  308. package/src/components/scratch-card/README.md +15 -0
  309. package/src/components/scratch-card/element.ts +30 -0
  310. package/src/components/scratch-card/engine.ts +257 -0
  311. package/src/components/scratch-card/react.tsx +40 -0
  312. package/src/components/scratch-card/scratch-card.css +74 -0
  313. package/src/components/skeleton/README.md +15 -0
  314. package/src/components/skeleton/element.ts +32 -0
  315. package/src/components/skeleton/engine.ts +104 -0
  316. package/src/components/skeleton/react.tsx +41 -0
  317. package/src/components/skeleton/skeleton.css +107 -0
  318. package/src/components/slot-picker/README.md +68 -0
  319. package/src/components/slot-picker/element.ts +27 -0
  320. package/src/components/slot-picker/engine.ts +412 -0
  321. package/src/components/slot-picker/react.tsx +76 -0
  322. package/src/components/slot-picker/slot-picker.css +125 -0
  323. package/src/components/split-view/README.md +50 -0
  324. package/src/components/split-view/element.ts +34 -0
  325. package/src/components/split-view/engine.ts +114 -0
  326. package/src/components/split-view/react.tsx +63 -0
  327. package/src/components/split-view/split-view.css +18 -0
  328. package/src/components/star-rating/README.md +14 -0
  329. package/src/components/star-rating/element.ts +26 -0
  330. package/src/components/star-rating/engine.ts +217 -0
  331. package/src/components/star-rating/react.tsx +33 -0
  332. package/src/components/star-rating/star-rating.css +79 -0
  333. package/src/components/stat-card/README.md +42 -0
  334. package/src/components/stat-card/element.ts +40 -0
  335. package/src/components/stat-card/engine.ts +283 -0
  336. package/src/components/stat-card/react.tsx +36 -0
  337. package/src/components/stat-card/stat-card.css +163 -0
  338. package/src/components/studio-dock/README.md +44 -0
  339. package/src/components/studio-dock/element.ts +11 -0
  340. package/src/components/studio-dock/engine.ts +212 -0
  341. package/src/components/studio-dock/react.tsx +23 -0
  342. package/src/components/studio-dock/studio-dock.css +34 -0
  343. package/src/components/swipe-actions/README.md +19 -0
  344. package/src/components/swipe-actions/element.ts +26 -0
  345. package/src/components/swipe-actions/engine.ts +260 -0
  346. package/src/components/swipe-actions/react.tsx +80 -0
  347. package/src/components/swipe-actions/swipe-actions.css +104 -0
  348. package/src/components/tag-input/README.md +42 -0
  349. package/src/components/tag-input/element.ts +39 -0
  350. package/src/components/tag-input/engine.ts +313 -0
  351. package/src/components/tag-input/react.tsx +54 -0
  352. package/src/components/tag-input/tag-input.css +243 -0
  353. package/src/components/time-field/README.md +54 -0
  354. package/src/components/time-field/element.ts +38 -0
  355. package/src/components/time-field/engine.ts +110 -0
  356. package/src/components/time-field/react.tsx +45 -0
  357. package/src/components/time-field/time-field.css +87 -0
  358. package/src/components/timeline/README.md +57 -0
  359. package/src/components/timeline/element.ts +31 -0
  360. package/src/components/timeline/engine.ts +300 -0
  361. package/src/components/timeline/react.tsx +59 -0
  362. package/src/components/timeline/timeline.css +218 -0
  363. package/src/components/tooltip/README.md +46 -0
  364. package/src/components/tooltip/element.ts +22 -0
  365. package/src/components/tooltip/engine.ts +288 -0
  366. package/src/components/tooltip/react.tsx +30 -0
  367. package/src/components/tooltip/tooltip.css +78 -0
  368. package/src/components/trailer/README.md +28 -0
  369. package/src/components/trailer/element.ts +13 -0
  370. package/src/components/trailer/engine.ts +189 -0
  371. package/src/components/trailer/react.tsx +16 -0
  372. package/src/components/trailer/trailer.css +36 -0
  373. package/src/components/tree-view/README.md +61 -0
  374. package/src/components/tree-view/element.ts +32 -0
  375. package/src/components/tree-view/engine.ts +569 -0
  376. package/src/components/tree-view/react.tsx +78 -0
  377. package/src/components/tree-view/tree-view.css +209 -0
  378. package/src/components/virtual-list/README.md +81 -0
  379. package/src/components/virtual-list/element.ts +42 -0
  380. package/src/components/virtual-list/engine.ts +678 -0
  381. package/src/components/virtual-list/react.tsx +101 -0
  382. package/src/components/virtual-list/virtual-list.css +194 -0
  383. package/src/core/chips.ts +196 -0
  384. package/src/core/color.ts +304 -0
  385. package/src/core/date-range.ts +67 -0
  386. package/src/core/dates.ts +108 -0
  387. package/src/core/files.ts +107 -0
  388. package/src/core/filter.ts +229 -0
  389. package/src/core/gsap.ts +80 -65
  390. package/src/core/listbox.ts +332 -0
  391. package/src/core/mark.ts +29 -0
  392. package/src/core/position.ts +89 -0
  393. package/src/core/relative-time.ts +77 -0
  394. package/src/core/segment-field.ts +373 -0
  395. package/src/core/segments.ts +153 -0
  396. package/src/core/tree.ts +116 -0
  397. package/src/core/virtual.ts +79 -0
  398. package/src/css/controls/README.md +14 -0
  399. package/src/css/controls/status.css +63 -0
  400. package/src/css/tokens.css +186 -182
  401. package/src/elements/index.ts +171 -0
  402. package/src/index.ts +97 -0
  403. package/src/motion/micro.ts +248 -0
  404. package/src/motion/sheet.ts +88 -65
  405. package/src/motion/surface.ts +9 -0
  406. package/src/react/index.ts +78 -0
  407. package/src/react/use-action-button.ts +6 -0
  408. package/src/react/use-editorial-reveal.ts +7 -0
  409. package/src/react/use-film-hero.ts +6 -0
  410. package/src/react/use-font-cycle.ts +7 -0
  411. package/src/react/use-image-zoom.ts +5 -0
  412. package/src/react/use-joined-cta.ts +5 -0
  413. package/src/react/use-local-clock.ts +5 -0
  414. package/src/react/use-scroll-dock.ts +5 -0
  415. package/src/react/use-sticker.ts +5 -0
  416. package/src/react/use-studio-dock.ts +8 -0
  417. package/src/react/use-thumbnail-nav.ts +7 -0
  418. package/src/react/use-trailer.ts +6 -0
  419. package/src/react/use-weight-hover.ts +5 -0
  420. package/src/recipes/action-button/README.md +31 -0
  421. package/src/recipes/action-button/action-button.css +113 -0
  422. package/src/recipes/action-button/create-action-button.ts +56 -0
  423. package/src/recipes/editorial-reveal/README.md +35 -0
  424. package/src/recipes/editorial-reveal/create-editorial-reveal.ts +91 -0
  425. package/src/recipes/editorial-reveal/editorial-reveal.css +17 -0
  426. package/src/recipes/film-hero/README.md +23 -0
  427. package/src/recipes/film-hero/create-film-hero.ts +65 -0
  428. package/src/recipes/film-hero/film-hero.css +29 -0
  429. package/src/recipes/font-cycle/README.md +11 -0
  430. package/src/recipes/font-cycle/create-font-cycle.ts +126 -0
  431. package/src/recipes/font-cycle/font-cycle.css +36 -0
  432. package/src/recipes/image-zoom/README.md +16 -0
  433. package/src/recipes/image-zoom/create-image-zoom.ts +27 -0
  434. package/src/recipes/image-zoom/image-zoom.css +7 -0
  435. package/src/recipes/joined-cta/README.md +13 -0
  436. package/src/recipes/joined-cta/create-joined-cta.ts +22 -0
  437. package/src/recipes/joined-cta/joined-cta.css +17 -0
  438. package/src/recipes/local-clock/README.md +31 -0
  439. package/src/recipes/local-clock/create-local-clock.ts +61 -0
  440. package/src/recipes/scroll-dock/README.md +46 -0
  441. package/src/recipes/scroll-dock/create-scroll-dock.ts +72 -0
  442. package/src/recipes/scroll-dock/scroll-dock.css +70 -0
  443. package/src/recipes/sticker/README.md +59 -0
  444. package/src/recipes/sticker/create-sticker.ts +490 -0
  445. package/src/recipes/sticker/sticker.css +112 -0
  446. package/src/recipes/thumbnail-nav/README.md +16 -0
  447. package/src/recipes/thumbnail-nav/create-thumbnail-nav.ts +165 -0
  448. package/src/recipes/thumbnail-nav/thumbnail-nav.css +108 -0
  449. package/src/recipes/weight-hover/README.md +37 -0
  450. package/src/recipes/weight-hover/create-weight-hover.ts +136 -0
  451. package/src/recipes/weight-hover/weight-hover.css +7 -0
  452. package/dist/chunk-BFC27F2G.js.map +0 -1
  453. package/dist/chunk-J6OCFNGU.js +0 -177
  454. package/dist/chunk-J6OCFNGU.js.map +0 -1
  455. package/dist/chunk-JQMJQSXD.js +0 -2374
  456. package/dist/chunk-JQMJQSXD.js.map +0 -1
  457. package/dist/chunk-XDWBVFHQ.js.map +0 -1
  458. package/dist/reveal-C0hCDcB_.d.ts +0 -415
@@ -0,0 +1,140 @@
1
+ 'use client';
2
+ import { useRef, useState, type FormHTMLAttributes, type ReactNode } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createQuestionnaire, type QuestionnaireAnswers, type QuestionnaireInstance, type QuestionnaireLabels } from './engine';
5
+
6
+ export type QuestionType = 'text' | 'textarea' | 'single' | 'multiple';
7
+
8
+ export interface QuestionOption {
9
+ value: string;
10
+ label: string;
11
+ }
12
+
13
+ export interface Question {
14
+ /** Field name and answer key. */
15
+ id: string;
16
+ label: string;
17
+ type: QuestionType;
18
+ options?: Array<string | QuestionOption>;
19
+ required?: boolean;
20
+ description?: string;
21
+ placeholder?: string;
22
+ }
23
+
24
+ export interface QuestionnaireProps extends Omit<FormHTMLAttributes<HTMLFormElement>, 'onChange' | 'children'> {
25
+ questions: Question[];
26
+ /** Answers the fields start with. */
27
+ defaultAnswers?: QuestionnaireAnswers;
28
+ labels?: QuestionnaireLabels & { next?: string; prev?: string; finish?: string; restart?: string; step?: (index: number, count: number) => ReactNode };
29
+ onChange?: (index: number, answers: QuestionnaireAnswers) => void;
30
+ onComplete?: (answers: QuestionnaireAnswers) => void;
31
+ /** Shown after the last step; a function receives the answers. */
32
+ complete?: ReactNode | ((answers: QuestionnaireAnswers) => ReactNode);
33
+ instanceRef?: { current: QuestionnaireInstance | null };
34
+ }
35
+
36
+ const optionOf = (option: string | QuestionOption): QuestionOption => (typeof option === 'string' ? { value: option, label: option } : option);
37
+
38
+ /**
39
+ * <Questionnaire questions={[{ id: 'name', label: 'Nombre', type: 'text', required: true }, …]} onComplete={save} />.
40
+ * Fields are native and uncontrolled, so answers survive steps and rerenders; the app owns persistence.
41
+ */
42
+ export function Questionnaire({ questions, defaultAnswers = {}, labels = {}, onChange, onComplete, complete, instanceRef, className, ...props }: QuestionnaireProps) {
43
+ const ref = useRef<HTMLFormElement>(null);
44
+ const [answers, setAnswers] = useState<QuestionnaireAnswers | null>(null);
45
+ const change = useLatest(onChange);
46
+ const completed = useLatest(onComplete);
47
+ const key = JSON.stringify(questions.map(({ id, type, required, options }) => [id, type, required, options]));
48
+ const { required, choice } = labels;
49
+ useIsomorphicLayoutEffect(() => {
50
+ const created = createQuestionnaire(ref.current, {
51
+ labels: { required, choice },
52
+ onChange: (index, current) => change.current?.(index, current),
53
+ onComplete: (current) => {
54
+ setAnswers(current);
55
+ completed.current?.(current);
56
+ },
57
+ });
58
+ if (instanceRef) instanceRef.current = created;
59
+ return () => {
60
+ created.destroy();
61
+ if (instanceRef) instanceRef.current = null;
62
+ };
63
+ // eslint-disable-next-line react-hooks/exhaustive-deps
64
+ }, [key, required, choice]);
65
+ const count = questions.length;
66
+ const defaults = (id: string) => defaultAnswers[id];
67
+ return (
68
+ <form {...props} ref={ref} noValidate className={['mk-questionnaire', className].filter(Boolean).join(' ')} onReset={() => setAnswers(null)}>
69
+ <div data-mk-question-progress="" aria-hidden="true">
70
+ <span data-mk-question-bar="" />
71
+ </div>
72
+ {questions.map((question, i) => {
73
+ const titleId = `${question.id}-title`;
74
+ const describedBy = question.description ? `${question.id}-description` : undefined;
75
+ const value = defaults(question.id);
76
+ return (
77
+ <section key={question.id} data-mk-question="" data-mk-step="" hidden={i !== 0} aria-labelledby={titleId}>
78
+ <p data-mk-question-step="">{labels.step ? labels.step(i, count) : `Pregunta ${i + 1} de ${count}`}</p>
79
+ <h3 id={titleId} data-mk-step-title="" data-mk-flow-item="">
80
+ {question.label}
81
+ {question.required ? <span data-mk-question-required="" aria-hidden="true"> *</span> : null}
82
+ </h3>
83
+ {question.description && (
84
+ <p id={describedBy} data-mk-question-description="" data-mk-flow-item="">
85
+ {question.description}
86
+ </p>
87
+ )}
88
+ <div data-mk-question-field="" data-mk-flow-item="">
89
+ {question.type === 'text' && (
90
+ <input name={question.id} type="text" required={question.required} aria-labelledby={titleId} aria-describedby={describedBy} placeholder={question.placeholder} defaultValue={typeof value === 'string' ? value : ''} />
91
+ )}
92
+ {question.type === 'textarea' && (
93
+ <textarea name={question.id} rows={4} required={question.required} aria-labelledby={titleId} aria-describedby={describedBy} placeholder={question.placeholder} defaultValue={typeof value === 'string' ? value : ''} />
94
+ )}
95
+ {(question.type === 'single' || question.type === 'multiple') && (
96
+ <div role={question.type === 'single' ? 'radiogroup' : 'group'} aria-labelledby={titleId} aria-describedby={describedBy} data-mk-question-options="">
97
+ {(question.options ?? []).map(optionOf).map((option) => (
98
+ <label key={option.value} data-mk-question-option="">
99
+ <input
100
+ type={question.type === 'single' ? 'radio' : 'checkbox'}
101
+ name={question.id}
102
+ value={option.value}
103
+ required={question.required}
104
+ defaultChecked={Array.isArray(value) ? value.includes(option.value) : value === option.value}
105
+ />
106
+ <span>{option.label}</span>
107
+ </label>
108
+ ))}
109
+ </div>
110
+ )}
111
+ </div>
112
+ <div data-mk-question-actions="" data-mk-flow-item="">
113
+ {i > 0 && (
114
+ <button type="button" className="mk-button mk-button--text" data-mk-question-prev="">
115
+ {labels.prev ?? 'Anterior'}
116
+ </button>
117
+ )}
118
+ {i < count - 1 ? (
119
+ <button type="button" className="mk-button" data-mk-question-next="">
120
+ {labels.next ?? 'Siguiente'}
121
+ </button>
122
+ ) : (
123
+ <button type="submit" className="mk-button">
124
+ {labels.finish ?? 'Finalizar'}
125
+ </button>
126
+ )}
127
+ </div>
128
+ </section>
129
+ );
130
+ })}
131
+ <p data-mk-question-error="" role="alert" />
132
+ <div data-mk-question-complete="" hidden>
133
+ {answers && (typeof complete === 'function' ? complete(answers) : complete)}
134
+ <button type="reset" className="mk-button mk-button--tonal">
135
+ {labels.restart ?? 'Empezar de nuevo'}
136
+ </button>
137
+ </div>
138
+ </form>
139
+ );
140
+ }
@@ -0,0 +1,58 @@
1
+ # range-calendar
2
+
3
+ Calendario para elegir un rango de fechas en dos clics, de uno o dos meses. El primer clic (o Enter) fija el inicio, que rebota; mientras eliges el final, al señalar se ve una banda más clara con borde discontinuo; el segundo clic cierra el rango (en cualquier dirección) y su banda se dibuja desde el inicio hasta el final (curva wipe, 12 ms entre días, 0,35 s como mucho); el día final rebota. Los días que no se pueden elegir (`min`, `max`, `unavailable`) aparecen tachados y un rango no puede cruzarlos: si lo intentas, el día pulsado pasa a ser el nuevo inicio. Al cambiar de mes, la cuadrícula se desliza como en `calendar`.
4
+
5
+ ## HTML / Astro
6
+
7
+ ```html
8
+ <mk-range-calendar class="mk-range-calendar" visible-months="2" min="2026-11-10"
9
+ unavailable="2026-12-24,2026-12-25"></mk-range-calendar>
10
+ <mk-range-calendar class="mk-range-calendar" start="2026-11-23" end="2026-11-26"></mk-range-calendar>
11
+ ```
12
+
13
+ ## React
14
+
15
+ ```tsx
16
+ const [range, setRange] = useState<{ start: Date; end: Date } | null>(null);
17
+ <RangeCalendar value={range} onChange={setRange} visibleMonths={2} unavailable={(day) => day.getDay() === 0} />
18
+ ```
19
+
20
+ ## Opciones
21
+
22
+ | Opción | Atributo | Por defecto | Qué hace |
23
+ |---|---|---|---|
24
+ | `value` | `start`, `end` | — | Rango `{start, end}` (Date o `'AAAA-MM-DD'`). |
25
+ | `month` | `month` | inicio del rango, o hoy | Primer mes visible. |
26
+ | `visibleMonths` | `visible-months` | `1` | `1` o `2` meses. |
27
+ | `firstDay` | `first-day` | `1` (lunes) | Primer día de la semana (0 = domingo). |
28
+ | `min`, `max` | `min`, `max` | — | Límites. |
29
+ | `unavailable` | `unavailable` (lista con comas) | — | Días `'AAAA-MM-DD'` o una función `(día) => boolean`. |
30
+ | `locale` | `locale` | `lang` de la página, o `es` | Nombres de meses y días. |
31
+ | `today` | `today` | hoy | Para demos y pruebas. |
32
+ | `labels` | — | Mes anterior / Mes siguiente | Nombres de los botones. |
33
+ | `selection` | — | `'range'` | `'single'`: un clic elige un solo día (lo usa `date-time-picker`). |
34
+ | `onChange(range)` | — | — | Rango cerrado `{start, end}` (Date). |
35
+
36
+ ## Teclado
37
+
38
+ | Tecla | Acción |
39
+ |---|---|
40
+ | ← / → | Día anterior / siguiente |
41
+ | ↑ / ↓ | Semana anterior / siguiente |
42
+ | Inicio / Fin | Principio / final de la semana |
43
+ | RePág / AvPág | Mes anterior / siguiente (con Mayús: año) |
44
+ | Enter / Espacio | El primer día fija el inicio; el segundo cierra el rango |
45
+
46
+ Solo un día es tabulable (tabindex itinerante); al salir de los meses visibles con el teclado, el calendario cambia de mes.
47
+
48
+ ## API y eventos
49
+
50
+ `setValue(range | null, animate = true)`, `next()`, `prev()`, `focusDay(día, animate = true)` (con `animate=false` cambia de mes sin deslizar), `value`, `month`, `destroy()`. `bandDuration(días)` dice cuántos segundos tarda en asentarse un rango (banda y rebote del día final). Evento `mk:change` con `{ start: 'AAAA-MM-DD', end: 'AAAA-MM-DD', length }` (días incluidos).
51
+
52
+ ## Estado de los días
53
+
54
+ Atributos en cada botón de día: `data-mk-today`, `data-mk-anchor`, `data-mk-in-range`, `data-mk-range-start`, `data-mk-range-end`, `data-mk-preview` (y `-start`/`-end`), `aria-disabled="true"`. La celda (`role="gridcell"`) lleva `aria-selected="true"` en los días del rango.
55
+
56
+ ## Variables CSS
57
+
58
+ `--mk-range-day` (2.5rem; 2.25rem bajo 380 px), `--mk-range-band` (`--mk-accent-soft`). Esquinas exteriores de 20 px, botones de 8 px. Con movimiento reducido no hay deslizamiento, banda animada ni rebotes.
@@ -0,0 +1,42 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createRangeCalendar, type RangeCalendarInstance } from './engine';
3
+
4
+ /**
5
+ * <mk-range-calendar class="mk-range-calendar" start="2026-11-23" end="2026-11-26" visible-months="2"
6
+ * unavailable="2026-12-24,2026-12-25"></mk-range-calendar>
7
+ * Emits mk:change ({start, end, length}). Changing start/end moves the range.
8
+ */
9
+ export class MkRangeCalendarElement extends MkElement<RangeCalendarInstance> {
10
+ static options: Record<string, OptionType> = {
11
+ start: 'string',
12
+ end: 'string',
13
+ month: 'string',
14
+ visibleMonths: 'number',
15
+ firstDay: 'number',
16
+ min: 'string',
17
+ max: 'string',
18
+ unavailable: 'list',
19
+ locale: 'string',
20
+ today: 'string',
21
+ };
22
+
23
+ protected create(options: Record<string, unknown>) {
24
+ const { start, end, ...rest } = options as { start?: string; end?: string };
25
+ return createRangeCalendar(this, { ...rest, value: start && end ? { start, end } : null });
26
+ }
27
+
28
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
29
+ if (previous === next || !this.instance) return;
30
+ if (name === 'start' || name === 'end') {
31
+ const start = this.getAttribute('start');
32
+ const end = this.getAttribute('end');
33
+ this.instance.setValue(start && end ? { start, end } : null);
34
+ } else super.attributeChangedCallback(name, previous, next);
35
+ }
36
+
37
+ get value() {
38
+ return this.instance?.value ?? null;
39
+ }
40
+ }
41
+
42
+ export const defineRangeCalendar = () => defineElement('mk-range-calendar', MkRangeCalendarElement);
@@ -0,0 +1,390 @@
1
+ import { gsap, EASE, registerMotion } from '../../core/gsap';
2
+ import { reduced } from '../../core/reduced';
3
+ import { createScope } from '../../core/scope';
4
+ import { inert, type Instance } from '../../core/types';
5
+ import {
6
+ addDays, addMonths, capitalize, clampFirstDay, dayKey, monthWeeks, safeFormat, sameDay, sameMonth, startOfMonth, startOfWeek, toDate, weekdayNames,
7
+ type DateInput,
8
+ } from '../../core/dates';
9
+ import { inRange, isPickable, pickDay, rangeKeys, rangeLength, toRange, type DateRange, type DayRules } from '../../core/date-range';
10
+ import { blurIn, press, ringPulse, shake } from '../../motion/micro';
11
+
12
+ /*
13
+ * Range calendar: one or two month grids (role="grid") with one tabbable day
14
+ * (roving tabindex). The first click (or Enter) fixes an anchor that pops; while
15
+ * choosing the end, pointing previews the range as a lighter band; the second
16
+ * click closes it and its band wipes in from the anchor to the end (EASE.wipe,
17
+ * 12 ms apart, .35 s at most), then the end pops. Days that cannot be picked
18
+ * (min, max, unavailable) are aria-disabled; a range may not cross one. Months
19
+ * slide like the calendar component (old grid out 30 px in .12 s, new in from
20
+ * the other side .25 s). Keyboard: arrows ±1/±7 days, Home/End week, PageUp/
21
+ * PageDown month (Shift: year), Enter/Space pick.
22
+ */
23
+
24
+ export interface RangeCalendarOptions {
25
+ value?: { start: DateInput; end: DateInput } | null;
26
+ /** First visible month (default: the value's start, else today). */
27
+ month?: DateInput;
28
+ visibleMonths?: 1 | 2;
29
+ /** 0 = Sunday … 6 = Saturday (default 1, Monday). */
30
+ firstDay?: number;
31
+ min?: DateInput | null;
32
+ max?: DateInput | null;
33
+ /** 'YYYY-MM-DD' keys or a test. */
34
+ unavailable?: string[] | ((day: Date) => boolean);
35
+ locale?: string;
36
+ /** Override "today" (demos, tests). */
37
+ today?: DateInput;
38
+ labels?: { prev?: string; next?: string };
39
+ /** 'range' (default): two clicks; 'single': one click picks a day (date-time-picker). */
40
+ selection?: 'range' | 'single';
41
+ onChange?: (range: DateRange) => void;
42
+ }
43
+
44
+ export interface RangeCalendarInstance extends Instance {
45
+ setValue(range: { start: DateInput; end: DateInput } | null, animate?: boolean): void;
46
+ next(): void;
47
+ prev(): void;
48
+ /** Move the focus to `day`, changing month if needed (`animate` false: no slide). */
49
+ focusDay(day: DateInput, animate?: boolean): void;
50
+ readonly value: DateRange | null;
51
+ readonly month: Date;
52
+ }
53
+
54
+ /** The band of a finished range: each day draws in .22 s, 12 ms after the one before, the run within .35 s. */
55
+ const BAND = { day: 0.22, gap: 0.012, spread: 0.35 };
56
+ const bandGap = (days: number) => Math.min(BAND.gap, BAND.spread / Math.max(1, days));
57
+
58
+ /** Seconds a finished range of `days` takes to settle: its band draws in while its end day pops (.3 s). */
59
+ export const bandDuration = (days: number) => Math.max(0.3, BAND.day + bandGap(days) * Math.max(0, days - 1));
60
+
61
+ const chevron = (path: string) =>
62
+ `<svg aria-hidden="true" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${path}"/></svg>`;
63
+
64
+ const el = <K extends keyof HTMLElementTagNameMap>(tag: K, attrs: Record<string, string> = {}, text?: string) => {
65
+ const element = document.createElement(tag);
66
+ for (const [name, value] of Object.entries(attrs)) element.setAttribute(name, value);
67
+ if (text !== undefined) element.textContent = text;
68
+ return element;
69
+ };
70
+
71
+ export function createRangeCalendar(root: HTMLElement | null, options: RangeCalendarOptions = {}): RangeCalendarInstance {
72
+ if (!root) return inert({ setValue() {}, next() {}, prev() {}, focusDay() {}, value: null, month: startOfMonth(new Date()) });
73
+ registerMotion();
74
+ const scope = createScope();
75
+ const effects = new Set<gsap.core.Animation>();
76
+ const keep = (animation: gsap.core.Animation | null) => {
77
+ if (!animation) return;
78
+ effects.add(animation);
79
+ // Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
80
+ for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
81
+ };
82
+
83
+ const locale = options.locale ?? root.closest('[lang]')?.getAttribute('lang') ?? 'es';
84
+ const firstDay = options.firstDay === undefined ? 1 : clampFirstDay(options.firstDay);
85
+ const count = options.visibleMonths === 2 ? 2 : 1;
86
+ const today = toDate(options.today ?? new Date());
87
+ const unavailable = options.unavailable;
88
+ const rules: DayRules = {
89
+ min: options.min ? toDate(options.min) : null,
90
+ max: options.max ? toDate(options.max) : null,
91
+ isUnavailable: typeof unavailable === 'function' ? unavailable : Array.isArray(unavailable) ? (day) => unavailable.includes(dayKey(day)) : undefined,
92
+ };
93
+ let range: DateRange | null = toRange(options.value);
94
+ let anchor: Date | null = null;
95
+ let hover: Date | null = null;
96
+ let month = startOfMonth(toDate(options.month ?? range?.start ?? today));
97
+ let focused: Date = range?.start && sameMonth(range.start, month) ? range.start : sameMonth(today, month) ? today : month;
98
+ const monthYear = safeFormat(locale, { month: 'long', year: 'numeric' });
99
+ const fullDate = safeFormat(locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
100
+ const lastVisible = () => addDays(startOfMonth(addMonths(month, count)), -1);
101
+ const visible = (day: Date) => day >= month && day <= lastVisible();
102
+
103
+ const header = el('header', { 'data-mk-range-header': '' });
104
+ const prevButton = el('button', { type: 'button', 'data-mk-range-prev': '', 'aria-label': options.labels?.prev ?? 'Mes anterior' });
105
+ prevButton.innerHTML = chevron('M15 6l-6 6 6 6');
106
+ const titleEl = el('h3', { 'data-mk-range-title': '', 'aria-live': 'polite' });
107
+ const nextButton = el('button', { type: 'button', 'data-mk-range-next': '', 'aria-label': options.labels?.next ?? 'Mes siguiente' });
108
+ nextButton.innerHTML = chevron('M9 6l6 6-6 6');
109
+ header.append(prevButton, titleEl, nextButton);
110
+ const months = el('div', { 'data-mk-range-months': '' });
111
+ root.append(header, months);
112
+ scope.add(() => {
113
+ for (const effect of effects) effect.kill();
114
+ effects.clear();
115
+ gsap.killTweensOf([months, titleEl]);
116
+ header.remove();
117
+ months.remove();
118
+ });
119
+ scope.attr(root, 'data-mk-months', String(count));
120
+ const single = options.selection === 'single';
121
+ if (single) scope.attr(root, 'data-mk-selection', 'single');
122
+ scope.attr(root, 'data-mk-ready', '');
123
+
124
+ const title = () => capitalize(count > 1 && typeof monthYear.formatRange === 'function' ? monthYear.formatRange(month, lastVisible()) : monthYear.format(month));
125
+
126
+ const buttons = () => [...months.querySelectorAll<HTMLButtonElement>('button[data-mk-day]')];
127
+ const buttonOf = (day: Date) => months.querySelector<HTMLButtonElement>(`button[data-mk-day="${dayKey(day)}"]`);
128
+ const dayOf = (button: Element) => toDate((button as HTMLElement).dataset.mkDay!);
129
+
130
+ const build = () => {
131
+ const short = weekdayNames(locale, firstDay, 'narrow');
132
+ const long = weekdayNames(locale, firstDay, 'long');
133
+ const grids: HTMLElement[] = [];
134
+ for (let i = 0; i < count; i++) {
135
+ const shown = addMonths(month, i);
136
+ const grid = el('div', { role: 'grid', 'data-mk-range-month': '', 'aria-label': monthYear.format(shown) });
137
+ const head = el('div', { role: 'row', 'data-mk-range-weekdays': '' });
138
+ short.forEach((name, j) => head.append(el('span', { role: 'columnheader', 'data-mk-range-weekday': '', 'aria-label': long[j]! }, name.toLocaleUpperCase(locale))));
139
+ grid.append(head);
140
+ for (const week of monthWeeks(shown, firstDay)) {
141
+ const row = el('div', { role: 'row', 'data-mk-range-week': '' });
142
+ for (const day of week) {
143
+ const cell = el('div', { role: 'gridcell', 'data-mk-range-cell': '' });
144
+ if (!sameMonth(day, shown)) cell.setAttribute('data-mk-outside', '');
145
+ else {
146
+ cell.append(
147
+ el('span', { 'data-mk-day-band': '', 'aria-hidden': 'true' }),
148
+ el('button', { type: 'button', 'data-mk-day': dayKey(day), tabindex: '-1', 'aria-label': fullDate.format(day) }, String(day.getDate())),
149
+ );
150
+ }
151
+ row.append(cell);
152
+ }
153
+ grid.append(row);
154
+ }
155
+ grids.push(grid);
156
+ }
157
+ months.replaceChildren(...grids);
158
+ paint();
159
+ };
160
+
161
+ const ordered = (a: Date, b: Date): DateRange => (a <= b ? { start: a, end: b } : { start: b, end: a });
162
+
163
+ const paint = () => {
164
+ const shown = anchor ? null : range;
165
+ const preview = anchor && hover ? ordered(anchor, hover) : null;
166
+ const all = buttons();
167
+ const target = visible(focused) ? dayKey(focused) : dayKey(all.map(dayOf).find((day) => isPickable(day, rules)) ?? month);
168
+ for (const button of all) {
169
+ const day = dayOf(button);
170
+ const cell = button.parentElement!;
171
+ const selected = inRange(day, shown);
172
+ button.toggleAttribute('data-mk-today', sameDay(day, today));
173
+ button.toggleAttribute('data-mk-anchor', sameDay(day, anchor));
174
+ button.toggleAttribute('data-mk-in-range', selected);
175
+ button.toggleAttribute('data-mk-range-start', !!shown && sameDay(day, shown.start));
176
+ button.toggleAttribute('data-mk-range-end', !!shown && sameDay(day, shown.end));
177
+ button.toggleAttribute('data-mk-preview', inRange(day, preview));
178
+ button.toggleAttribute('data-mk-preview-start', !!preview && sameDay(day, preview.start));
179
+ button.toggleAttribute('data-mk-preview-end', !!preview && sameDay(day, preview.end));
180
+ if (isPickable(day, rules)) button.removeAttribute('aria-disabled');
181
+ else button.setAttribute('aria-disabled', 'true');
182
+ if (selected) cell.setAttribute('aria-selected', 'true');
183
+ else cell.removeAttribute('aria-selected');
184
+ button.setAttribute('tabindex', button.dataset.mkDay === target ? '0' : '-1');
185
+ }
186
+ };
187
+
188
+ const go = (next: Date, direction: 1 | -1, after?: () => void, animate = true) => {
189
+ month = startOfMonth(next);
190
+ gsap.killTweensOf([months, titleEl]);
191
+ if (reduced() || !animate) {
192
+ build();
193
+ titleEl.textContent = title();
194
+ after?.();
195
+ return;
196
+ }
197
+ keep(
198
+ gsap.to(months, {
199
+ x: -direction * 30,
200
+ opacity: 0,
201
+ duration: 0.12,
202
+ ease: 'power2.in',
203
+ onComplete: () => {
204
+ build();
205
+ titleEl.textContent = title();
206
+ keep(blurIn(titleEl, { from: 'soft', duration: 0.3 }));
207
+ keep(gsap.fromTo(months, { x: direction * 30, opacity: 0 }, { x: 0, opacity: 1, duration: 0.25, ease: 'power2.out', clearProps: 'x,opacity,transform' }));
208
+ after?.();
209
+ },
210
+ }),
211
+ );
212
+ };
213
+
214
+ const focusDay = (input: DateInput, animate = true) => {
215
+ focused = toDate(input);
216
+ if (anchor) hover = focused;
217
+ if (visible(focused)) {
218
+ paint();
219
+ buttonOf(focused)?.focus();
220
+ return;
221
+ }
222
+ const before = focused < month;
223
+ go(before ? startOfMonth(focused) : startOfMonth(addMonths(focused, -(count - 1))), before ? -1 : 1, () => buttonOf(focused)?.focus(), animate);
224
+ };
225
+
226
+ const pop = (button: HTMLElement | null) => {
227
+ if (button && !reduced()) keep(gsap.fromTo(button, { scale: 0.6 }, { scale: 1, duration: 0.3, ease: EASE.pop, clearProps: 'scale,transform' }));
228
+ };
229
+
230
+ /** The band of the finished range draws itself from the anchor to the end. */
231
+ const wipe = (forward: boolean, last: Date | null) => {
232
+ if (reduced() || !range) return;
233
+ const bands = buttons()
234
+ .filter((button) => button.hasAttribute('data-mk-in-range'))
235
+ .map((button) => button.previousElementSibling as HTMLElement);
236
+ if (!forward) bands.reverse();
237
+ if (bands.length) {
238
+ keep(
239
+ gsap.fromTo(
240
+ bands,
241
+ { scaleX: 0 },
242
+ {
243
+ scaleX: 1,
244
+ duration: BAND.day,
245
+ ease: EASE.wipe,
246
+ stagger: bandGap(bands.length),
247
+ transformOrigin: forward ? '0% 50%' : '100% 50%',
248
+ clearProps: 'scale,transform,transformOrigin',
249
+ },
250
+ ),
251
+ );
252
+ }
253
+ pop(last ? buttonOf(last) : null);
254
+ };
255
+
256
+ const emit = () => {
257
+ if (!range) return;
258
+ options.onChange?.({ ...range });
259
+ root.dispatchEvent(new CustomEvent('mk:change', { bubbles: true, detail: { ...rangeKeys(range), length: rangeLength(range) } }));
260
+ };
261
+
262
+ const pick = (day: Date) => {
263
+ const button = buttonOf(day);
264
+ if (!isPickable(day, rules)) {
265
+ if (button) keep(shake(button, 3));
266
+ return;
267
+ }
268
+ if (single) {
269
+ // One click, one day: it pops and the change is reported.
270
+ range = { start: day, end: day };
271
+ anchor = null;
272
+ hover = null;
273
+ paint();
274
+ pop(button);
275
+ emit();
276
+ return;
277
+ }
278
+ const wasAnchor = anchor;
279
+ const next = pickDay(anchor, day, rules);
280
+ anchor = next.anchor;
281
+ hover = anchor;
282
+ if (!next.range) {
283
+ paint();
284
+ if (anchor && button && !reduced()) {
285
+ pop(button);
286
+ keep(ringPulse(button));
287
+ }
288
+ return;
289
+ }
290
+ range = next.range;
291
+ paint();
292
+ wipe(wasAnchor !== null && wasAnchor <= day, day);
293
+ emit();
294
+ };
295
+
296
+ const KEYS: Record<string, (day: Date, shift: boolean) => Date> = {
297
+ ArrowLeft: (day) => addDays(day, -1),
298
+ ArrowRight: (day) => addDays(day, 1),
299
+ ArrowUp: (day) => addDays(day, -7),
300
+ ArrowDown: (day) => addDays(day, 7),
301
+ Home: (day) => startOfWeek(day, firstDay),
302
+ End: (day) => addDays(startOfWeek(day, firstDay), 6),
303
+ PageUp: (day, shift) => addMonths(day, shift ? -12 : -1),
304
+ PageDown: (day, shift) => addMonths(day, shift ? 12 : 1),
305
+ };
306
+ scope.on(months, 'keydown', (event) => {
307
+ const key = event as KeyboardEvent;
308
+ const button = (key.target as Element | null)?.closest('button[data-mk-day]');
309
+ if (!button) return;
310
+ const move = KEYS[key.key];
311
+ if (move) {
312
+ key.preventDefault();
313
+ focusDay(move(dayOf(button), key.shiftKey));
314
+ } else if (key.key === 'Enter' || key.key === ' ') {
315
+ key.preventDefault();
316
+ focused = dayOf(button);
317
+ pick(focused);
318
+ }
319
+ });
320
+ scope.on(months, 'click', (event) => {
321
+ const button = (event.target as Element | null)?.closest<HTMLButtonElement>('button[data-mk-day]');
322
+ if (!button) return;
323
+ focused = dayOf(button);
324
+ pick(focused);
325
+ });
326
+ scope.on(months, 'pointerover', (event) => {
327
+ if (!anchor) return;
328
+ const button = (event.target as Element | null)?.closest('button[data-mk-day]');
329
+ if (!button) return;
330
+ const day = dayOf(button);
331
+ if (hover && sameDay(hover, day)) return;
332
+ hover = day;
333
+ paint();
334
+ });
335
+ scope.on(months, 'pointerleave', () => {
336
+ if (!anchor || !hover) return;
337
+ hover = anchor;
338
+ paint();
339
+ });
340
+ const step = (direction: 1 | -1) => {
341
+ // The focused day follows so that the tabbable day stays in view.
342
+ focused = addMonths(focused, direction);
343
+ go(addMonths(month, direction), direction);
344
+ };
345
+ scope.on(prevButton, 'click', () => {
346
+ keep(press(prevButton));
347
+ step(-1);
348
+ });
349
+ scope.on(nextButton, 'click', () => {
350
+ keep(press(nextButton));
351
+ step(1);
352
+ });
353
+
354
+ titleEl.textContent = title();
355
+ build();
356
+
357
+ return {
358
+ setValue(next, animate = true) {
359
+ if (scope.disposed) return;
360
+ range = toRange(next);
361
+ anchor = null;
362
+ hover = null;
363
+ if (range && !visible(range.start)) {
364
+ focused = range.start;
365
+ go(
366
+ startOfMonth(range.start),
367
+ range.start < month ? -1 : 1,
368
+ () => {
369
+ if (animate) wipe(true, null);
370
+ },
371
+ animate,
372
+ );
373
+ return;
374
+ }
375
+ if (range) focused = range.start;
376
+ paint();
377
+ if (animate) wipe(true, null);
378
+ },
379
+ next: () => step(1),
380
+ prev: () => step(-1),
381
+ focusDay,
382
+ get value() {
383
+ return range ? { ...range } : null;
384
+ },
385
+ get month() {
386
+ return new Date(month);
387
+ },
388
+ destroy: () => scope.dispose(),
389
+ };
390
+ }