@yewdiel21/motion-kit 0.0.0-stage → 0.9.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 (391) hide show
  1. package/NOTICE +38 -0
  2. package/README.md +80 -2
  3. package/bin/motion-kit.mjs +242 -0
  4. package/css.d.ts +2 -0
  5. package/dist/auto/index.d.ts +63 -0
  6. package/dist/auto/index.js +207 -0
  7. package/dist/auto/index.js.map +1 -0
  8. package/dist/chunk-2BQGFOA7.js +177 -0
  9. package/dist/chunk-2BQGFOA7.js.map +1 -0
  10. package/dist/chunk-34XYOXHG.js +2374 -0
  11. package/dist/chunk-34XYOXHG.js.map +1 -0
  12. package/dist/chunk-6OJAP6ZL.js +832 -0
  13. package/dist/chunk-6OJAP6ZL.js.map +1 -0
  14. package/dist/chunk-ABNRDDO7.js +28 -0
  15. package/dist/chunk-ABNRDDO7.js.map +1 -0
  16. package/dist/chunk-FS2HJVVY.js +3320 -0
  17. package/dist/chunk-FS2HJVVY.js.map +1 -0
  18. package/dist/chunk-J27KH2A2.js +157 -0
  19. package/dist/chunk-J27KH2A2.js.map +1 -0
  20. package/dist/chunk-KLY5BRRI.js +18 -0
  21. package/dist/chunk-KLY5BRRI.js.map +1 -0
  22. package/dist/chunk-Q4LVFNLN.js +1617 -0
  23. package/dist/chunk-Q4LVFNLN.js.map +1 -0
  24. package/dist/chunk-RFDIZYFX.js +304 -0
  25. package/dist/chunk-RFDIZYFX.js.map +1 -0
  26. package/dist/chunk-TGV27ZWM.js +17 -0
  27. package/dist/chunk-TGV27ZWM.js.map +1 -0
  28. package/dist/confetti-BSX1CLLV.d.ts +41 -0
  29. package/dist/css/components/chat-background.css +87 -0
  30. package/dist/css/components/chat.css +237 -0
  31. package/dist/css/components/cursor.css +178 -0
  32. package/dist/css/components/dialog.css +259 -0
  33. package/dist/css/components/island.css +301 -0
  34. package/dist/css/components/menu.css +105 -0
  35. package/dist/css/components/nav.css +1054 -0
  36. package/dist/css/components/steps.css +50 -0
  37. package/dist/css/components/theme-picker.css +115 -0
  38. package/dist/css/components/toast.css +115 -0
  39. package/dist/css/controls.css +950 -0
  40. package/dist/css/effects.css +587 -0
  41. package/dist/css/fonts.css +3 -0
  42. package/dist/css/themes/black-green.css +126 -0
  43. package/dist/css/themes/black.css +126 -0
  44. package/dist/css/themes/blue.css +126 -0
  45. package/dist/css/themes/brown.css +126 -0
  46. package/dist/css/themes/cold-blue.css +126 -0
  47. package/dist/css/themes/funny.css +126 -0
  48. package/dist/css/themes/gray.css +126 -0
  49. package/dist/css/themes/green.css +126 -0
  50. package/dist/css/themes/modern-1.css +126 -0
  51. package/dist/css/themes/neogreen.css +126 -0
  52. package/dist/css/themes/new.css +126 -0
  53. package/dist/css/themes/new2.css +126 -0
  54. package/dist/css/themes/oled.css +126 -0
  55. package/dist/css/themes/orange.css +126 -0
  56. package/dist/css/themes/pastel-pink.css +126 -0
  57. package/dist/css/themes/pink.css +126 -0
  58. package/dist/css/themes/purple.css +126 -0
  59. package/dist/css/themes/red.css +126 -0
  60. package/dist/css/themes/super-blue.css +126 -0
  61. package/dist/css/themes.css +2431 -0
  62. package/dist/css/tokens.css +452 -0
  63. package/dist/elements/index.d.ts +183 -0
  64. package/dist/elements/index.js +472 -0
  65. package/dist/elements/index.js.map +1 -0
  66. package/dist/engine-CUr9QijM.d.ts +46 -0
  67. package/dist/gl/index.d.ts +61 -0
  68. package/dist/gl/index.js +18 -0
  69. package/dist/gl/index.js.map +1 -0
  70. package/dist/index.css +5296 -0
  71. package/dist/index.d.ts +440 -0
  72. package/dist/index.js +390 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/react/index.d.ts +476 -0
  75. package/dist/react/index.js +969 -0
  76. package/dist/react/index.js.map +1 -0
  77. package/dist/reduced-BM4Ls0g5.d.ts +566 -0
  78. package/dist/reveal-WFUWDrei.d.ts +416 -0
  79. package/dist/store-Bqy_obnD.d.ts +195 -0
  80. package/dist/theme/generate.d.ts +18 -0
  81. package/dist/theme/generate.js +64 -0
  82. package/dist/theme/generate.js.map +1 -0
  83. package/dist/theme/index.d.ts +17 -0
  84. package/dist/theme/index.js +32 -0
  85. package/dist/theme/index.js.map +1 -0
  86. package/dist/theme-BFMoCzi6.d.ts +158 -0
  87. package/dist/types-BkS_raL8.d.ts +7 -0
  88. package/package.json +114 -3
  89. package/registry.json +469 -0
  90. package/skills/motion-kit/SKILL.md +189 -0
  91. package/skills/motion-kit/reference/components.md +86 -0
  92. package/skills/motion-kit/reference/design.md +92 -0
  93. package/skills/motion-kit/reference/recipes.md +172 -0
  94. package/src/auto/auto-init.ts +151 -0
  95. package/src/auto/index.ts +3 -0
  96. package/src/auto/parse-options.ts +68 -0
  97. package/src/components/chat/README.md +90 -0
  98. package/src/components/chat/chat.css +236 -0
  99. package/src/components/chat/element.ts +98 -0
  100. package/src/components/chat/engine.ts +295 -0
  101. package/src/components/chat/react.tsx +196 -0
  102. package/src/components/chat-background/README.md +50 -0
  103. package/src/components/chat-background/audio.ts +56 -0
  104. package/src/components/chat-background/chat-background.css +86 -0
  105. package/src/components/chat-background/element.ts +31 -0
  106. package/src/components/chat-background/engine.ts +108 -0
  107. package/src/components/chat-background/react.tsx +36 -0
  108. package/src/components/cursor/README.md +53 -0
  109. package/src/components/cursor/cursor.css +177 -0
  110. package/src/components/cursor/element.ts +23 -0
  111. package/src/components/cursor/engine.ts +254 -0
  112. package/src/components/cursor/react.tsx +26 -0
  113. package/src/components/dialog/README.md +106 -0
  114. package/src/components/dialog/dialog.css +258 -0
  115. package/src/components/dialog/element.ts +118 -0
  116. package/src/components/dialog/engine.ts +432 -0
  117. package/src/components/dialog/react.tsx +200 -0
  118. package/src/components/icon.tsx +10 -0
  119. package/src/components/icons.ts +24 -0
  120. package/src/components/island/README.md +91 -0
  121. package/src/components/island/element.ts +45 -0
  122. package/src/components/island/engine.ts +200 -0
  123. package/src/components/island/island.css +300 -0
  124. package/src/components/island/react.tsx +115 -0
  125. package/src/components/menu/README.md +67 -0
  126. package/src/components/menu/element.ts +55 -0
  127. package/src/components/menu/engine.ts +142 -0
  128. package/src/components/menu/menu.css +104 -0
  129. package/src/components/menu/react.tsx +82 -0
  130. package/src/components/nav/README.md +85 -0
  131. package/src/components/nav/element.ts +39 -0
  132. package/src/components/nav/engine.ts +164 -0
  133. package/src/components/nav/nav.css +1053 -0
  134. package/src/components/nav/react.tsx +90 -0
  135. package/src/components/steps/README.md +70 -0
  136. package/src/components/steps/element.ts +43 -0
  137. package/src/components/steps/engine.ts +185 -0
  138. package/src/components/steps/react.tsx +91 -0
  139. package/src/components/steps/steps.css +49 -0
  140. package/src/components/theme-picker/README.md +57 -0
  141. package/src/components/theme-picker/element.ts +20 -0
  142. package/src/components/theme-picker/engine.ts +91 -0
  143. package/src/components/theme-picker/react.tsx +31 -0
  144. package/src/components/theme-picker/theme-picker.css +114 -0
  145. package/src/components/toast/README.md +60 -0
  146. package/src/components/toast/element.ts +25 -0
  147. package/src/components/toast/engine.ts +483 -0
  148. package/src/components/toast/react.tsx +22 -0
  149. package/src/components/toast/store.ts +122 -0
  150. package/src/components/toast/toast.css +114 -0
  151. package/src/core/channels.ts +165 -0
  152. package/src/core/curves.ts +35 -0
  153. package/src/core/frame.ts +12 -0
  154. package/src/core/gsap.ts +65 -0
  155. package/src/core/loop.ts +44 -0
  156. package/src/core/math.ts +9 -0
  157. package/src/core/physics-spring.ts +59 -0
  158. package/src/core/pointer.ts +63 -0
  159. package/src/core/reduced.ts +21 -0
  160. package/src/core/scope.ts +70 -0
  161. package/src/core/scroll-track.ts +106 -0
  162. package/src/core/split.ts +68 -0
  163. package/src/core/spring.ts +22 -0
  164. package/src/core/styles.ts +31 -0
  165. package/src/core/timing.ts +204 -0
  166. package/src/core/types.ts +20 -0
  167. package/src/core/visible.ts +19 -0
  168. package/src/css/base.css +142 -0
  169. package/src/css/controls/README.md +122 -0
  170. package/src/css/controls/burger.css +67 -0
  171. package/src/css/controls/button.css +226 -0
  172. package/src/css/controls/checkbox.css +53 -0
  173. package/src/css/controls/chip.css +85 -0
  174. package/src/css/controls/field.css +124 -0
  175. package/src/css/controls/glass.css +220 -0
  176. package/src/css/controls/icon.css +26 -0
  177. package/src/css/controls/segmented.css +60 -0
  178. package/src/css/controls/switch.css +72 -0
  179. package/src/css/default-theme.css +123 -0
  180. package/src/css/effects/README.md +134 -0
  181. package/src/css/effects/anim.css +224 -0
  182. package/src/css/effects/fade.css +15 -0
  183. package/src/css/effects/float.css +64 -0
  184. package/src/css/effects/frost.css +62 -0
  185. package/src/css/effects/grain.css +58 -0
  186. package/src/css/effects/loading.css +60 -0
  187. package/src/css/effects/success.css +22 -0
  188. package/src/css/effects/text.css +67 -0
  189. package/src/css/fonts.css +3 -0
  190. package/src/css/states.css +51 -0
  191. package/src/css/themes/black-green.css +126 -0
  192. package/src/css/themes/black.css +126 -0
  193. package/src/css/themes/blue.css +126 -0
  194. package/src/css/themes/brown.css +126 -0
  195. package/src/css/themes/cold-blue.css +126 -0
  196. package/src/css/themes/funny.css +126 -0
  197. package/src/css/themes/gray.css +126 -0
  198. package/src/css/themes/green.css +126 -0
  199. package/src/css/themes/modern-1.css +126 -0
  200. package/src/css/themes/neogreen.css +126 -0
  201. package/src/css/themes/new.css +126 -0
  202. package/src/css/themes/new2.css +126 -0
  203. package/src/css/themes/oled.css +126 -0
  204. package/src/css/themes/orange.css +126 -0
  205. package/src/css/themes/pastel-pink.css +126 -0
  206. package/src/css/themes/pink.css +126 -0
  207. package/src/css/themes/purple.css +126 -0
  208. package/src/css/themes/red.css +126 -0
  209. package/src/css/themes/super-blue.css +126 -0
  210. package/src/css/tokens.css +182 -0
  211. package/src/elements/base.ts +76 -0
  212. package/src/elements/index.ts +48 -0
  213. package/src/gl/index.ts +15 -0
  214. package/src/gl/liquid-blend/README.md +23 -0
  215. package/src/gl/liquid-blend/create-liquid-blend.ts +7 -0
  216. package/src/gl/liquid-blend/shader.ts +9 -0
  217. package/src/gl/matcap/README.md +23 -0
  218. package/src/gl/matcap/create-matcap.ts +7 -0
  219. package/src/gl/matcap/shader.ts +16 -0
  220. package/src/gl/particle-hero/README.md +41 -0
  221. package/src/gl/particle-hero/create-particle-hero.ts +544 -0
  222. package/src/gl/particle-hero/particle-hero.css +19 -0
  223. package/src/gl/particle-hero/shaders.ts +84 -0
  224. package/src/gl/shader-blur/README.md +23 -0
  225. package/src/gl/shader-blur/create-shader-blur.ts +7 -0
  226. package/src/gl/shader-blur/shader.ts +11 -0
  227. package/src/gl/shader-grain/README.md +23 -0
  228. package/src/gl/shader-grain/create-shader-grain.ts +7 -0
  229. package/src/gl/shader-grain/shader.ts +6 -0
  230. package/src/gl/shared/create-shader-effect.ts +186 -0
  231. package/src/gl/shared/shader.css +3 -0
  232. package/src/gl/shared/shaders.ts +30 -0
  233. package/src/index.ts +177 -0
  234. package/src/motion/anchor.ts +45 -0
  235. package/src/motion/chat.ts +97 -0
  236. package/src/motion/confetti.ts +130 -0
  237. package/src/motion/count.ts +95 -0
  238. package/src/motion/details.ts +70 -0
  239. package/src/motion/drag-dismiss.ts +231 -0
  240. package/src/motion/effects.ts +81 -0
  241. package/src/motion/entrances.ts +27 -0
  242. package/src/motion/floating.ts +389 -0
  243. package/src/motion/flow.ts +115 -0
  244. package/src/motion/indicator.ts +44 -0
  245. package/src/motion/layout.ts +39 -0
  246. package/src/motion/liquid.ts +227 -0
  247. package/src/motion/morph.ts +540 -0
  248. package/src/motion/reveal.ts +112 -0
  249. package/src/motion/sheet.ts +65 -0
  250. package/src/motion/surface.ts +108 -0
  251. package/src/react/animated-number.tsx +30 -0
  252. package/src/react/flip-group.tsx +44 -0
  253. package/src/react/frost.tsx +33 -0
  254. package/src/react/index.ts +80 -0
  255. package/src/react/slot-text.tsx +44 -0
  256. package/src/react/theme-provider.tsx +42 -0
  257. package/src/react/use-card-dock.ts +9 -0
  258. package/src/react/use-cascade.ts +9 -0
  259. package/src/react/use-contrast.ts +9 -0
  260. package/src/react/use-counter.ts +8 -0
  261. package/src/react/use-curved-carousel.ts +10 -0
  262. package/src/react/use-depth-grid.ts +9 -0
  263. package/src/react/use-details.ts +7 -0
  264. package/src/react/use-dock-pill.ts +10 -0
  265. package/src/react/use-drag-ruler.ts +10 -0
  266. package/src/react/use-fill-text.ts +9 -0
  267. package/src/react/use-fit-text.ts +8 -0
  268. package/src/react/use-flow.ts +8 -0
  269. package/src/react/use-frame-zoom.ts +9 -0
  270. package/src/react/use-infinite-reel.ts +8 -0
  271. package/src/react/use-letters.ts +9 -0
  272. package/src/react/use-liquid-blend.ts +8 -0
  273. package/src/react/use-marquee.ts +9 -0
  274. package/src/react/use-mask-text.ts +9 -0
  275. package/src/react/use-matcap.ts +8 -0
  276. package/src/react/use-parallax.ts +9 -0
  277. package/src/react/use-particle-hero.ts +9 -0
  278. package/src/react/use-pile.ts +8 -0
  279. package/src/react/use-pop.ts +8 -0
  280. package/src/react/use-radial-menu.ts +10 -0
  281. package/src/react/use-recipe.ts +38 -0
  282. package/src/react/use-reflow.ts +9 -0
  283. package/src/react/use-repel-bento.ts +8 -0
  284. package/src/react/use-reveal.ts +8 -0
  285. package/src/react/use-rotator.ts +10 -0
  286. package/src/react/use-shader-blur.ts +8 -0
  287. package/src/react/use-shader-grain.ts +8 -0
  288. package/src/react/use-slot-text.ts +17 -0
  289. package/src/react/use-snap-strip.ts +9 -0
  290. package/src/react/use-sortable.ts +14 -0
  291. package/src/react/use-spring-cards.ts +8 -0
  292. package/src/react/use-stretch-footer.ts +20 -0
  293. package/src/react/use-tabs.ts +10 -0
  294. package/src/react/use-text-roll.ts +9 -0
  295. package/src/react/use-ticket-menu.ts +15 -0
  296. package/src/react/use-words.ts +8 -0
  297. package/src/recipes/card-dock/README.md +36 -0
  298. package/src/recipes/card-dock/card-dock.css +178 -0
  299. package/src/recipes/card-dock/create-card-dock.ts +97 -0
  300. package/src/recipes/cascade/README.md +33 -0
  301. package/src/recipes/cascade/create-cascade.ts +111 -0
  302. package/src/recipes/contrast/README.md +29 -0
  303. package/src/recipes/contrast/create-contrast.ts +68 -0
  304. package/src/recipes/counter/README.md +42 -0
  305. package/src/recipes/counter/create-counter.ts +84 -0
  306. package/src/recipes/curved-carousel/README.md +50 -0
  307. package/src/recipes/curved-carousel/carousel-math.ts +20 -0
  308. package/src/recipes/curved-carousel/create-curved-carousel.ts +257 -0
  309. package/src/recipes/curved-carousel/curved-carousel.css +50 -0
  310. package/src/recipes/depth-grid/README.md +30 -0
  311. package/src/recipes/depth-grid/create-depth-grid.ts +120 -0
  312. package/src/recipes/details/README.md +31 -0
  313. package/src/recipes/details/create-details.ts +27 -0
  314. package/src/recipes/dock-pill/README.md +23 -0
  315. package/src/recipes/dock-pill/create-dock-pill.ts +68 -0
  316. package/src/recipes/dock-pill/dock-pill.css +5 -0
  317. package/src/recipes/drag-ruler/README.md +21 -0
  318. package/src/recipes/drag-ruler/create-drag-ruler.ts +95 -0
  319. package/src/recipes/drag-ruler/drag-ruler.css +16 -0
  320. package/src/recipes/fill-text/README.md +27 -0
  321. package/src/recipes/fill-text/create-fill-text.ts +93 -0
  322. package/src/recipes/fit-text/README.md +37 -0
  323. package/src/recipes/fit-text/create-fit-text.ts +55 -0
  324. package/src/recipes/fit-text/fit-text.css +6 -0
  325. package/src/recipes/flow/README.md +46 -0
  326. package/src/recipes/flow/create-flow.ts +55 -0
  327. package/src/recipes/frame-zoom/README.md +29 -0
  328. package/src/recipes/frame-zoom/create-frame-zoom.ts +91 -0
  329. package/src/recipes/infinite-reel/README.md +23 -0
  330. package/src/recipes/infinite-reel/create-infinite-reel.ts +140 -0
  331. package/src/recipes/infinite-reel/infinite-reel.css +8 -0
  332. package/src/recipes/letters/README.md +30 -0
  333. package/src/recipes/letters/create-letters.ts +113 -0
  334. package/src/recipes/marquee/README.md +32 -0
  335. package/src/recipes/marquee/create-marquee.ts +74 -0
  336. package/src/recipes/marquee/marquee.css +41 -0
  337. package/src/recipes/mask-text/README.md +39 -0
  338. package/src/recipes/mask-text/create-mask-text.ts +134 -0
  339. package/src/recipes/parallax/README.md +31 -0
  340. package/src/recipes/parallax/create-parallax.ts +74 -0
  341. package/src/recipes/pile/README.md +48 -0
  342. package/src/recipes/pile/create-pile.ts +50 -0
  343. package/src/recipes/pile/pile-math.ts +27 -0
  344. package/src/recipes/pile/pile.css +63 -0
  345. package/src/recipes/pill/README.md +30 -0
  346. package/src/recipes/pill/pill.css +48 -0
  347. package/src/recipes/pop/README.md +36 -0
  348. package/src/recipes/pop/create-pop.ts +52 -0
  349. package/src/recipes/radial-menu/README.md +44 -0
  350. package/src/recipes/radial-menu/create-radial-menu.ts +96 -0
  351. package/src/recipes/radial-menu/radial-menu.css +45 -0
  352. package/src/recipes/reflow/README.md +47 -0
  353. package/src/recipes/reflow/create-reflow.ts +49 -0
  354. package/src/recipes/repel-bento/README.md +21 -0
  355. package/src/recipes/repel-bento/create-repel-bento.ts +65 -0
  356. package/src/recipes/repel-bento/repel-bento.css +3 -0
  357. package/src/recipes/reveal/README.md +36 -0
  358. package/src/recipes/reveal/create-reveal.ts +48 -0
  359. package/src/recipes/rotator/README.md +21 -0
  360. package/src/recipes/rotator/create-rotator.ts +66 -0
  361. package/src/recipes/rotator/rotator.css +3 -0
  362. package/src/recipes/slot-text/README.md +44 -0
  363. package/src/recipes/slot-text/create-slot-text.ts +244 -0
  364. package/src/recipes/slot-text/slot-text.css +42 -0
  365. package/src/recipes/snap-strip/README.md +31 -0
  366. package/src/recipes/snap-strip/create-snap-strip.ts +133 -0
  367. package/src/recipes/snap-strip/snap-strip.css +23 -0
  368. package/src/recipes/sortable/README.md +88 -0
  369. package/src/recipes/sortable/create-sortable.ts +863 -0
  370. package/src/recipes/sortable/sortable.css +40 -0
  371. package/src/recipes/spring-cards/README.md +48 -0
  372. package/src/recipes/spring-cards/create-spring-cards.ts +140 -0
  373. package/src/recipes/spring-cards/spring-cards.css +23 -0
  374. package/src/recipes/stretch-footer/README.md +44 -0
  375. package/src/recipes/stretch-footer/create-stretch-footer.ts +69 -0
  376. package/src/recipes/stretch-footer/stretch-footer.css +28 -0
  377. package/src/recipes/tabs/README.md +39 -0
  378. package/src/recipes/tabs/create-tabs.ts +91 -0
  379. package/src/recipes/tabs/tabs.css +19 -0
  380. package/src/recipes/text-roll/README.md +35 -0
  381. package/src/recipes/text-roll/create-text-roll.ts +137 -0
  382. package/src/recipes/text-roll/text-roll.css +21 -0
  383. package/src/recipes/ticket-menu/README.md +24 -0
  384. package/src/recipes/ticket-menu/create-ticket-menu.ts +6 -0
  385. package/src/recipes/words/README.md +43 -0
  386. package/src/recipes/words/create-words.ts +114 -0
  387. package/src/theme/css-format.ts +32 -0
  388. package/src/theme/generate.ts +72 -0
  389. package/src/theme/index.ts +15 -0
  390. package/src/theme/presets.ts +79 -0
  391. package/src/theme/theme.ts +119 -0
@@ -0,0 +1,3320 @@
1
+ import {
2
+ EASE,
3
+ ELASTIC,
4
+ Flip,
5
+ ScrollTrigger,
6
+ SplitText,
7
+ bindPointer,
8
+ clearStyles,
9
+ countTo,
10
+ createMenu,
11
+ flowIn,
12
+ gsap,
13
+ motionSpring,
14
+ pointer,
15
+ registerMotion,
16
+ revealFrom,
17
+ splitGraphemes,
18
+ takeMovement
19
+ } from "./chunk-34XYOXHG.js";
20
+ import {
21
+ clamp,
22
+ damp,
23
+ mapRange,
24
+ wrap
25
+ } from "./chunk-KLY5BRRI.js";
26
+ import {
27
+ TIMING,
28
+ createScope,
29
+ entrances,
30
+ reduced,
31
+ whileVisible
32
+ } from "./chunk-RFDIZYFX.js";
33
+ import {
34
+ inert,
35
+ warnOnce
36
+ } from "./chunk-TGV27ZWM.js";
37
+
38
+ // src/core/scroll-track.ts
39
+ var clamp01 = (value) => Math.min(1, Math.max(0, value));
40
+ function edgeOf(box, edge) {
41
+ if (edge === "top") return box.top;
42
+ if (edge === "bottom") return box.bottom;
43
+ if (edge === "center") return box.top + box.height / 2;
44
+ if (edge.endsWith("%")) return box.top + box.height * parseFloat(edge) / 100;
45
+ return box.top + (parseFloat(edge) || 0);
46
+ }
47
+ function lineOf(height, line) {
48
+ if (line === "top") return 0;
49
+ if (line === "bottom") return height;
50
+ if (line === "center") return height / 2;
51
+ if (line.endsWith("%")) return height * parseFloat(line) / 100;
52
+ return parseFloat(line) || 0;
53
+ }
54
+ function trackScroll(el, { start = "top bottom", end = "bottom top", lerp = 1, onUpdate } = {}) {
55
+ const [startEdge = "top", startLine = "bottom"] = start.trim().split(/\s+/);
56
+ const [endEdge = "bottom", endLine = "top"] = end.trim().split(/\s+/);
57
+ let destroyed = false;
58
+ const measure = () => {
59
+ const box = el.getBoundingClientRect();
60
+ const height = window.innerHeight;
61
+ const fromStart = lineOf(height, startLine) - edgeOf(box, startEdge);
62
+ const fromEnd = lineOf(height, endLine) - edgeOf(box, endEdge);
63
+ const distance = fromStart - fromEnd;
64
+ if (distance <= 0) return fromStart >= 0 ? 1 : 0;
65
+ return clamp01(fromStart / distance);
66
+ };
67
+ let target = measure();
68
+ let value = target;
69
+ onUpdate?.(value);
70
+ const update = (dt = 1) => {
71
+ if (destroyed) return;
72
+ target = measure();
73
+ const step = lerp >= 1 ? 1 : 1 - Math.pow(1 - Math.max(lerp, 0), dt);
74
+ value += (target - value) * step;
75
+ if (Math.abs(target - value) < 1e-4) value = target;
76
+ onUpdate?.(value);
77
+ };
78
+ const stop = whileVisible(el, update, "200px");
79
+ return {
80
+ get value() {
81
+ return value;
82
+ },
83
+ get target() {
84
+ return target;
85
+ },
86
+ measure,
87
+ update,
88
+ set(next) {
89
+ if (destroyed) return;
90
+ value = target = clamp01(next);
91
+ onUpdate?.(value);
92
+ },
93
+ destroy() {
94
+ if (destroyed) return;
95
+ destroyed = true;
96
+ stop();
97
+ }
98
+ };
99
+ }
100
+
101
+ // src/motion/details.ts
102
+ var running = /* @__PURE__ */ new WeakMap();
103
+ function toggleDetails(details, open, { duration = TIMING.details } = {}) {
104
+ registerMotion();
105
+ const summary = details.querySelector(":scope > summary");
106
+ const chevron = summary?.querySelector("[data-mk-chevron]");
107
+ running.get(details)?.kill();
108
+ if (!summary || reduced()) {
109
+ details.open = open;
110
+ if (chevron) gsap.set(chevron, { rotation: open ? 180 : 0 });
111
+ return null;
112
+ }
113
+ const from = details.offsetHeight;
114
+ const content = [...details.children].filter((child) => child !== summary && child instanceof HTMLElement);
115
+ details.style.height = "";
116
+ details.open = false;
117
+ const closed = details.offsetHeight;
118
+ details.open = true;
119
+ const to = open ? details.offsetHeight : closed;
120
+ const timeline = gsap.timeline({
121
+ onComplete: () => {
122
+ if (!open) details.open = false;
123
+ gsap.set(details, { clearProps: "height,overflow,boxSizing" });
124
+ gsap.set(content, { clearProps: "opacity" });
125
+ running.delete(details);
126
+ }
127
+ });
128
+ if (from !== to) {
129
+ timeline.fromTo(details, { height: from, overflow: "hidden", boxSizing: "border-box" }, { height: to, duration, ease: EASE.signature }, 0);
130
+ }
131
+ timeline.fromTo(content, { opacity: open ? 0 : 1 }, { opacity: open ? 1 : 0, duration, ease: EASE.signature }, 0);
132
+ if (chevron) timeline.to(chevron, { rotation: open ? 180 : 0, duration: TIMING.chevron, ease: EASE.signature }, 0);
133
+ running.set(details, timeline);
134
+ return timeline;
135
+ }
136
+ function installDetailsMotion(root = document) {
137
+ const onClick = (event) => {
138
+ const mouse = event;
139
+ if (mouse.defaultPrevented || mouse.button !== 0) return;
140
+ const target = mouse.target instanceof Element ? mouse.target : null;
141
+ const summary = target?.closest("summary");
142
+ const details = summary?.parentElement;
143
+ if (!summary || !(details instanceof HTMLDetailsElement)) return;
144
+ if (details.querySelector(":scope > summary") !== summary) return;
145
+ if (details.dataset.mkMotion === "none") return;
146
+ const control = target?.closest("a, button, input, select, textarea");
147
+ if (control && control !== summary && summary.contains(control)) return;
148
+ mouse.preventDefault();
149
+ toggleDetails(details, !details.open);
150
+ };
151
+ root.addEventListener("click", onClick);
152
+ return () => root.removeEventListener("click", onClick);
153
+ }
154
+
155
+ // src/motion/indicator.ts
156
+ function slideIndicator(indicator, target, options = {}) {
157
+ registerMotion();
158
+ const { immediate = false, duration = TIMING.indicator, scroller, orientation = "horizontal" } = options;
159
+ gsap.killTweensOf(indicator);
160
+ if (!target) {
161
+ gsap.set(indicator, { opacity: 0 });
162
+ return null;
163
+ }
164
+ const box = { x: target.offsetLeft, y: target.offsetTop, width: target.offsetWidth, height: target.offsetHeight, opacity: 1 };
165
+ if (scroller) {
166
+ const horizontal = orientation === "horizontal";
167
+ const overflow = horizontal ? scroller.scrollWidth > scroller.clientWidth : scroller.scrollHeight > scroller.clientHeight;
168
+ if (overflow && typeof scroller.scrollTo === "function") {
169
+ const offset = horizontal ? box.x - (scroller.clientWidth - box.width) / 2 : box.y - (scroller.clientHeight - box.height) / 2;
170
+ scroller.scrollTo({ [horizontal ? "left" : "top"]: offset, behavior: immediate || reduced() ? "auto" : "smooth" });
171
+ }
172
+ }
173
+ if (immediate || reduced()) {
174
+ gsap.set(indicator, box);
175
+ return null;
176
+ }
177
+ return gsap.to(indicator, { ...box, duration, ease: EASE.signature });
178
+ }
179
+
180
+ // src/motion/layout.ts
181
+ function captureLayout(root, selector) {
182
+ registerMotion();
183
+ const items = [...root.querySelectorAll(selector)];
184
+ return { state: Flip.getState(items), ids: new Set(items.map((item) => item.dataset.flipId)) };
185
+ }
186
+ function reflowLayout(root, selector, captured, { duration = TIMING.layoutTravel } = {}) {
187
+ registerMotion();
188
+ if (reduced()) return null;
189
+ const targets = [...root.querySelectorAll(selector)];
190
+ return Flip.from(captured.state, {
191
+ targets,
192
+ duration,
193
+ ease: EASE.layoutReflow,
194
+ zIndex: 3,
195
+ onEnter: (elements) => gsap.fromTo(
196
+ elements,
197
+ { opacity: 0, scale: 0.94 },
198
+ { opacity: 1, scale: 1, duration: duration * TIMING.layoutEnterFactor, ease: EASE.layoutReflow, clearProps: "opacity,transform" }
199
+ )
200
+ });
201
+ }
202
+
203
+ // src/core/spring.ts
204
+ function createSpring(stiffness = 0.05, friction = 0.8) {
205
+ const current = { x: 0, y: 0 };
206
+ const target = { x: 0, y: 0 };
207
+ const velocity = { x: 0, y: 0 };
208
+ function update(dt = 1) {
209
+ const decay = Math.pow(friction, dt);
210
+ for (const axis of ["x", "y"]) {
211
+ velocity[axis] = velocity[axis] * decay + (target[axis] - current[axis]) * stiffness * dt;
212
+ current[axis] = Math.round((current[axis] + velocity[axis] * dt) * 100) / 100;
213
+ }
214
+ }
215
+ return { current, target, velocity, update };
216
+ }
217
+
218
+ // src/recipes/spring-cards/create-spring-cards.ts
219
+ var VARS = ["--mk-x", "--mk-y", "--mk-velocity"];
220
+ var rand = (min, max) => Math.random() * (max - min) + min;
221
+ function createSpringCards(root, options = {}) {
222
+ const o = {
223
+ selector: "[data-mk-card]",
224
+ initialOffset: 100,
225
+ releaseAfter: 100,
226
+ push: 0.4,
227
+ falloff: 6,
228
+ damping: 0.9,
229
+ stiffness: 0.05,
230
+ friction: 0.8,
231
+ entrance: true,
232
+ stagger: 0.08,
233
+ delay: 0.4,
234
+ ...options
235
+ };
236
+ const elements = root ? [...root.querySelectorAll(o.selector)] : [];
237
+ if (!root || !elements.length) {
238
+ warnOnce(`spring-cards: no "${o.selector}" inside the root`);
239
+ return inert();
240
+ }
241
+ registerMotion();
242
+ const scope = createScope();
243
+ const ctx = gsap.context(() => {
244
+ });
245
+ scope.add(() => ctx.revert());
246
+ if (o.entrance && entrances()) {
247
+ ctx.add(() => gsap.fromTo(elements, { scale: 0 }, { scale: 1, duration: 1, ease: ELASTIC, stagger: o.stagger, delay: o.delay }));
248
+ }
249
+ root.dataset.mkReady = "";
250
+ scope.add(() => {
251
+ delete root.dataset.mkReady;
252
+ elements.forEach((el) => VARS.forEach((name) => el.style.removeProperty(name)));
253
+ });
254
+ if (reduced()) return { destroy: () => scope.dispose() };
255
+ scope.add(bindPointer());
256
+ const cards = elements.map((el) => {
257
+ const spring = createSpring(o.stiffness, o.friction);
258
+ spring.current.x = spring.target.x = rand(-o.initialOffset, o.initialOffset);
259
+ spring.current.y = spring.target.y = rand(-o.initialOffset, o.initialOffset);
260
+ return { el, spring, push: { x: 0, y: 0 }, cx: 0, cy: 0 };
261
+ });
262
+ scope.timeout(() => cards.forEach(({ spring }) => spring.target.x = spring.target.y = 0), o.releaseAfter);
263
+ const measure = () => cards.forEach((card) => {
264
+ const rect = card.el.getBoundingClientRect();
265
+ card.cx = rect.left + scrollX + rect.width / 2;
266
+ card.cy = rect.top + scrollY + rect.height / 2;
267
+ });
268
+ measure();
269
+ scope.observe(new ResizeObserver(measure)).observe(root);
270
+ scope.add(
271
+ whileVisible(root, (dt) => {
272
+ const movement = takeMovement();
273
+ const mx = clamp(movement.mx, -80, 80);
274
+ const my = clamp(movement.my, -80, 80);
275
+ const decay = Math.pow(o.damping, dt);
276
+ for (const card of cards) {
277
+ const { spring, push } = card;
278
+ push.x *= decay;
279
+ push.y *= decay;
280
+ spring.current.x += push.x * dt;
281
+ spring.current.y += push.y * dt;
282
+ spring.update(dt);
283
+ if (pointer.active) {
284
+ const dx = (pointer.x + scrollX - card.cx) / innerWidth * 2;
285
+ const dy = (pointer.y + scrollY - card.cy) / innerHeight * 2;
286
+ const k = Math.pow(Math.max(0, 1 - Math.hypot(dx, dy)), o.falloff) * o.push;
287
+ push.x += mx * k;
288
+ push.y += my * k;
289
+ }
290
+ const style = card.el.style;
291
+ style.setProperty("--mk-x", spring.current.x.toFixed(2));
292
+ style.setProperty("--mk-y", spring.current.y.toFixed(2));
293
+ style.setProperty("--mk-velocity", (spring.velocity.x - spring.velocity.y).toFixed(3));
294
+ }
295
+ })
296
+ );
297
+ return { destroy: () => scope.dispose() };
298
+ }
299
+
300
+ // src/core/visible.ts
301
+ function thresholds(threshold) {
302
+ const steps = Array.from({ length: 11 }, (_, i) => i / 10);
303
+ return [.../* @__PURE__ */ new Set([...steps, threshold])].sort((a, b) => a - b);
304
+ }
305
+ function shows(entry, threshold) {
306
+ if (!entry.isIntersecting) return false;
307
+ if (entry.intersectionRatio >= threshold) return true;
308
+ return entry.rootBounds ? entry.intersectionRect.height >= entry.rootBounds.height * threshold : false;
309
+ }
310
+
311
+ // src/recipes/curved-carousel/carousel-math.ts
312
+ var FRICTION = 0.85;
313
+ function slideX(k, offset, step, length) {
314
+ return wrap(k * step - offset + step, length) - step;
315
+ }
316
+ var restingOffset = (offset, v, f = FRICTION) => offset - v * f / (1 - f);
317
+ function snapOffset(offset, v, step, center, f = FRICTION, dir = 0) {
318
+ const base = dir === 0 ? restingOffset(offset, v, f) : offset;
319
+ return (Math.round((base + center) / step) + dir) * step - center;
320
+ }
321
+
322
+ // src/recipes/curved-carousel/create-curved-carousel.ts
323
+ function createCurvedCarousel(root, options = {}) {
324
+ const o = {
325
+ slideSelector: "[data-mk-slide]",
326
+ friction: FRICTION,
327
+ throwBoost: 2,
328
+ wheel: 0.2,
329
+ tapDistance: 6,
330
+ startIndex: 0,
331
+ ...options
332
+ };
333
+ o.friction = Math.min(0.99, Math.max(0.5, o.friction));
334
+ const slides = root ? [...root.querySelectorAll(o.slideSelector)] : [];
335
+ const count = slides.length;
336
+ if (!root || !count) {
337
+ warnOnce(`curved-carousel: no "${o.slideSelector}" inside the root`);
338
+ return inert({ next() {
339
+ }, prev() {
340
+ }, goTo() {
341
+ }, index: 0 });
342
+ }
343
+ registerMotion();
344
+ const scope = createScope();
345
+ root.dataset.mkCarousel = "";
346
+ slides.forEach((slide, k) => slide.style.setProperty("--mk-k", String(k)));
347
+ scope.add(() => {
348
+ delete root.dataset.mkCarousel;
349
+ delete root.dataset.mkReady;
350
+ delete root.dataset.mkInview;
351
+ slides.forEach((slide) => ["--mk-k", "--mk-x", "--mk-t"].forEach((name) => slide.style.removeProperty(name)));
352
+ });
353
+ let rootW = 0;
354
+ let slideW = 0;
355
+ let step = 1;
356
+ let length = count;
357
+ let center = 0;
358
+ let offset = 0;
359
+ let v = 0;
360
+ let target = null;
361
+ let measured = false;
362
+ let down = false;
363
+ let dragDelta = 0;
364
+ let lastX = 0;
365
+ let travelled = 0;
366
+ let suppressClick = false;
367
+ let wheelTimer;
368
+ scope.add(() => clearTimeout(wheelTimer));
369
+ const render = () => {
370
+ for (let k = 0; k < count; k++) {
371
+ const x = slideX(k, offset, step, length);
372
+ const style = slides[k].style;
373
+ style.setProperty("--mk-x", x.toFixed(2));
374
+ style.setProperty("--mk-t", (x + slideW / 2 - rootW / 2).toFixed(2));
375
+ }
376
+ };
377
+ const centred = (k) => {
378
+ const base = k * step - center;
379
+ return base + Math.round((offset - base) / length) * length;
380
+ };
381
+ const settleTo = (next) => {
382
+ target = next;
383
+ if (!reduced()) return;
384
+ offset = next;
385
+ target = null;
386
+ v = 0;
387
+ render();
388
+ };
389
+ const measure = () => {
390
+ const previousCenter = center;
391
+ rootW = root.clientWidth;
392
+ slideW = slides[0].offsetWidth;
393
+ const gap = parseFloat(getComputedStyle(root).columnGap) || 16;
394
+ const index = measured ? (offset + previousCenter) / step : o.startIndex;
395
+ step = slideW + gap || 1;
396
+ length = count * step;
397
+ center = (rootW - slideW) / 2;
398
+ offset = index * step - center;
399
+ if (target !== null) target = Math.round((target + previousCenter) / step) * step - center;
400
+ measured = true;
401
+ render();
402
+ };
403
+ const tick = (dt) => {
404
+ if (down) {
405
+ v = dragDelta;
406
+ dragDelta = 0;
407
+ offset -= v;
408
+ } else if (target !== null) {
409
+ const next = offset + (target - offset) * (1 - Math.pow(o.friction, dt));
410
+ v = offset - next;
411
+ offset = next;
412
+ if (Math.abs(target - offset) < 0.1) {
413
+ offset = target;
414
+ target = null;
415
+ v = 0;
416
+ }
417
+ } else if (Math.abs(v) > 0.01) {
418
+ v *= Math.pow(o.friction, dt);
419
+ offset -= v * dt;
420
+ } else {
421
+ return;
422
+ }
423
+ render();
424
+ };
425
+ scope.on(root, "pointerdown", (e) => {
426
+ if (e.button !== 0) return;
427
+ if (e.pointerType === "mouse") e.preventDefault();
428
+ down = true;
429
+ target = null;
430
+ dragDelta = 0;
431
+ travelled = 0;
432
+ lastX = e.clientX;
433
+ });
434
+ scope.on(
435
+ window,
436
+ "pointermove",
437
+ (e) => {
438
+ if (!down) return;
439
+ const dx = e.clientX - lastX;
440
+ lastX = e.clientX;
441
+ dragDelta += dx;
442
+ travelled += Math.abs(dx);
443
+ },
444
+ { passive: true }
445
+ );
446
+ const release = (cancelled) => {
447
+ if (!down) return;
448
+ down = false;
449
+ suppressClick = travelled > o.tapDistance;
450
+ const throwV = cancelled || reduced() ? 0 : v * o.throwBoost;
451
+ settleTo(snapOffset(offset, throwV, step, center, o.friction));
452
+ };
453
+ scope.on(window, "pointerup", () => release(false));
454
+ scope.on(window, "pointercancel", () => release(true));
455
+ scope.on(
456
+ root,
457
+ "click",
458
+ (e) => {
459
+ if (!suppressClick) return;
460
+ suppressClick = false;
461
+ e.preventDefault();
462
+ e.stopPropagation();
463
+ },
464
+ true
465
+ );
466
+ scope.on(root, "click", (e) => {
467
+ const slide = e.target.closest(o.slideSelector);
468
+ if (slide && root.contains(slide)) o.onSelect?.(slides.indexOf(slide), slide);
469
+ });
470
+ scope.on(
471
+ root,
472
+ "wheel",
473
+ (e) => {
474
+ if (Math.abs(e.deltaX) <= Math.abs(e.deltaY)) return;
475
+ e.preventDefault();
476
+ target = null;
477
+ if (reduced()) {
478
+ offset += e.deltaX;
479
+ render();
480
+ } else {
481
+ v -= e.deltaX * o.wheel;
482
+ }
483
+ clearTimeout(wheelTimer);
484
+ wheelTimer = setTimeout(() => settleTo(snapOffset(offset, v, step, center, o.friction)), 150);
485
+ },
486
+ { passive: false }
487
+ );
488
+ const stepBy = (dir) => settleTo(snapOffset(target ?? offset, 0, step, center, o.friction, dir));
489
+ scope.on(root, "keydown", (e) => {
490
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
491
+ e.preventDefault();
492
+ stepBy(e.key === "ArrowLeft" ? -1 : 1);
493
+ });
494
+ scope.on(root, "focusin", (e) => {
495
+ const slide = e.target.closest(o.slideSelector);
496
+ if (!slide || down) return;
497
+ settleTo(centred(slides.indexOf(slide)));
498
+ });
499
+ scope.observe(new ResizeObserver(measure)).observe(root);
500
+ scope.observe(
501
+ new IntersectionObserver(
502
+ ([entry]) => {
503
+ if (entry && shows(entry, 0.2)) root.dataset.mkInview = "";
504
+ },
505
+ { threshold: thresholds(0.2) }
506
+ )
507
+ ).observe(root);
508
+ scope.add(whileVisible(root, tick));
509
+ measure();
510
+ root.dataset.mkReady = "";
511
+ return {
512
+ destroy: () => scope.dispose(),
513
+ next: () => stepBy(1),
514
+ prev: () => stepBy(-1),
515
+ goTo: (index) => settleTo(centred(wrap(Math.round(index), count))),
516
+ get index() {
517
+ return wrap(Math.round(((target ?? offset) + center) / step), count);
518
+ }
519
+ };
520
+ }
521
+
522
+ // src/recipes/pile/pile-math.ts
523
+ function mulberry32(seed) {
524
+ let a = seed >>> 0;
525
+ return () => {
526
+ a = a + 1831565813 >>> 0;
527
+ let t = a;
528
+ t = Math.imul(t ^ t >>> 15, t | 1);
529
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
530
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
531
+ };
532
+ }
533
+ function pileLayers(count, seed) {
534
+ const rand2 = mulberry32(seed + 1);
535
+ const range = (min, max) => rand2() * (max - min) + min;
536
+ return Array.from({ length: count }, (_, i) => ({
537
+ rotation: range(2, 5) * (i % 2 * 2 - 1),
538
+ offsetX: range(4, 10) * (rand2() < 0.5 ? -1 : 1),
539
+ offsetY: (count - i) * 5
540
+ }));
541
+ }
542
+
543
+ // src/recipes/pile/create-pile.ts
544
+ var pileStyle = (layer) => `--mk-rot:${layer.rotation.toFixed(2)};--mk-px:${layer.offsetX.toFixed(2)};--mk-py:${layer.offsetY}`;
545
+ function createPile(root, options = {}) {
546
+ if (!root) {
547
+ warnOnce("pile: missing root");
548
+ return inert();
549
+ }
550
+ const scope = createScope();
551
+ const threshold = Math.min(1, Math.max(0, options.threshold ?? 0.25));
552
+ const io = scope.observe(
553
+ new IntersectionObserver(
554
+ ([entry]) => {
555
+ if (!entry || !shows(entry, threshold)) return;
556
+ root.dataset.mkInview = "";
557
+ io.disconnect();
558
+ },
559
+ { threshold: thresholds(threshold) }
560
+ )
561
+ );
562
+ io.observe(root);
563
+ root.dataset.mkReady = "";
564
+ scope.add(() => {
565
+ delete root.dataset.mkInview;
566
+ delete root.dataset.mkReady;
567
+ });
568
+ return { destroy: () => scope.dispose() };
569
+ }
570
+
571
+ // src/recipes/words/create-words.ts
572
+ var HIDDEN = { scaleY: 0, yPercent: 25 };
573
+ function createWords(el, options = {}) {
574
+ if (!el) {
575
+ warnOnce("words: missing element");
576
+ return inert({ reveal() {
577
+ }, replace(mutate) {
578
+ mutate();
579
+ } });
580
+ }
581
+ const letters2 = options.by === "chars";
582
+ const o = {
583
+ trigger: "scroll",
584
+ start: "top 88%",
585
+ duration: 0.8,
586
+ stagger: letters2 ? 0.06 : 0.1,
587
+ delay: 0,
588
+ ...options
589
+ };
590
+ registerMotion();
591
+ let split = null;
592
+ let scroll = null;
593
+ let destroyed = false;
594
+ const units = () => split ? letters2 ? split.chars : split.words : [];
595
+ const rendered = () => el.getClientRects().length > 0;
596
+ const prepare = () => {
597
+ split = SplitText.create(el, {
598
+ type: letters2 ? "chars" : "words",
599
+ charsClass: "mk-w",
600
+ wordsClass: "mk-w",
601
+ // Headings get an aria-label; inline elements stay readable as they are.
602
+ aria: /^H[1-6]$/.test(el.tagName) ? "auto" : "none",
603
+ smartWrap: letters2
604
+ });
605
+ const list = units();
606
+ if (list.length) gsap.set(list, { transformOrigin: "50% 100%", ...HIDDEN });
607
+ };
608
+ const release = () => {
609
+ if (!split) return;
610
+ gsap.killTweensOf(units());
611
+ split.revert();
612
+ split = null;
613
+ };
614
+ function reveal(delay = o.delay) {
615
+ if (destroyed) return;
616
+ scroll?.kill();
617
+ scroll = null;
618
+ if (!entrances()) return;
619
+ if (!split) prepare();
620
+ const list = units();
621
+ if (!list.length) return;
622
+ gsap.killTweensOf(list);
623
+ gsap.fromTo(list, HIDDEN, { scaleY: 1, yPercent: 0, duration: o.duration, ease: ELASTIC, stagger: o.stagger, delay });
624
+ }
625
+ function replace(mutate) {
626
+ release();
627
+ mutate();
628
+ if (entrances() && rendered()) {
629
+ prepare();
630
+ reveal(0);
631
+ }
632
+ }
633
+ if (entrances() && rendered()) prepare();
634
+ el.dataset.mkReady = "";
635
+ if (o.trigger === "load") reveal(o.delay || 0.2);
636
+ else if (o.trigger === "scroll" && entrances()) {
637
+ scroll = ScrollTrigger.create({ trigger: el, start: o.start, end: "max", once: true, onEnter: () => reveal() });
638
+ }
639
+ return {
640
+ reveal,
641
+ replace,
642
+ destroy() {
643
+ if (destroyed) return;
644
+ destroyed = true;
645
+ scroll?.kill();
646
+ release();
647
+ delete el.dataset.mkReady;
648
+ }
649
+ };
650
+ }
651
+
652
+ // src/recipes/flow/create-flow.ts
653
+ function createFlow(container, options = {}) {
654
+ if (!container) {
655
+ warnOnce("flow: missing container");
656
+ return inert({ play() {
657
+ } });
658
+ }
659
+ registerMotion();
660
+ const children = [...container.children];
661
+ const animate = entrances() && children.length > 0;
662
+ const ctx = gsap.context(() => {
663
+ });
664
+ let played = !animate;
665
+ const play = () => {
666
+ if (played) return;
667
+ played = true;
668
+ ctx.add(() => flowIn(children, options));
669
+ };
670
+ if (animate) {
671
+ ctx.add(() => {
672
+ gsap.set(children, { autoAlpha: 0 });
673
+ if (options.trigger === "load") play();
674
+ else if (options.trigger !== "manual") {
675
+ ScrollTrigger.create({ trigger: container, start: options.start ?? "top 88%", end: "max", once: true, onEnter: play });
676
+ }
677
+ });
678
+ }
679
+ container.dataset.mkReady = "";
680
+ return {
681
+ play,
682
+ destroy() {
683
+ ctx.revert();
684
+ delete container.dataset.mkReady;
685
+ }
686
+ };
687
+ }
688
+
689
+ // src/recipes/pop/create-pop.ts
690
+ function createPop(el, options = {}) {
691
+ if (!el) {
692
+ warnOnce("pop: missing element");
693
+ return inert({ play() {
694
+ } });
695
+ }
696
+ registerMotion();
697
+ const { scale = 0.7, duration = 1.1, delay = 0, trigger = "scroll", start = "top 85%" } = options;
698
+ const animate = entrances();
699
+ const ctx = gsap.context(() => {
700
+ });
701
+ let played = !animate;
702
+ const play = () => {
703
+ if (played) return;
704
+ played = true;
705
+ ctx.add(() => gsap.to(el, { scale: 1, autoAlpha: 1, duration, delay, ease: ELASTIC }));
706
+ };
707
+ if (animate) {
708
+ ctx.add(() => {
709
+ gsap.set(el, { scale, autoAlpha: 0 });
710
+ if (trigger === "load") play();
711
+ else if (trigger !== "manual") ScrollTrigger.create({ trigger: el, start, end: "max", once: true, onEnter: play });
712
+ });
713
+ }
714
+ el.dataset.mkReady = "";
715
+ return {
716
+ play,
717
+ destroy() {
718
+ ctx.revert();
719
+ delete el.dataset.mkReady;
720
+ }
721
+ };
722
+ }
723
+
724
+ // src/recipes/fit-text/create-fit-text.ts
725
+ function createFitText(el, options = {}) {
726
+ const parent = el?.parentElement;
727
+ if (!el || !parent) {
728
+ warnOnce("fit-text: element needs a parent");
729
+ return inert({ refit() {
730
+ } });
731
+ }
732
+ const safety = Math.min(1, Math.max(0.5, options.safety ?? 0.995));
733
+ const scope = createScope();
734
+ const previousSize = el.style.fontSize;
735
+ const refit = () => {
736
+ const style = getComputedStyle(parent);
737
+ const available = parent.clientWidth - parseFloat(style.paddingLeft || "0") - parseFloat(style.paddingRight || "0");
738
+ el.style.fontSize = "100px";
739
+ const width = el.getBoundingClientRect().width;
740
+ if (width > 0 && available > 0) el.style.fontSize = `${100 * available * safety / width}px`;
741
+ else el.style.fontSize = previousSize;
742
+ };
743
+ let lastWidth = -1;
744
+ scope.observe(
745
+ new ResizeObserver(([entry]) => {
746
+ const width = entry?.contentRect.width ?? 0;
747
+ if (width === lastWidth) return;
748
+ lastWidth = width;
749
+ refit();
750
+ })
751
+ ).observe(parent);
752
+ refit();
753
+ el.dataset.mkReady = "";
754
+ scope.add(() => {
755
+ el.style.fontSize = previousSize;
756
+ if (!el.getAttribute("style")) el.removeAttribute("style");
757
+ delete el.dataset.mkReady;
758
+ });
759
+ return { refit, destroy: () => scope.dispose() };
760
+ }
761
+
762
+ // src/recipes/stretch-footer/create-stretch-footer.ts
763
+ function createStretchFooter(footer, options = {}) {
764
+ const phantom = options.phantom ?? document.querySelector("[data-mk-phantom]");
765
+ const title = options.title ?? footer?.querySelector("[data-mk-footer-title]") ?? null;
766
+ if (!footer || !phantom || !title) {
767
+ warnOnce("stretch-footer: needs the footer, a [data-mk-phantom] spacer and a [data-mk-footer-title]");
768
+ return inert();
769
+ }
770
+ registerMotion();
771
+ const scope = createScope();
772
+ footer.dataset.mkReady = "";
773
+ scope.add(() => {
774
+ delete footer.dataset.mkReady;
775
+ footer.style.removeProperty("--mk-footer-scale");
776
+ footer.style.removeProperty("--mk-footer-offset");
777
+ if (!footer.getAttribute("style")) footer.removeAttribute("style");
778
+ });
779
+ if (reduced()) return { destroy: () => scope.dispose() };
780
+ const lambda = options.lambda ?? 0.9;
781
+ let phantomTop = 0;
782
+ let titleBottom = 0;
783
+ let viewport = innerHeight;
784
+ let g = scrollY;
785
+ const measure = () => {
786
+ phantomTop = phantom.offsetTop;
787
+ titleBottom = title.offsetTop + title.offsetHeight;
788
+ viewport = innerHeight;
789
+ };
790
+ measure();
791
+ scope.observe(new ResizeObserver(measure)).observe(document.body);
792
+ scope.on(window, "resize", measure);
793
+ const update = (dt) => {
794
+ g = damp(g, scrollY, lambda, dt);
795
+ const start = phantomTop - viewport;
796
+ footer.style.setProperty("--mk-footer-scale", mapRange(g, start, phantomTop, 0, 1, true).toFixed(4));
797
+ footer.style.setProperty("--mk-footer-offset", mapRange(g, start, phantomTop, viewport - titleBottom, 0, true).toFixed(2));
798
+ };
799
+ update(100);
800
+ scope.add(whileVisible(phantom, update, "0px"));
801
+ return { destroy: () => scope.dispose() };
802
+ }
803
+
804
+ // src/recipes/radial-menu/create-radial-menu.ts
805
+ var noop = { open() {
806
+ }, close() {
807
+ }, toggle() {
808
+ }, isOpen: false };
809
+ function createRadialMenu(root, options = {}) {
810
+ const toggleEl = root?.querySelector("[data-mk-toggle]");
811
+ const items = root ? [...root.querySelectorAll("[data-mk-item]")] : [];
812
+ if (!root || !toggleEl || !items.length) {
813
+ warnOnce("radial-menu: needs a [data-mk-toggle] and at least one [data-mk-item]");
814
+ return inert(noop);
815
+ }
816
+ const scope = createScope();
817
+ const expandedBefore = toggleEl.getAttribute("aria-expanded");
818
+ const inertBefore = items.map((item) => item.hasAttribute("inert"));
819
+ const vars = [
820
+ ["--mk-radius", options.radius],
821
+ ["--mk-start", options.startAngle?.toString()],
822
+ ["--mk-spacing", options.spacing?.toString()]
823
+ ];
824
+ vars.forEach(([name, value]) => value !== void 0 && root.style.setProperty(name, value));
825
+ items.forEach((item, i) => item.style.setProperty("--mk-i", String(i)));
826
+ let open = false;
827
+ const set = (next) => {
828
+ open = next;
829
+ root.toggleAttribute("data-mk-open", open);
830
+ toggleEl.setAttribute("aria-expanded", String(open));
831
+ items.forEach((item) => item.inert = !open);
832
+ options.onToggle?.(open);
833
+ };
834
+ set(false);
835
+ scope.on(toggleEl, "click", () => set(!open));
836
+ scope.on(root, "keydown", (e) => {
837
+ if (e.key !== "Escape" || !open) return;
838
+ set(false);
839
+ toggleEl.focus();
840
+ });
841
+ if (options.closeOnOutside ?? true) {
842
+ scope.on(document, "pointerdown", (e) => {
843
+ if (open && !root.contains(e.target)) set(false);
844
+ });
845
+ }
846
+ root.dataset.mkReady = "";
847
+ scope.add(() => {
848
+ root.removeAttribute("data-mk-open");
849
+ delete root.dataset.mkReady;
850
+ if (expandedBefore === null) toggleEl.removeAttribute("aria-expanded");
851
+ else toggleEl.setAttribute("aria-expanded", expandedBefore);
852
+ items.forEach((item, i) => {
853
+ item.inert = inertBefore[i];
854
+ item.style.removeProperty("--mk-i");
855
+ if (!item.getAttribute("style")) item.removeAttribute("style");
856
+ });
857
+ vars.forEach(([name]) => root.style.removeProperty(name));
858
+ if (!root.getAttribute("style")) root.removeAttribute("style");
859
+ });
860
+ return {
861
+ open: () => set(true),
862
+ close: () => set(false),
863
+ toggle: () => set(!open),
864
+ get isOpen() {
865
+ return open;
866
+ },
867
+ destroy: () => scope.dispose()
868
+ };
869
+ }
870
+
871
+ // src/recipes/tabs/create-tabs.ts
872
+ function createTabs(root, options = {}) {
873
+ const tabs = root ? [...root.querySelectorAll("[data-mk-tab]")] : [];
874
+ if (!root || !tabs.length) {
875
+ warnOnce("tabs: needs at least one [data-mk-tab]");
876
+ return inert({ select() {
877
+ }, index: 0 });
878
+ }
879
+ registerMotion();
880
+ const { orientation = "horizontal", duration = TIMING.indicator } = options;
881
+ const indicator = root.querySelector("[data-mk-indicator]");
882
+ const scope = createScope();
883
+ const ctx = gsap.context(() => {
884
+ });
885
+ let index = Math.max(0, tabs.findIndex((tab) => tab.getAttribute("aria-selected") === "true"));
886
+ scope.attr(root, "role", "tablist");
887
+ if (orientation === "vertical") scope.attr(root, "aria-orientation", "vertical");
888
+ const render = (immediate) => {
889
+ tabs.forEach((tab, i) => {
890
+ scope.attr(tab, "role", "tab");
891
+ scope.attr(tab, "aria-selected", String(i === index));
892
+ scope.attr(tab, "tabindex", i === index ? "0" : "-1");
893
+ });
894
+ if (indicator) ctx.add(() => slideIndicator(indicator, tabs[index], { immediate, duration, scroller: root, orientation }));
895
+ };
896
+ const select = (next, focus = false) => {
897
+ const target = Math.min(tabs.length - 1, Math.max(0, next));
898
+ if (focus) tabs[target].focus();
899
+ if (target === index) return;
900
+ index = target;
901
+ render(false);
902
+ root.dispatchEvent(new CustomEvent("mk:tab", { detail: { index, tab: tabs[index] }, bubbles: true }));
903
+ options.onSelect?.(index, tabs[index]);
904
+ };
905
+ scope.on(root, "click", (event) => {
906
+ const tab = event.target?.closest("[data-mk-tab]");
907
+ const at = tab ? tabs.indexOf(tab) : -1;
908
+ if (at >= 0) select(at);
909
+ });
910
+ scope.on(root, "keydown", (event) => {
911
+ const key = event.key;
912
+ const back = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
913
+ const forward = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
914
+ const moves = { [back]: index - 1, [forward]: index + 1, Home: 0, End: tabs.length - 1 };
915
+ if (!(key in moves)) return;
916
+ event.preventDefault();
917
+ select(moves[key], true);
918
+ });
919
+ if (typeof ResizeObserver === "function") {
920
+ scope.observe(new ResizeObserver(() => render(true))).observe(root);
921
+ }
922
+ render(true);
923
+ root.dataset.mkReady = "";
924
+ return {
925
+ select: (next) => select(next),
926
+ get index() {
927
+ return index;
928
+ },
929
+ destroy() {
930
+ scope.dispose();
931
+ ctx.revert();
932
+ delete root.dataset.mkReady;
933
+ }
934
+ };
935
+ }
936
+
937
+ // src/recipes/counter/create-counter.ts
938
+ function createCounter(el, options = {}) {
939
+ if (!el) {
940
+ warnOnce("counter: missing element");
941
+ return inert({ play() {
942
+ }, set() {
943
+ } });
944
+ }
945
+ registerMotion();
946
+ const original = el.textContent ?? "";
947
+ const parsed = Number(original.replace(/[^\d.-]/g, ""));
948
+ const {
949
+ to = Number.isFinite(parsed) ? parsed : 0,
950
+ from = 0,
951
+ duration = TIMING.number,
952
+ decimals = 0,
953
+ locale,
954
+ prefix = "",
955
+ suffix = "",
956
+ trigger = "scroll",
957
+ start = "top 90%"
958
+ } = options;
959
+ const format = (value) => prefix + value.toLocaleString(locale, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }) + suffix;
960
+ const ctx = gsap.context(() => {
961
+ });
962
+ let played = false;
963
+ const play = () => {
964
+ if (played) return;
965
+ played = true;
966
+ ctx.add(() => countTo(el, to, { from, duration, format }));
967
+ };
968
+ if (entrances()) {
969
+ el.textContent = format(from);
970
+ ctx.add(() => {
971
+ if (trigger === "load") play();
972
+ else if (trigger !== "manual") ScrollTrigger.create({ trigger: el, start, end: "max", once: true, onEnter: play });
973
+ });
974
+ } else {
975
+ played = true;
976
+ el.textContent = format(to);
977
+ }
978
+ el.dataset.mkReady = "";
979
+ return {
980
+ play,
981
+ set(value) {
982
+ played = true;
983
+ ctx.add(() => countTo(el, value, { duration, format }));
984
+ },
985
+ destroy() {
986
+ ctx.revert();
987
+ el.textContent = original;
988
+ delete el.dataset.mkReady;
989
+ }
990
+ };
991
+ }
992
+
993
+ // src/recipes/details/create-details.ts
994
+ function createDetails(root) {
995
+ if (!root) {
996
+ warnOnce("details: missing root");
997
+ return inert();
998
+ }
999
+ const uninstall = installDetailsMotion(root);
1000
+ root.dataset.mkReady = "";
1001
+ let destroyed = false;
1002
+ return {
1003
+ destroy() {
1004
+ if (destroyed) return;
1005
+ destroyed = true;
1006
+ uninstall();
1007
+ delete root.dataset.mkReady;
1008
+ }
1009
+ };
1010
+ }
1011
+
1012
+ // src/recipes/reveal/create-reveal.ts
1013
+ function createReveal(el, options = {}) {
1014
+ if (!el) {
1015
+ warnOnce("reveal: missing element");
1016
+ return inert({ play() {
1017
+ } });
1018
+ }
1019
+ const { speed = "normal", delay = 0, trigger = "manual" } = options;
1020
+ let handle = null;
1021
+ const play = (origin2) => {
1022
+ handle?.cancel();
1023
+ const from = origin2 ?? (options.origin ? document.querySelector(options.origin) : void 0);
1024
+ handle = revealFrom(el, { speed, delay, origin: from });
1025
+ };
1026
+ if (trigger === "load" && entrances()) play();
1027
+ el.dataset.mkReady = "";
1028
+ return {
1029
+ play,
1030
+ destroy() {
1031
+ handle?.cancel();
1032
+ handle = null;
1033
+ delete el.dataset.mkReady;
1034
+ }
1035
+ };
1036
+ }
1037
+
1038
+ // src/recipes/reflow/create-reflow.ts
1039
+ function createReflow(root, options = {}) {
1040
+ if (!root) {
1041
+ warnOnce("reflow: missing root");
1042
+ return inert({ capture() {
1043
+ }, play() {
1044
+ } });
1045
+ }
1046
+ registerMotion();
1047
+ const { selector = "[data-mk-item]", duration = TIMING.layoutTravel } = options;
1048
+ const ctx = gsap.context(() => {
1049
+ });
1050
+ let captured = null;
1051
+ root.dataset.mkReady = "";
1052
+ return {
1053
+ capture() {
1054
+ captured = captureLayout(root, selector);
1055
+ },
1056
+ play() {
1057
+ if (!captured) return;
1058
+ const state = captured;
1059
+ captured = null;
1060
+ ctx.add(() => reflowLayout(root, selector, state, { duration }));
1061
+ },
1062
+ destroy() {
1063
+ ctx.revert();
1064
+ captured = null;
1065
+ delete root.dataset.mkReady;
1066
+ }
1067
+ };
1068
+ }
1069
+
1070
+ // src/recipes/slot-text/create-slot-text.ts
1071
+ var EASING = "cubic-bezier(0.34, 1.56, 0.64, 1)";
1072
+ var RESIZE_EASING = "cubic-bezier(0.2, 0, 0, 1)";
1073
+ var BOUNCE = 0.6;
1074
+ var VARIATION = { index: 12.9898, channel: 78.233, seed: 43758.5453, duration: 1, stagger: 2, tilt: 3 };
1075
+ var GLYPH = { tailDuration: 0.75, durationStrength: 0.45, tailStaggerStart: 0.5, tailStaggerStep: 0.25, staggerStrength: 0.25 };
1076
+ var WIDTH = { tailCollapseDelay: 0.55, tailCollapseDuration: 0.6, newSlotExpansion: 0.45, minimumChange: 0.5 };
1077
+ var COMPLETION_BUFFER = 80;
1078
+ var NBSP = "\xA0";
1079
+ var glyph = (letter) => letter === " " ? NBSP : letter;
1080
+ var letters = (text) => splitGraphemes(text, Number.MAX_SAFE_INTEGER);
1081
+ var noise = (index, channel) => {
1082
+ const value = Math.sin((index + 1) * VARIATION.index + channel * VARIATION.channel) * VARIATION.seed;
1083
+ return (value - Math.floor(value)) * 2 - 1;
1084
+ };
1085
+ var face = (letter) => {
1086
+ const element = document.createElement("span");
1087
+ element.className = "mk-slot__face";
1088
+ element.textContent = glyph(letter);
1089
+ return element;
1090
+ };
1091
+ var slot = (letter) => {
1092
+ const element = document.createElement("span");
1093
+ element.className = "mk-slot__char";
1094
+ element.dataset.char = letter;
1095
+ const sizer = document.createElement("span");
1096
+ sizer.className = "mk-slot__sizer";
1097
+ sizer.textContent = glyph(letter);
1098
+ element.append(sizer, face(letter));
1099
+ return element;
1100
+ };
1101
+ function build(target, text) {
1102
+ const nodes = [];
1103
+ let word = null;
1104
+ for (const letter of letters(text)) {
1105
+ if (letter === " ") {
1106
+ if (word) word.append(slot(letter));
1107
+ else nodes.push(slot(letter));
1108
+ word = null;
1109
+ continue;
1110
+ }
1111
+ if (!word) {
1112
+ word = document.createElement("span");
1113
+ word.className = "mk-slot__word";
1114
+ nodes.push(word);
1115
+ }
1116
+ word.append(slot(letter));
1117
+ }
1118
+ target.replaceChildren(...nodes);
1119
+ }
1120
+ function createSlotText(el, options = {}) {
1121
+ if (!el) {
1122
+ warnOnce("slot-text: missing element");
1123
+ return inert({ set() {
1124
+ }, text: "" });
1125
+ }
1126
+ const {
1127
+ direction = "down",
1128
+ duration: baseDuration = TIMING.slot.durationMs,
1129
+ stagger = TIMING.slot.staggerMs,
1130
+ exitOffset = TIMING.slot.exitOffsetMs,
1131
+ colorFade = TIMING.slot.colorFadeMs,
1132
+ maxTilt = TIMING.slot.maxTilt
1133
+ } = options;
1134
+ const original = [...el.childNodes];
1135
+ const originalText = el.textContent ?? "";
1136
+ let text = options.text ?? originalText;
1137
+ let timers = [];
1138
+ let destroyed = false;
1139
+ const copy = document.createElement("span");
1140
+ copy.className = "mk-sr-only";
1141
+ copy.textContent = text;
1142
+ const track = document.createElement("span");
1143
+ track.className = "mk-slot";
1144
+ track.setAttribute("aria-hidden", "true");
1145
+ build(track, text);
1146
+ if (options.accessibleCopy === false) {
1147
+ track.removeAttribute("aria-hidden");
1148
+ el.replaceChildren(track);
1149
+ } else el.replaceChildren(copy, track);
1150
+ el.dataset.mkReady = "";
1151
+ const stop = () => {
1152
+ for (const timer of timers) clearTimeout(timer);
1153
+ timers = [];
1154
+ };
1155
+ const roll = (next) => {
1156
+ const interrupted = timers.length > 0;
1157
+ stop();
1158
+ if (interrupted) build(track, text);
1159
+ let slots = [...track.querySelectorAll(".mk-slot__char")];
1160
+ if (!slots.length || reduced()) {
1161
+ build(track, next);
1162
+ return;
1163
+ }
1164
+ const current = slots.map((entry) => entry.dataset.char ?? "");
1165
+ const target = letters(next);
1166
+ for (let index = slots.length; index < Math.max(current.length, target.length); index += 1) {
1167
+ const extra = slot("");
1168
+ track.append(extra);
1169
+ slots = [...slots, extra];
1170
+ }
1171
+ const firstFilled = slots.find((entry) => (entry.dataset.char ?? "") !== "") ?? slots[0];
1172
+ const height = Math.ceil(firstFilled?.getBoundingClientRect().height ?? 0) || 18;
1173
+ const changes = slots.map((entry, index) => ({
1174
+ index,
1175
+ slot: entry,
1176
+ sizer: entry.querySelector(".mk-slot__sizer"),
1177
+ previous: entry.querySelector(".mk-slot__face"),
1178
+ from: current[index] ?? "",
1179
+ to: target[index] ?? "",
1180
+ width: entry.getBoundingClientRect().width
1181
+ })).filter(({ from, to }) => from !== to);
1182
+ if (!changes.length) {
1183
+ build(track, next);
1184
+ return;
1185
+ }
1186
+ for (const change of changes) if (change.sizer) change.sizer.textContent = glyph(change.to);
1187
+ const measured = changes.map((change) => {
1188
+ const width = change.sizer?.getBoundingClientRect().width ?? change.width;
1189
+ return { ...change, targetWidth: width, resize: Math.abs(width - change.width) > WIDTH.minimumChange };
1190
+ });
1191
+ const later = (run, ms) => timers.push(window.setTimeout(run, ms));
1192
+ const enterFrom = direction === "down" ? -height : height;
1193
+ const exitTo = -enterFrom;
1194
+ let completion = 0;
1195
+ const planned = measured.map((change) => {
1196
+ const tail = change.to === "";
1197
+ const vary = (strength, channel) => 1 + BOUNCE * strength * noise(change.index, channel);
1198
+ const duration = Math.round(baseDuration * (tail ? GLYPH.tailDuration : 1) * vary(GLYPH.durationStrength, VARIATION.duration));
1199
+ const order = tail ? target.length * GLYPH.tailStaggerStart + (change.index - target.length) * GLYPH.tailStaggerStep : change.index;
1200
+ const delay = Math.round(order * stagger * vary(GLYPH.staggerStrength, VARIATION.stagger));
1201
+ const tilt = Number((BOUNCE * maxTilt * noise(change.index, VARIATION.tilt)).toFixed(2));
1202
+ const minimum = (factor) => Math.max(TIMING.slot.minResizeMs, Math.round(duration * factor));
1203
+ const widthTiming = change.to === "" ? { delay: delay + Math.round(duration * WIDTH.tailCollapseDelay), duration: minimum(WIDTH.tailCollapseDuration) } : change.from === "" ? { delay, duration: minimum(WIDTH.newSlotExpansion) } : { delay, duration };
1204
+ if (change.resize) change.slot.style.width = `${change.width}px`;
1205
+ if (change.from === "" || change.to === "") change.slot.classList.add("is-resizing");
1206
+ const incoming = face(change.to);
1207
+ incoming.style.transform = `translateY(${enterFrom}px) rotate(${tilt}deg)`;
1208
+ incoming.style.opacity = "0";
1209
+ change.slot.append(incoming);
1210
+ completion = Math.max(completion, change.resize ? widthTiming.delay + widthTiming.duration : 0, delay + exitOffset + duration);
1211
+ return { change, duration, delay, tilt, widthTiming, incoming };
1212
+ });
1213
+ void track.offsetWidth;
1214
+ for (const { change, duration, delay, tilt, widthTiming, incoming } of planned) {
1215
+ const transition = `transform ${duration}ms ${EASING}, opacity ${colorFade}ms linear`;
1216
+ if (change.resize) {
1217
+ later(() => {
1218
+ change.slot.style.transition = `width ${widthTiming.duration}ms ${RESIZE_EASING}`;
1219
+ change.slot.style.width = `${change.targetWidth}px`;
1220
+ }, widthTiming.delay);
1221
+ }
1222
+ if (change.previous) {
1223
+ const previous = change.previous;
1224
+ later(() => {
1225
+ previous.style.transition = transition;
1226
+ previous.style.transform = `translateY(${exitTo}px) rotate(${-tilt}deg)`;
1227
+ previous.style.opacity = "0";
1228
+ }, delay);
1229
+ }
1230
+ later(() => {
1231
+ incoming.style.transition = transition;
1232
+ incoming.style.transform = "translateY(0) rotate(0deg)";
1233
+ incoming.style.opacity = "1";
1234
+ }, delay + exitOffset);
1235
+ }
1236
+ later(() => {
1237
+ timers = [];
1238
+ build(track, next);
1239
+ }, completion + COMPLETION_BUFFER);
1240
+ };
1241
+ return {
1242
+ set(next) {
1243
+ if (destroyed || next === text) return;
1244
+ roll(next);
1245
+ text = next;
1246
+ copy.textContent = next;
1247
+ },
1248
+ get text() {
1249
+ return text;
1250
+ },
1251
+ destroy() {
1252
+ if (destroyed) return;
1253
+ destroyed = true;
1254
+ stop();
1255
+ if (text === originalText) el.replaceChildren(...original);
1256
+ else el.textContent = text;
1257
+ delete el.dataset.mkReady;
1258
+ }
1259
+ };
1260
+ }
1261
+
1262
+ // src/recipes/sortable/create-sortable.ts
1263
+ var GROUP = "[data-mk-sortable-group]";
1264
+ var ITEM = "[data-mk-item]";
1265
+ var HANDLE = "[data-mk-sort-handle]";
1266
+ var INTERACTIVE = [
1267
+ "a[href]",
1268
+ "button",
1269
+ "input",
1270
+ "select",
1271
+ "textarea",
1272
+ '[contenteditable="true"]',
1273
+ '[role="button"]',
1274
+ '[role="slider"]',
1275
+ '[data-clickable="true"]'
1276
+ ].join(",");
1277
+ var THRESHOLD = 6;
1278
+ var TILT = { max: 14, vel: 2e-3, accel: 45e-5, stiffness: 170, damping: 13, hang: 0.4, refArea: 24e3 };
1279
+ var EDGE = 56;
1280
+ var MAX_SCROLL = 16;
1281
+ var TRANSFORMS = ["transform", "translate", "rotate", "scale"];
1282
+ var LABELS = {
1283
+ lifted: (position, total) => `Elemento levantado, posici\xF3n ${position} de ${total}. Flechas para mover, Espacio para soltar, Escape para cancelar.`,
1284
+ moved: (position, total, group) => group ? `${group}, posici\xF3n ${position} de ${total}.` : `Posici\xF3n ${position} de ${total}.`,
1285
+ dropped: (position, total) => `Elemento soltado en la posici\xF3n ${position} de ${total}.`,
1286
+ cancelled: "Movimiento cancelado."
1287
+ };
1288
+ function createSortable(root, options = {}) {
1289
+ if (!root) {
1290
+ warnOnce("sortable: missing root");
1291
+ return inert({ cancel() {
1292
+ }, dragging: false });
1293
+ }
1294
+ registerMotion();
1295
+ const scope = createScope();
1296
+ const { mutate = true, autoScroll = true } = options;
1297
+ const follow = { stiffness: 1100, damping: 38, ...options.follow };
1298
+ const labels = { ...LABELS, ...options.labels };
1299
+ const groups = () => {
1300
+ const list = [...root.querySelectorAll(GROUP)];
1301
+ if (root.matches(GROUP)) list.unshift(root);
1302
+ else if (!list.length) list.push(root);
1303
+ return list;
1304
+ };
1305
+ const ownerOf = (item) => {
1306
+ const group = item.parentElement?.closest(GROUP);
1307
+ return group && root.contains(group) ? group : root;
1308
+ };
1309
+ const itemsOf = (group) => [...group.querySelectorAll(ITEM)].filter((item) => ownerOf(item) === group);
1310
+ const keyOf = (group) => group.dataset.mkSortableGroup ?? "";
1311
+ const isLocked = (group) => group.hasAttribute("data-mk-sortable-locked") && group.dataset.mkSortableLocked !== "false";
1312
+ const idOf = (item) => {
1313
+ if (item.dataset.mkId !== void 0) return item.dataset.mkId;
1314
+ warnOnce("sortable: give each [data-mk-item] a data-mk-id");
1315
+ return String(itemsOf(ownerOf(item)).indexOf(item));
1316
+ };
1317
+ const findItem = (id) => [...root.querySelectorAll(ITEM)].find((item) => item.dataset.mkId === id) ?? null;
1318
+ const touched = /* @__PURE__ */ new Set();
1319
+ const transformY = (el) => {
1320
+ touched.add(el);
1321
+ return Number(gsap.getProperty(el, "y")) || 0;
1322
+ };
1323
+ const transformX = (el) => {
1324
+ touched.add(el);
1325
+ return Number(gsap.getProperty(el, "x")) || 0;
1326
+ };
1327
+ const untouch = () => {
1328
+ const idle = [...touched].filter((el) => !gsap.isTweening(el));
1329
+ for (const el of idle) touched.delete(el);
1330
+ clearStyles(idle, TRANSFORMS);
1331
+ };
1332
+ const makeDetail = (item, from, to) => {
1333
+ const others = itemsOf(to.group).filter((el) => el !== item);
1334
+ const after = others[to.index - 1];
1335
+ const before = others[to.index];
1336
+ const detail = {
1337
+ id: idOf(item),
1338
+ item,
1339
+ fromGroup: keyOf(from.group),
1340
+ toGroup: keyOf(to.group),
1341
+ fromIndex: from.index,
1342
+ toIndex: to.index,
1343
+ groupChanged: from.group !== to.group,
1344
+ afterId: after ? idOf(after) : null,
1345
+ beforeId: after ? null : before ? idOf(before) : null
1346
+ };
1347
+ return detail;
1348
+ };
1349
+ const moveDom = (item, group, index) => {
1350
+ const focused = item.contains(document.activeElement) ? document.activeElement : null;
1351
+ const others = itemsOf(group).filter((el) => el !== item);
1352
+ const before = others[index];
1353
+ if (before) before.before(item);
1354
+ else if (others.length) others[others.length - 1].after(item);
1355
+ else group.append(item);
1356
+ focused?.focus({ preventScroll: true });
1357
+ };
1358
+ const waiting = /* @__PURE__ */ new Set();
1359
+ scope.add(() => {
1360
+ for (const stop of [...waiting]) stop();
1361
+ });
1362
+ const domChange = (ms = 400) => new Promise((resolve) => {
1363
+ let done = false;
1364
+ const finish2 = (changed) => {
1365
+ if (done) return;
1366
+ done = true;
1367
+ waiting.delete(stop);
1368
+ observer.disconnect();
1369
+ clearTimeout(timer);
1370
+ resolve(changed);
1371
+ };
1372
+ const stop = () => finish2(false);
1373
+ waiting.add(stop);
1374
+ const observer = new MutationObserver((records) => {
1375
+ if (records.some(
1376
+ (record) => [...record.addedNodes, ...record.removedNodes].some((node) => node instanceof HTMLElement && node.matches(ITEM))
1377
+ ))
1378
+ finish2(true);
1379
+ });
1380
+ observer.observe(root, { childList: true, subtree: true });
1381
+ const timer = setTimeout(stop, ms);
1382
+ });
1383
+ let live = null;
1384
+ const announce = (text) => {
1385
+ if (!live) {
1386
+ live = document.createElement("div");
1387
+ live.setAttribute("aria-live", "assertive");
1388
+ live.setAttribute("role", "status");
1389
+ Object.assign(live.style, {
1390
+ position: "absolute",
1391
+ width: "1px",
1392
+ height: "1px",
1393
+ overflow: "hidden",
1394
+ clipPath: "inset(50%)",
1395
+ whiteSpace: "nowrap"
1396
+ });
1397
+ document.body.append(live);
1398
+ const created = live;
1399
+ scope.add(() => created.remove());
1400
+ }
1401
+ live.textContent = text;
1402
+ };
1403
+ const pointer2 = { x: 0, y: 0 };
1404
+ let press = null;
1405
+ let dragging = false;
1406
+ let floating = { x: 0, y: 0, width: 0, tilt: 0, lift: 0, fade: 0 };
1407
+ let layers = [];
1408
+ let lifting = null;
1409
+ let landing = null;
1410
+ let layout = null;
1411
+ const shifted = /* @__PURE__ */ new Map();
1412
+ let placement = null;
1413
+ let physics = null;
1414
+ let scrollers = [];
1415
+ let draggedEls = /* @__PURE__ */ new Set();
1416
+ const render = () => {
1417
+ const { x, y, width, tilt, lift, fade } = floating;
1418
+ layers.forEach((layer, i) => {
1419
+ const style = layer.el.style;
1420
+ style.transform = `translate3d(${x - layer.ox}px, ${y - layer.oy}px, 0) rotate(${tilt}deg) scale(${1 + lift * 0.03})`;
1421
+ style.width = `${width}px`;
1422
+ style.setProperty("--mk-lift", lift.toFixed(3));
1423
+ if (layers.length > 1) style.opacity = String(i === 0 ? 1 - fade : fade);
1424
+ });
1425
+ };
1426
+ const makeLayer = (source, parent, grabX, grabY) => {
1427
+ const el = source.cloneNode(true);
1428
+ for (const name of ["data-mk-item", "data-mk-id", "data-mk-dragging", "data-mk-sort-grabbed", "data-flip-id", "id", "tabindex"])
1429
+ el.removeAttribute(name);
1430
+ for (const child of el.querySelectorAll("[id]")) child.removeAttribute("id");
1431
+ el.dataset.mkSortableClone = "";
1432
+ el.setAttribute("aria-hidden", "true");
1433
+ el.inert = true;
1434
+ Object.assign(el.style, {
1435
+ position: "fixed",
1436
+ top: "0",
1437
+ left: "0",
1438
+ margin: "0",
1439
+ boxSizing: "border-box",
1440
+ zIndex: "9999",
1441
+ pointerEvents: "none",
1442
+ willChange: "transform",
1443
+ transform: "none",
1444
+ transformOrigin: `${grabX}px ${grabY}px`,
1445
+ translate: "none",
1446
+ rotate: "none",
1447
+ scale: "none",
1448
+ visibility: "visible"
1449
+ });
1450
+ parent.append(el);
1451
+ const box = el.getBoundingClientRect();
1452
+ return { el, ox: box.left, oy: box.top };
1453
+ };
1454
+ const measure = () => {
1455
+ if (!press) return;
1456
+ const item = press.item;
1457
+ const measured = groups().map((group) => ({
1458
+ el: group,
1459
+ rect: group.getBoundingClientRect(),
1460
+ locked: isLocked(group),
1461
+ items: itemsOf(group).map((el) => {
1462
+ const box = el.getBoundingClientRect();
1463
+ return { el, top: box.top - transformY(el), height: box.height };
1464
+ })
1465
+ }));
1466
+ const source = measured.find((group) => group.el === press.group) ?? measured[0];
1467
+ const index = source.items.findIndex((entry) => entry.el === item);
1468
+ const dragged = source.items[index];
1469
+ if (!dragged) return;
1470
+ const next = source.items[index + 1];
1471
+ const previous = source.items[index - 1];
1472
+ const spacing = next ? next.top - (dragged.top + dragged.height) : previous ? dragged.top - (previous.top + previous.height) : parseFloat(getComputedStyle(source.el).rowGap) || 0;
1473
+ layout = { groups: measured, source, dragTop: dragged.top, shift: dragged.height + Math.max(0, spacing) };
1474
+ };
1475
+ const applyShift = (target, index) => {
1476
+ if (!layout || !press) return;
1477
+ const item = press.item;
1478
+ const next = /* @__PURE__ */ new Map();
1479
+ for (const entry of layout.source.items) if (entry.top > layout.dragTop) next.set(entry.el, -layout.shift);
1480
+ target.items.filter((entry) => entry.el !== item).forEach((entry, i) => {
1481
+ if (i >= index) next.set(entry.el, (next.get(entry.el) ?? 0) + layout.shift);
1482
+ });
1483
+ for (const el of shifted.keys()) if (!next.has(el)) next.set(el, 0);
1484
+ for (const [el, y] of next) {
1485
+ if ((shifted.get(el) ?? 0) === y) continue;
1486
+ shifted.set(el, y);
1487
+ touched.add(el);
1488
+ gsap.to(el, { y, duration: reduced() ? 0 : TIMING.sortable.shift, ease: EASE.layoutReflow, overwrite: "auto" });
1489
+ }
1490
+ };
1491
+ const unshift = (animated) => {
1492
+ const els = [...shifted.keys()];
1493
+ shifted.clear();
1494
+ if (!els.length) return;
1495
+ if (animated && !reduced()) {
1496
+ gsap.to(els, {
1497
+ y: 0,
1498
+ duration: TIMING.sortable.shift,
1499
+ ease: EASE.layoutReflow,
1500
+ overwrite: "auto",
1501
+ onComplete: () => {
1502
+ clearStyles(els, TRANSFORMS);
1503
+ for (const el of els) touched.delete(el);
1504
+ }
1505
+ });
1506
+ } else {
1507
+ gsap.killTweensOf(els);
1508
+ clearStyles(els, TRANSFORMS);
1509
+ }
1510
+ };
1511
+ const place = () => {
1512
+ if (!layout || !press) return;
1513
+ const all = layout.groups;
1514
+ const inside = (rect) => pointer2.x >= rect.left && pointer2.x <= rect.right;
1515
+ const target = all.length <= 1 ? all[0] : all.find(({ rect }) => inside(rect) && pointer2.y >= rect.top && pointer2.y <= rect.bottom) ?? all.find(({ rect }) => inside(rect));
1516
+ if (!target || target.locked) return;
1517
+ const others = target.items.filter((entry) => entry.el !== press.item);
1518
+ let index = 0;
1519
+ for (const entry of others) {
1520
+ if (pointer2.y < entry.top + entry.height / 2) break;
1521
+ index++;
1522
+ }
1523
+ applyShift(target, index);
1524
+ placement = { group: target.el, index };
1525
+ };
1526
+ const tick = () => {
1527
+ if (!physics || !press) return;
1528
+ const target = { x: pointer2.x - press.grabX, y: pointer2.y - press.grabY };
1529
+ if (reduced()) {
1530
+ floating.x = target.x;
1531
+ floating.y = target.y;
1532
+ render();
1533
+ return;
1534
+ }
1535
+ const P = physics;
1536
+ const now = performance.now();
1537
+ const dt = Math.min((now - P.t) / 1e3, 1 / 30);
1538
+ if (dt <= 0) return;
1539
+ P.t = now;
1540
+ const k = 1 - Math.exp(-dt / 0.05);
1541
+ const ax = ((pointer2.x - P.x) / dt - P.vx) * k;
1542
+ const ay = ((pointer2.y - P.y) / dt - P.vy) * k;
1543
+ P.vx += ax;
1544
+ P.vy += ay;
1545
+ P.x = pointer2.x;
1546
+ P.y = pointer2.y;
1547
+ const torque = (P.vx * TILT.vel + ax / dt * TILT.accel) * P.armY - (P.vy * TILT.vel + ay / dt * TILT.accel) * P.armX;
1548
+ const tilt = TILT.max * Math.tanh(torque / TILT.max);
1549
+ P.tiltV += ((tilt - floating.tilt) * TILT.stiffness - P.tiltV * TILT.damping) * dt / P.mass;
1550
+ floating.tilt += P.tiltV * dt;
1551
+ P.fvx += ((target.x - floating.x) * follow.stiffness - P.fvx * follow.damping) * dt / P.mass;
1552
+ P.fvy += ((target.y - floating.y) * follow.stiffness - P.fvy * follow.damping) * dt / P.mass;
1553
+ floating.x += P.fvx * dt;
1554
+ floating.y += P.fvy * dt;
1555
+ render();
1556
+ };
1557
+ const scrollStep = (distance) => Math.ceil(MAX_SCROLL * Math.min(1, distance / EDGE));
1558
+ const scrollTick = () => {
1559
+ for (const scroller of scrollers) {
1560
+ const el = scroller.el;
1561
+ const box = el ? el.getBoundingClientRect() : { top: 0, left: 0, bottom: innerHeight, right: innerWidth };
1562
+ const top = el ? el.scrollTop : scrollY;
1563
+ const left = el ? el.scrollLeft : scrollX;
1564
+ const height = el ? el.clientHeight : innerHeight;
1565
+ const width = el ? el.clientWidth : innerWidth;
1566
+ const fullHeight = el ? el.scrollHeight : document.documentElement.scrollHeight;
1567
+ const fullWidth = el ? el.scrollWidth : document.documentElement.scrollWidth;
1568
+ let dy = 0;
1569
+ let dx = 0;
1570
+ if (scroller.y) {
1571
+ if (pointer2.y < box.top + EDGE && top > 0) dy = -scrollStep(box.top + EDGE - pointer2.y);
1572
+ else if (pointer2.y > box.bottom - EDGE && top + height < fullHeight - 1) dy = scrollStep(pointer2.y - (box.bottom - EDGE));
1573
+ }
1574
+ if (scroller.x) {
1575
+ if (pointer2.x < box.left + EDGE && left > 0) dx = -scrollStep(box.left + EDGE - pointer2.x);
1576
+ else if (pointer2.x > box.right - EDGE && left + width < fullWidth - 1) dx = scrollStep(pointer2.x - (box.right - EDGE));
1577
+ }
1578
+ if (dx || dy) {
1579
+ if (el) el.scrollBy(dx, dy);
1580
+ else window.scrollBy(dx, dy);
1581
+ measure();
1582
+ place();
1583
+ return;
1584
+ }
1585
+ }
1586
+ };
1587
+ const startScroll = () => {
1588
+ scrollers = [];
1589
+ for (let el = root; el && el !== document.body && el !== document.documentElement; el = el.parentElement) {
1590
+ const style = getComputedStyle(el);
1591
+ const y = /(auto|scroll)/.test(style.overflowY) && el.scrollHeight > el.clientHeight + 1;
1592
+ const x = /(auto|scroll)/.test(style.overflowX) && el.scrollWidth > el.clientWidth + 1;
1593
+ if (x || y) scrollers.push({ el, x, y });
1594
+ }
1595
+ const page = document.documentElement;
1596
+ scrollers.push({ el: null, y: page.scrollHeight > innerHeight + 1, x: page.scrollWidth > innerWidth + 1 });
1597
+ gsap.ticker.add(scrollTick);
1598
+ };
1599
+ const start = () => {
1600
+ if (!press) return;
1601
+ const { item, group, startX, startY, grabX, grabY, width, height } = press;
1602
+ dragging = true;
1603
+ floating = { x: startX - grabX, y: startY - grabY, width, tilt: 0, lift: reduced() ? 1 : 0, fade: 0 };
1604
+ layers = [makeLayer(item, group, grabX, grabY)];
1605
+ draggedEls = /* @__PURE__ */ new Set([item]);
1606
+ item.setAttribute("data-mk-dragging", "");
1607
+ root.setAttribute("data-mk-sorting", "");
1608
+ if (!reduced()) lifting = gsap.to(floating, { lift: 1, duration: TIMING.sortable.lift, ease: "power2.out", onUpdate: render });
1609
+ const halfW = width / 2 || 1;
1610
+ const halfH = height / 2 || 1;
1611
+ physics = {
1612
+ vx: 0,
1613
+ vy: 0,
1614
+ tiltV: 0,
1615
+ fvx: 0,
1616
+ fvy: 0,
1617
+ armX: (halfW - grabX) / halfW,
1618
+ armY: Math.max((halfH - grabY) / halfH, TILT.hang),
1619
+ mass: Math.min(2.5, Math.max(1, Math.sqrt(width * height / TILT.refArea))),
1620
+ x: pointer2.x,
1621
+ y: pointer2.y,
1622
+ t: performance.now()
1623
+ };
1624
+ gsap.ticker.add(tick);
1625
+ measure();
1626
+ if (autoScroll) startScroll();
1627
+ placement = { group, index: press.index };
1628
+ render();
1629
+ };
1630
+ const finish = () => {
1631
+ landing = null;
1632
+ lifting?.kill();
1633
+ lifting = null;
1634
+ for (const layer of layers) layer.el.remove();
1635
+ layers = [];
1636
+ for (const el of draggedEls) el.removeAttribute("data-mk-dragging");
1637
+ draggedEls.clear();
1638
+ root.removeAttribute("data-mk-sorting");
1639
+ dragging = false;
1640
+ untouch();
1641
+ };
1642
+ const land = (target) => {
1643
+ lifting?.kill();
1644
+ lifting = null;
1645
+ const item = target && target.isConnected ? target : null;
1646
+ if (!item || reduced()) {
1647
+ finish();
1648
+ return;
1649
+ }
1650
+ if (!draggedEls.has(item)) {
1651
+ item.setAttribute("data-mk-dragging", "");
1652
+ draggedEls.add(item);
1653
+ }
1654
+ const box = item.getBoundingClientRect();
1655
+ if (layers.length === 1 && ownerOf(item) !== layers[0].el.parentElement) {
1656
+ const origin2 = layers[0].el.style.transformOrigin.split(" ").map(parseFloat);
1657
+ layers.push(makeLayer(item, ownerOf(item), origin2[0] ?? 0, origin2[1] ?? 0));
1658
+ render();
1659
+ }
1660
+ landing = gsap.timeline({ onComplete: finish });
1661
+ landing.to(
1662
+ floating,
1663
+ {
1664
+ x: box.left - transformX(item),
1665
+ y: box.top - transformY(item),
1666
+ width: box.width,
1667
+ tilt: 0,
1668
+ lift: 0,
1669
+ duration: TIMING.sortable.land,
1670
+ ease: EASE.sortableLand,
1671
+ onUpdate: render
1672
+ },
1673
+ 0
1674
+ );
1675
+ landing.to(floating, { fade: 1, duration: TIMING.sortable.landFade, ease: "power1.inOut", onUpdate: render }, 0);
1676
+ };
1677
+ const stopTracking = () => {
1678
+ window.removeEventListener("pointermove", onMove);
1679
+ window.removeEventListener("pointerup", onUp);
1680
+ window.removeEventListener("pointercancel", onCancel);
1681
+ window.removeEventListener("keydown", onEscape);
1682
+ gsap.ticker.remove(tick);
1683
+ gsap.ticker.remove(scrollTick);
1684
+ physics = null;
1685
+ scrollers = [];
1686
+ };
1687
+ const swallowClick = () => {
1688
+ const swallow = (event) => {
1689
+ event.preventDefault();
1690
+ event.stopPropagation();
1691
+ };
1692
+ window.addEventListener("click", swallow, { capture: true, once: true });
1693
+ setTimeout(() => window.removeEventListener("click", swallow, { capture: true }), 0);
1694
+ };
1695
+ const drop = async () => {
1696
+ const current = press;
1697
+ const target = placement;
1698
+ const started = dragging;
1699
+ stopTracking();
1700
+ press = null;
1701
+ placement = null;
1702
+ layout = null;
1703
+ if (!current || !started) return;
1704
+ swallowClick();
1705
+ const { item } = current;
1706
+ const origin2 = { group: current.group, index: current.index };
1707
+ if (!target || target.group === origin2.group && target.index === origin2.index) {
1708
+ unshift(true);
1709
+ land(item);
1710
+ return;
1711
+ }
1712
+ const detail = makeDetail(item, origin2, target);
1713
+ if (!root.dispatchEvent(new CustomEvent("mk:reorder", { bubbles: true, cancelable: true, detail }))) {
1714
+ unshift(true);
1715
+ land(item);
1716
+ return;
1717
+ }
1718
+ if (mutate) {
1719
+ moveDom(item, target.group, target.index);
1720
+ unshift(false);
1721
+ options.onReorder?.(detail);
1722
+ land(item);
1723
+ return;
1724
+ }
1725
+ const changed = domChange();
1726
+ options.onReorder?.(detail);
1727
+ const answered = await changed;
1728
+ if (scope.disposed) return;
1729
+ if (!answered) {
1730
+ unshift(true);
1731
+ land(item.isConnected ? item : findItem(detail.id));
1732
+ return;
1733
+ }
1734
+ unshift(false);
1735
+ land(findItem(detail.id) ?? item);
1736
+ };
1737
+ const cancel = () => {
1738
+ const current = press;
1739
+ const started = dragging;
1740
+ stopTracking();
1741
+ press = null;
1742
+ placement = null;
1743
+ layout = null;
1744
+ if (!current || !started) return;
1745
+ unshift(true);
1746
+ land(current.item);
1747
+ };
1748
+ function onMove(event) {
1749
+ if (!press) return;
1750
+ pointer2.x = event.clientX;
1751
+ pointer2.y = event.clientY;
1752
+ if (!dragging) {
1753
+ const dx = event.clientX - press.startX;
1754
+ const dy = event.clientY - press.startY;
1755
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
1756
+ start();
1757
+ }
1758
+ event.preventDefault();
1759
+ place();
1760
+ }
1761
+ function onUp() {
1762
+ void drop();
1763
+ }
1764
+ function onCancel() {
1765
+ cancel();
1766
+ }
1767
+ function onEscape(event) {
1768
+ if (event.key === "Escape") cancel();
1769
+ }
1770
+ scope.on(root, "pointerdown", (event) => {
1771
+ const down = event;
1772
+ const target = down.target;
1773
+ if (down.button > 0 || press || keyboard || !target) return;
1774
+ const item = target.closest(ITEM);
1775
+ if (!item || !root.contains(item)) return;
1776
+ const onHandle = Boolean(target.closest(HANDLE));
1777
+ const coarse = window.matchMedia?.("(pointer: coarse)").matches === true;
1778
+ if ((options.handle || coarse) && !onHandle) return;
1779
+ const control = target.closest(INTERACTIVE);
1780
+ if (!onHandle && control && control !== item && item.contains(control)) return;
1781
+ landing?.progress(1);
1782
+ const group = ownerOf(item);
1783
+ const box = item.getBoundingClientRect();
1784
+ press = {
1785
+ item,
1786
+ group,
1787
+ index: itemsOf(group).indexOf(item),
1788
+ startX: down.clientX,
1789
+ startY: down.clientY,
1790
+ grabX: down.clientX - box.left,
1791
+ grabY: down.clientY - box.top,
1792
+ width: box.width,
1793
+ height: box.height
1794
+ };
1795
+ pointer2.x = down.clientX;
1796
+ pointer2.y = down.clientY;
1797
+ down.preventDefault();
1798
+ window.addEventListener("pointermove", onMove, { passive: false });
1799
+ window.addEventListener("pointerup", onUp);
1800
+ window.addEventListener("pointercancel", onCancel);
1801
+ window.addEventListener("keydown", onEscape);
1802
+ });
1803
+ scope.on(root, "dragstart", (event) => {
1804
+ if (event.target?.closest?.(ITEM)) event.preventDefault();
1805
+ });
1806
+ let keyboard = null;
1807
+ let moving = false;
1808
+ let flipping = null;
1809
+ const positionOf = (item) => {
1810
+ const list = itemsOf(ownerOf(item));
1811
+ return { position: list.indexOf(item) + 1, total: list.length };
1812
+ };
1813
+ const groupName = (group) => group.getAttribute("aria-label") ?? (groups().length > 1 ? keyOf(group) : "");
1814
+ const commit = async (item, group, index) => {
1815
+ const from = { group: ownerOf(item), index: itemsOf(ownerOf(item)).indexOf(item) };
1816
+ if (from.group === group && from.index === index) return item;
1817
+ const detail = makeDetail(item, from, { group, index });
1818
+ if (!root.dispatchEvent(new CustomEvent("mk:reorder", { bubbles: true, cancelable: true, detail }))) return item;
1819
+ moving = true;
1820
+ const focused = item.contains(document.activeElement) ? document.activeElement : item;
1821
+ const captured = captureLayout(root, ITEM);
1822
+ let current = item;
1823
+ if (mutate) {
1824
+ moveDom(item, group, index);
1825
+ options.onReorder?.(detail);
1826
+ } else {
1827
+ const changed = domChange();
1828
+ options.onReorder?.(detail);
1829
+ await changed;
1830
+ current = findItem(detail.id) ?? item;
1831
+ }
1832
+ const target = current === item ? focused : current;
1833
+ if (target.isConnected && document.activeElement !== target) target.focus({ preventScroll: true });
1834
+ moving = false;
1835
+ if (scope.disposed) return current;
1836
+ const untag = () => {
1837
+ for (const el of root.querySelectorAll('[data-flip-id^="auto-"]')) {
1838
+ el.removeAttribute("data-flip-id");
1839
+ touched.add(el);
1840
+ }
1841
+ untouch();
1842
+ };
1843
+ flipping?.progress(1);
1844
+ flipping = reflowLayout(root, ITEM, captured);
1845
+ if (flipping) flipping.eventCallback("onComplete", untag);
1846
+ else untag();
1847
+ return current;
1848
+ };
1849
+ const release = (announcement) => {
1850
+ if (!keyboard) return;
1851
+ keyboard.item.removeAttribute("data-mk-sort-grabbed");
1852
+ keyboard = null;
1853
+ if (announcement) announce(announcement);
1854
+ };
1855
+ scope.on(root, "keydown", (event) => {
1856
+ const key = event;
1857
+ const target = key.target;
1858
+ const item = target?.closest(ITEM);
1859
+ if (!item || !root.contains(item) || dragging) return;
1860
+ const handle = target.closest(HANDLE);
1861
+ if (target !== item && !(handle && item.contains(handle))) return;
1862
+ const lift = key.key === " " || key.key === "Enter";
1863
+ if (keyboard && keyboard.item !== item) release();
1864
+ if (!keyboard) {
1865
+ if (!lift) return;
1866
+ key.preventDefault();
1867
+ keyboard = { item, startGroup: ownerOf(item), startIndex: itemsOf(ownerOf(item)).indexOf(item) };
1868
+ item.setAttribute("data-mk-sort-grabbed", "");
1869
+ const { position, total } = positionOf(item);
1870
+ announce(labels.lifted(position, total));
1871
+ return;
1872
+ }
1873
+ const grabbed = keyboard;
1874
+ const group = ownerOf(item);
1875
+ const list = itemsOf(group);
1876
+ const index = list.indexOf(item);
1877
+ let to = null;
1878
+ if (key.key === "ArrowUp" || key.key === "ArrowDown") {
1879
+ const next = Math.min(list.length - 1, Math.max(0, index + (key.key === "ArrowUp" ? -1 : 1)));
1880
+ to = { group, index: next };
1881
+ } else if (key.key === "ArrowLeft" || key.key === "ArrowRight") {
1882
+ const open = groups().filter((candidate) => candidate === group || !isLocked(candidate));
1883
+ const next = open[open.indexOf(group) + (key.key === "ArrowLeft" ? -1 : 1)];
1884
+ if (next) to = { group: next, index: Math.min(index, itemsOf(next).length) };
1885
+ } else if (lift) {
1886
+ key.preventDefault();
1887
+ const { position, total } = positionOf(item);
1888
+ release(labels.dropped(position, total));
1889
+ return;
1890
+ } else if (key.key === "Escape") {
1891
+ key.preventDefault();
1892
+ void commit(item, grabbed.startGroup, grabbed.startIndex).then(() => release(labels.cancelled));
1893
+ return;
1894
+ } else return;
1895
+ key.preventDefault();
1896
+ if (!to) return;
1897
+ void commit(item, to.group, to.index).then((moved) => {
1898
+ if (!keyboard) return;
1899
+ if (moved !== keyboard.item) {
1900
+ keyboard.item.removeAttribute("data-mk-sort-grabbed");
1901
+ keyboard.item = moved;
1902
+ moved.setAttribute("data-mk-sort-grabbed", "");
1903
+ }
1904
+ const { position, total } = positionOf(moved);
1905
+ announce(labels.moved(position, total, groupName(ownerOf(moved))));
1906
+ });
1907
+ });
1908
+ scope.on(root, "keyup", (event) => {
1909
+ if (event.key === " " && event.target?.closest(HANDLE)) event.preventDefault();
1910
+ });
1911
+ scope.on(root, "focusout", (event) => {
1912
+ if (!keyboard || moving) return;
1913
+ const next = event.relatedTarget;
1914
+ if (next && keyboard.item.contains(next)) return;
1915
+ release();
1916
+ });
1917
+ const focusable = (item) => {
1918
+ if (item.querySelector(HANDLE) || item.hasAttribute("tabindex") || item.matches(INTERACTIVE)) return;
1919
+ scope.attr(item, "tabindex", "0");
1920
+ };
1921
+ for (const item of root.querySelectorAll(ITEM)) focusable(item);
1922
+ scope.observe(
1923
+ new MutationObserver(() => {
1924
+ for (const item of root.querySelectorAll(`${ITEM}:not([tabindex])`)) focusable(item);
1925
+ })
1926
+ ).observe(root, { childList: true, subtree: true });
1927
+ root.dataset.mkReady = "";
1928
+ return {
1929
+ cancel,
1930
+ get dragging() {
1931
+ return dragging;
1932
+ },
1933
+ destroy() {
1934
+ stopTracking();
1935
+ press = null;
1936
+ landing?.kill();
1937
+ flipping?.progress(1);
1938
+ unshift(false);
1939
+ finish();
1940
+ release();
1941
+ scope.dispose();
1942
+ delete root.dataset.mkReady;
1943
+ }
1944
+ };
1945
+ }
1946
+ function moveItem(list, from, to) {
1947
+ const next = list.slice();
1948
+ const [entry] = next.splice(from, 1);
1949
+ if (entry !== void 0) next.splice(Math.min(Math.max(to, 0), next.length), 0, entry);
1950
+ return next;
1951
+ }
1952
+
1953
+ // src/recipes/mask-text/create-mask-text.ts
1954
+ var CLASSES = { lines: "mk-mask-line", words: "mk-mask-word", chars: "mk-mask-char" };
1955
+ var TYPES = { lines: "lines", words: "words", chars: "words,chars" };
1956
+ function createMaskText(el, options = {}) {
1957
+ if (!el) {
1958
+ warnOnce("mask-text: missing element");
1959
+ return inert({ play() {
1960
+ }, hide() {
1961
+ }, split: null });
1962
+ }
1963
+ const t = TIMING.maskText;
1964
+ const {
1965
+ by = "lines",
1966
+ y = t.y,
1967
+ duration = t.duration,
1968
+ ease = "expo.out",
1969
+ delay = t.delay,
1970
+ stagger = t.stagger,
1971
+ trigger = "scroll",
1972
+ once = false
1973
+ } = options;
1974
+ if (!entrances()) {
1975
+ el.dataset.mkReady = "";
1976
+ return {
1977
+ play() {
1978
+ },
1979
+ hide() {
1980
+ },
1981
+ split: null,
1982
+ destroy() {
1983
+ delete el.dataset.mkReady;
1984
+ }
1985
+ };
1986
+ }
1987
+ registerMotion();
1988
+ let shown = false;
1989
+ let tween = null;
1990
+ let observer = null;
1991
+ const split = SplitText.create(el, {
1992
+ type: TYPES[by],
1993
+ mask: by,
1994
+ autoSplit: true,
1995
+ aria: "auto",
1996
+ linesClass: CLASSES.lines,
1997
+ wordsClass: CLASSES.words,
1998
+ charsClass: CLASSES.chars,
1999
+ // A new split (resize, fonts) keeps the state the text was in.
2000
+ onSplit: (self) => {
2001
+ tween?.kill();
2002
+ gsap.set(self[by], { yPercent: shown ? 0 : y });
2003
+ }
2004
+ });
2005
+ const pieces = () => split[by];
2006
+ gsap.set(pieces(), { yPercent: y });
2007
+ const play = () => {
2008
+ shown = true;
2009
+ tween?.kill();
2010
+ tween = gsap.to(pieces(), { yPercent: 0, duration, ease, delay, stagger: { each: stagger, from: "start" }, overwrite: true });
2011
+ };
2012
+ const hide = () => {
2013
+ shown = false;
2014
+ tween?.kill();
2015
+ tween = gsap.to(pieces(), { yPercent: y, duration, ease, stagger: { each: t.exitStagger }, overwrite: true });
2016
+ };
2017
+ const reset = () => {
2018
+ shown = false;
2019
+ tween?.kill();
2020
+ gsap.set(pieces(), { yPercent: y });
2021
+ };
2022
+ if (trigger === "load") play();
2023
+ else if (trigger === "scroll" && typeof IntersectionObserver === "function") {
2024
+ observer = new IntersectionObserver(
2025
+ (entries) => {
2026
+ for (const entry of entries) {
2027
+ if (shows(entry, t.threshold)) {
2028
+ if (!shown) play();
2029
+ if (once) observer?.disconnect();
2030
+ } else if (!entry.isIntersecting && shown && !once) reset();
2031
+ }
2032
+ },
2033
+ { threshold: thresholds(t.threshold), rootMargin: "10px" }
2034
+ );
2035
+ observer.observe(el);
2036
+ }
2037
+ el.dataset.mkReady = "";
2038
+ return {
2039
+ play,
2040
+ hide,
2041
+ split,
2042
+ destroy() {
2043
+ observer?.disconnect();
2044
+ tween?.kill();
2045
+ gsap.killTweensOf(pieces());
2046
+ split.revert();
2047
+ delete el.dataset.mkReady;
2048
+ }
2049
+ };
2050
+ }
2051
+
2052
+ // src/recipes/cascade/create-cascade.ts
2053
+ var STYLES = ["transform", "translate", "rotate", "scale", "transform-origin", "opacity", "visibility"];
2054
+ function createCascade(root, options = {}) {
2055
+ if (!root) {
2056
+ warnOnce("cascade: missing element");
2057
+ return inert({ play() {
2058
+ }, reset() {
2059
+ } });
2060
+ }
2061
+ const t = TIMING.cascade;
2062
+ const { duration = t.duration, step = t.step, once = false, trigger = "scroll" } = options;
2063
+ const delay = options.delay ?? (Number(root.dataset.mkDelay) || 0);
2064
+ const items = [...root.querySelectorAll("[data-mk-cascade]")].map((el, index) => ({
2065
+ el,
2066
+ index,
2067
+ kind: el.dataset.mkCascade || "fade"
2068
+ }));
2069
+ if (!entrances() || !items.length) {
2070
+ root.dataset.mkReady = "";
2071
+ return {
2072
+ play() {
2073
+ },
2074
+ reset() {
2075
+ },
2076
+ destroy() {
2077
+ delete root.dataset.mkReady;
2078
+ }
2079
+ };
2080
+ }
2081
+ registerMotion();
2082
+ let observer = null;
2083
+ const setOut = ({ el, index, kind }) => {
2084
+ gsap.killTweensOf(el);
2085
+ if (kind === "rule") gsap.set(el, { scaleX: 0, transformOrigin: index % 2 === 0 ? "left center" : "right center" });
2086
+ else if (kind === "rise") gsap.set(el, { yPercent: t.rise });
2087
+ else gsap.set(el, { autoAlpha: 0 });
2088
+ };
2089
+ items.forEach(setOut);
2090
+ let shown = false;
2091
+ const play = () => {
2092
+ shown = true;
2093
+ for (const { el, index, kind } of items) {
2094
+ const to = kind === "rule" ? { scaleX: 1 } : kind === "rise" ? { yPercent: 0 } : { autoAlpha: 1 };
2095
+ gsap.to(el, { ...to, duration, ease: "expo.out", delay: delay + index * step, overwrite: "auto" });
2096
+ }
2097
+ };
2098
+ const reset = () => {
2099
+ shown = false;
2100
+ items.forEach(setOut);
2101
+ };
2102
+ if (trigger === "load") play();
2103
+ else if (trigger === "scroll" && typeof IntersectionObserver === "function") {
2104
+ observer = new IntersectionObserver(
2105
+ (entries) => {
2106
+ for (const entry of entries) {
2107
+ if (shows(entry, t.threshold)) {
2108
+ if (!shown) play();
2109
+ if (once) observer?.disconnect();
2110
+ } else if (!entry.isIntersecting && shown && !once) reset();
2111
+ }
2112
+ },
2113
+ { threshold: thresholds(t.threshold) }
2114
+ );
2115
+ observer.observe(root);
2116
+ }
2117
+ root.dataset.mkReady = "";
2118
+ return {
2119
+ play,
2120
+ reset,
2121
+ destroy() {
2122
+ observer?.disconnect();
2123
+ const elements = items.map((item) => item.el);
2124
+ gsap.killTweensOf(elements);
2125
+ clearStyles(elements, STYLES);
2126
+ delete root.dataset.mkReady;
2127
+ }
2128
+ };
2129
+ }
2130
+
2131
+ // src/recipes/text-roll/create-text-roll.ts
2132
+ function createTextRoll(el, options = {}) {
2133
+ if (!el) {
2134
+ warnOnce("text-roll: missing element");
2135
+ return inert({ play() {
2136
+ }, timelines: [] });
2137
+ }
2138
+ const t = TIMING.textRoll;
2139
+ const { duration = t.duration, stagger = t.stagger, turns = t.turns, trigger = "load", delay = 0, interval = 0 } = options;
2140
+ if (!entrances()) {
2141
+ el.dataset.mkReady = "";
2142
+ return {
2143
+ play() {
2144
+ },
2145
+ timelines: [],
2146
+ destroy() {
2147
+ delete el.dataset.mkReady;
2148
+ }
2149
+ };
2150
+ }
2151
+ registerMotion();
2152
+ const split = SplitText.create(el, { type: "words,chars", wordsClass: "mk-roll-word", charsClass: "mk-roll-char", aria: "auto" });
2153
+ const timelines = split.chars.map((char) => {
2154
+ const letter = char.textContent ?? "";
2155
+ const face2 = document.createElement("span");
2156
+ face2.className = "mk-roll-face";
2157
+ face2.textContent = letter;
2158
+ const copy = face2.cloneNode(true);
2159
+ char.replaceChildren(face2, copy);
2160
+ const timeline = gsap.timeline({ paused: true });
2161
+ timeline.to([face2, copy], { yPercent: "-=100", repeat: gsap.utils.random(turns[0], turns[1], 1), ease: "none", duration: 1 });
2162
+ return timeline;
2163
+ });
2164
+ let envelope = null;
2165
+ const play = () => {
2166
+ envelope?.kill();
2167
+ envelope = gsap.to(timelines, {
2168
+ progress: 1,
2169
+ duration,
2170
+ ease: "expo.out",
2171
+ stagger: { each: stagger, ease: "none", from: "random" },
2172
+ onComplete: () => {
2173
+ gsap.set(timelines, { progress: 0 });
2174
+ }
2175
+ });
2176
+ };
2177
+ const cleanups = [];
2178
+ let visible = true;
2179
+ let scrolling = false;
2180
+ if (interval > 0 || trigger === "scroll") {
2181
+ if (typeof IntersectionObserver === "function") {
2182
+ visible = false;
2183
+ let played = false;
2184
+ const observer = new IntersectionObserver(
2185
+ ([entry]) => {
2186
+ visible = Boolean(entry?.isIntersecting);
2187
+ if (entry && shows(entry, 0.5) && trigger === "scroll" && !played) {
2188
+ played = true;
2189
+ play();
2190
+ }
2191
+ },
2192
+ { threshold: thresholds(0.5) }
2193
+ );
2194
+ observer.observe(el);
2195
+ cleanups.push(() => observer.disconnect());
2196
+ }
2197
+ }
2198
+ if (interval > 0) {
2199
+ let quiet;
2200
+ const onScroll = () => {
2201
+ scrolling = true;
2202
+ clearTimeout(quiet);
2203
+ quiet = setTimeout(() => scrolling = false, t.quietMs);
2204
+ };
2205
+ addEventListener("scroll", onScroll, { passive: true });
2206
+ const timer = setInterval(() => {
2207
+ if (visible && !scrolling) play();
2208
+ }, interval);
2209
+ cleanups.push(() => {
2210
+ removeEventListener("scroll", onScroll);
2211
+ clearInterval(timer);
2212
+ clearTimeout(quiet);
2213
+ });
2214
+ }
2215
+ if (trigger === "hover") {
2216
+ el.addEventListener("pointerenter", play);
2217
+ cleanups.push(() => el.removeEventListener("pointerenter", play));
2218
+ }
2219
+ if (trigger === "load") {
2220
+ const call = gsap.delayedCall(delay, play);
2221
+ cleanups.push(() => call.kill());
2222
+ }
2223
+ el.dataset.mkReady = "";
2224
+ return {
2225
+ play,
2226
+ timelines,
2227
+ destroy() {
2228
+ for (const cleanup of cleanups.splice(0)) cleanup();
2229
+ envelope?.kill();
2230
+ timelines.forEach((timeline) => timeline.kill());
2231
+ split.revert();
2232
+ delete el.dataset.mkReady;
2233
+ }
2234
+ };
2235
+ }
2236
+
2237
+ // src/recipes/letters/create-letters.ts
2238
+ var PASTELS = ["#FFD6FF", "#E7C6FF", "#C8B6FF", "#B8C0FF", "#BBD0FF"];
2239
+ var ORDER = [0, 2, 1, 4, 3, 0, 2, 1, 4];
2240
+ function createLetters(el, options = {}) {
2241
+ if (!el) {
2242
+ warnOnce("letters: missing element");
2243
+ return inert({ play() {
2244
+ } });
2245
+ }
2246
+ const t = TIMING.letters;
2247
+ const { effect = "scale", colors = PASTELS, autoplay = true } = options;
2248
+ if (!entrances()) {
2249
+ el.dataset.mkReady = "";
2250
+ return {
2251
+ play() {
2252
+ },
2253
+ destroy() {
2254
+ delete el.dataset.mkReady;
2255
+ }
2256
+ };
2257
+ }
2258
+ registerMotion();
2259
+ const split = SplitText.create(el, { type: "words,chars", wordsClass: "mk-letters-word", charsClass: "mk-letter", aria: "auto" });
2260
+ const chars = split.chars;
2261
+ const cleanups = [];
2262
+ const spring = motionSpring(...TIMING.springs.letters);
2263
+ const lift = (up) => {
2264
+ chars.forEach(
2265
+ (char, index) => gsap.to(char, {
2266
+ scale: up ? t.scale : 1,
2267
+ y: up ? t.y : 0,
2268
+ duration: spring.duration,
2269
+ ease: spring.ease,
2270
+ delay: index * t.step,
2271
+ overwrite: true
2272
+ })
2273
+ );
2274
+ };
2275
+ const blink = () => {
2276
+ chars.forEach((char, index) => {
2277
+ gsap.killTweensOf(char, "color");
2278
+ clearStyles([char], ["color"]);
2279
+ const own = getComputedStyle(char).color;
2280
+ const pastel = colors[ORDER[index % ORDER.length] % colors.length];
2281
+ gsap.to(char, {
2282
+ keyframes: [
2283
+ { color: pastel, duration: t.blink / 2, ease: EASE.inOut },
2284
+ { color: own, duration: t.blink / 2, ease: EASE.inOut }
2285
+ ],
2286
+ delay: t.blinkDelays[index % t.blinkDelays.length],
2287
+ repeat: 1,
2288
+ repeatDelay: t.blinkRepeatDelay,
2289
+ onComplete: () => clearStyles([char], ["color"])
2290
+ });
2291
+ });
2292
+ };
2293
+ if (effect === "scale") {
2294
+ const enter = () => lift(true);
2295
+ const leave = () => lift(false);
2296
+ el.addEventListener("pointerenter", enter);
2297
+ el.addEventListener("pointerleave", leave);
2298
+ cleanups.push(() => {
2299
+ el.removeEventListener("pointerenter", enter);
2300
+ el.removeEventListener("pointerleave", leave);
2301
+ });
2302
+ } else {
2303
+ el.addEventListener("pointerenter", blink);
2304
+ cleanups.push(() => el.removeEventListener("pointerenter", blink));
2305
+ if (autoplay) {
2306
+ const call = gsap.delayedCall(t.blinkStart, blink);
2307
+ cleanups.push(() => call.kill());
2308
+ }
2309
+ }
2310
+ el.dataset.mkReady = "";
2311
+ return {
2312
+ play: effect === "blink" ? blink : () => lift(true),
2313
+ destroy() {
2314
+ for (const cleanup of cleanups.splice(0)) cleanup();
2315
+ gsap.killTweensOf(chars);
2316
+ split.revert();
2317
+ delete el.dataset.mkReady;
2318
+ }
2319
+ };
2320
+ }
2321
+
2322
+ // src/recipes/parallax/create-parallax.ts
2323
+ function createParallax(root, options = {}) {
2324
+ const target = root?.querySelector("[data-mk-parallax-target]") ?? root?.querySelector(":scope > img, :scope > video, :scope > picture") ?? root?.firstElementChild ?? null;
2325
+ if (!root || !target) {
2326
+ warnOnce("parallax: needs an element with a picture inside");
2327
+ return inert({ seek() {
2328
+ } });
2329
+ }
2330
+ const t = TIMING.parallax;
2331
+ const { amount = 1, lerp = t.lerp, scroll = true } = options;
2332
+ const phone = typeof matchMedia === "function" && matchMedia("(max-width: 767px)").matches;
2333
+ const scale = amount !== 1 && !phone ? t.strongScale : t.scale;
2334
+ registerMotion();
2335
+ const moving = !reduced();
2336
+ const originalTransform = target.style.transform;
2337
+ const originalOverflow = root.style.overflow;
2338
+ root.style.overflow = "clip";
2339
+ const render = (progress) => {
2340
+ const p = moving ? progress : 0.5;
2341
+ const y = Number((-t.travel * amount + p * 2 * t.travel * amount).toFixed(3));
2342
+ target.style.transform = `translateY(${y}%) scale(${scale})`;
2343
+ };
2344
+ let track = null;
2345
+ if (moving && scroll) {
2346
+ const top = root.getBoundingClientRect().top + (window.scrollY || 0);
2347
+ track = trackScroll(root, { start: top < 20 ? "top top" : "top bottom", lerp, onUpdate: render });
2348
+ } else render(0.5);
2349
+ root.dataset.mkReady = "";
2350
+ return {
2351
+ seek(progress) {
2352
+ if (track) track.set(progress);
2353
+ else render(progress);
2354
+ },
2355
+ destroy() {
2356
+ track?.destroy();
2357
+ target.style.transform = originalTransform;
2358
+ root.style.overflow = originalOverflow;
2359
+ for (const el of [target, root]) if (el.getAttribute("style") === "") el.removeAttribute("style");
2360
+ delete root.dataset.mkReady;
2361
+ }
2362
+ };
2363
+ }
2364
+
2365
+ // src/recipes/depth-grid/create-depth-grid.ts
2366
+ var STYLES2 = ["transform", "translate", "rotate", "scale", "opacity", "visibility"];
2367
+ function origin(child, box) {
2368
+ const t = TIMING.depthGrid;
2369
+ const own = child.getBoundingClientRect();
2370
+ const centre = { x: box.width / 2, y: box.height / 2 };
2371
+ const point = { x: own.left - box.left + own.width / 2, y: own.top - box.top + own.height / 2 };
2372
+ const angle = Math.atan2(Math.abs(centre.y - point.y), Math.abs(centre.x - point.x));
2373
+ const cos = Math.abs(Math.cos(angle));
2374
+ const sin = Math.abs(Math.sin(angle));
2375
+ const reach = Math.hypot(centre.x, centre.y) || 1;
2376
+ const h = Math.hypot(centre.x - point.x, centre.y - point.y) / reach;
2377
+ const left = point.x < centre.x;
2378
+ const above = point.y < centre.y;
2379
+ return {
2380
+ x: (left ? -1 : 1) * cos * t.distance,
2381
+ y: (above ? -1 : 1) * sin * t.distance,
2382
+ z: t.depth * h,
2383
+ rotateX: (above ? -1 : 1) * sin * t.rotate * h,
2384
+ rotateY: (left ? 1 : -1) * cos * t.rotate * h
2385
+ };
2386
+ }
2387
+ function createDepthGrid(root, options = {}) {
2388
+ const items = root ? [...root.children] : [];
2389
+ if (!root || !items.length) {
2390
+ warnOnce("depth-grid: needs a grid with children");
2391
+ return inert({ seek() {
2392
+ } });
2393
+ }
2394
+ const t = TIMING.depthGrid;
2395
+ const { start = "top center", end = "bottom bottom", lerp = t.lerp, scroll = true } = options;
2396
+ if (!entrances()) {
2397
+ root.dataset.mkReady = "";
2398
+ return {
2399
+ seek() {
2400
+ },
2401
+ destroy() {
2402
+ delete root.dataset.mkReady;
2403
+ }
2404
+ };
2405
+ }
2406
+ registerMotion();
2407
+ const perspective = root.style.perspective;
2408
+ root.style.perspective = `${t.perspective}px`;
2409
+ const box = root.getBoundingClientRect();
2410
+ const starts = items.map((item) => origin(item, box));
2411
+ const timeline = gsap.timeline({ paused: true, defaults: { ease: "none" } });
2412
+ timeline.fromTo(
2413
+ items,
2414
+ {
2415
+ x: (i) => starts[i].x,
2416
+ y: (i) => starts[i].y,
2417
+ z: (i) => starts[i].z,
2418
+ rotateX: (i) => starts[i].rotateX,
2419
+ rotateY: (i) => starts[i].rotateY,
2420
+ autoAlpha: t.opacity,
2421
+ scale: t.scale
2422
+ },
2423
+ {
2424
+ x: 0,
2425
+ y: 0,
2426
+ z: 0,
2427
+ rotateX: 0,
2428
+ rotateY: 0,
2429
+ autoAlpha: 1,
2430
+ scale: 1,
2431
+ duration: 1,
2432
+ stagger: { amount: t.stagger, from: "center", grid: "auto" }
2433
+ }
2434
+ );
2435
+ const render = (progress) => timeline.progress(progress);
2436
+ let track = null;
2437
+ if (scroll) track = trackScroll(root, { start, end, lerp, onUpdate: render });
2438
+ else render(0);
2439
+ root.dataset.mkReady = "";
2440
+ return {
2441
+ seek(progress) {
2442
+ if (track) track.set(progress);
2443
+ else render(progress);
2444
+ },
2445
+ destroy() {
2446
+ track?.destroy();
2447
+ timeline.kill();
2448
+ clearStyles(items, STYLES2);
2449
+ root.style.perspective = perspective;
2450
+ if (root.getAttribute("style") === "") root.removeAttribute("style");
2451
+ delete root.dataset.mkReady;
2452
+ }
2453
+ };
2454
+ }
2455
+
2456
+ // src/recipes/frame-zoom/create-frame-zoom.ts
2457
+ function createFrameZoom(root, options = {}) {
2458
+ const inner = root?.firstElementChild ?? null;
2459
+ if (!root || !inner) {
2460
+ warnOnce("frame-zoom: needs a frame with a picture inside");
2461
+ return inert({ seek() {
2462
+ }, open() {
2463
+ }, state: { progress: 0, open: 0 } });
2464
+ }
2465
+ const t = TIMING.frameZoom;
2466
+ const { start = "top bottom", end = "bottom top", lerp = t.lerp, scroll = true } = options;
2467
+ registerMotion();
2468
+ const phone = typeof matchMedia === "function" && matchMedia("(max-width: 767px)").matches;
2469
+ const [outerBase, outerRange] = phone ? t.phoneOuter : t.outer;
2470
+ const [innerBase, innerRange] = phone ? t.phoneInner : t.inner;
2471
+ const moving = !reduced();
2472
+ const state = { progress: 0, open: 0 };
2473
+ const originals = [root.style.transform, inner.style.transform, root.style.overflow];
2474
+ root.style.overflow = "clip";
2475
+ const render = () => {
2476
+ const e = moving ? (1 - state.progress) * (1 - state.open) + state.open : 1;
2477
+ root.style.transform = `scale(${Number(((outerBase + outerRange * e) / 100).toFixed(4))})`;
2478
+ inner.style.transform = `scale(${Number(((innerBase - innerRange * e) / 100).toFixed(4))})`;
2479
+ };
2480
+ let track = null;
2481
+ if (moving && scroll)
2482
+ track = trackScroll(root, {
2483
+ start,
2484
+ end,
2485
+ lerp,
2486
+ onUpdate: (value) => {
2487
+ state.progress = value;
2488
+ render();
2489
+ }
2490
+ });
2491
+ else render();
2492
+ root.dataset.mkReady = "";
2493
+ return {
2494
+ state,
2495
+ seek(progress) {
2496
+ if (track) track.set(progress);
2497
+ else {
2498
+ state.progress = Math.min(1, Math.max(0, progress));
2499
+ render();
2500
+ }
2501
+ },
2502
+ open(on) {
2503
+ gsap.killTweensOf(state);
2504
+ if (!moving) return;
2505
+ gsap.to(state, { open: on ? 1 : 0, duration: t.open, ease: "expo.out", onUpdate: render });
2506
+ },
2507
+ destroy() {
2508
+ track?.destroy();
2509
+ gsap.killTweensOf(state);
2510
+ root.style.transform = originals[0];
2511
+ inner.style.transform = originals[1];
2512
+ root.style.overflow = originals[2];
2513
+ for (const el of [root, inner]) if (el.getAttribute("style") === "") el.removeAttribute("style");
2514
+ delete root.dataset.mkReady;
2515
+ }
2516
+ };
2517
+ }
2518
+
2519
+ // src/recipes/contrast/create-contrast.ts
2520
+ function createContrast(target, options = {}) {
2521
+ if (!target) {
2522
+ warnOnce("contrast: missing element");
2523
+ return inert({ tone: null });
2524
+ }
2525
+ const { sections = "[data-mk-tone]", attribute = "data-mk-on" } = options;
2526
+ const scope = createScope();
2527
+ const watched = [...document.querySelectorAll(sections)];
2528
+ const under = /* @__PURE__ */ new Set();
2529
+ let tone = target.getAttribute(attribute);
2530
+ const apply = () => {
2531
+ const current = [...under].at(-1);
2532
+ if (!current) return;
2533
+ tone = current.dataset.mkTone ?? null;
2534
+ scope.attr(target, attribute, tone);
2535
+ };
2536
+ if (typeof IntersectionObserver === "function" && watched.length) {
2537
+ const observer = scope.observe(
2538
+ new IntersectionObserver(
2539
+ (entries) => {
2540
+ for (const entry of entries) {
2541
+ const section = entry.target;
2542
+ under.delete(section);
2543
+ if (entry.isIntersecting) under.add(section);
2544
+ }
2545
+ apply();
2546
+ },
2547
+ { rootMargin: "0% 0% -90% 0%", threshold: 0 }
2548
+ )
2549
+ );
2550
+ for (const section of watched) observer.observe(section);
2551
+ }
2552
+ target.dataset.mkReady = "";
2553
+ return {
2554
+ get tone() {
2555
+ return tone;
2556
+ },
2557
+ destroy() {
2558
+ scope.dispose();
2559
+ delete target.dataset.mkReady;
2560
+ }
2561
+ };
2562
+ }
2563
+
2564
+ // src/recipes/marquee/create-marquee.ts
2565
+ function createMarquee(root, options = {}) {
2566
+ const group = root?.firstElementChild ?? null;
2567
+ if (!root || !group) {
2568
+ warnOnce("marquee: needs a group inside");
2569
+ return inert({ refresh() {
2570
+ } });
2571
+ }
2572
+ const t = TIMING.marquee;
2573
+ const { axis = "x", speed = t.perItem, reverse = false } = options;
2574
+ const scope = createScope();
2575
+ const classes = ["mk-marquee", `mk-marquee--${axis}`, reverse && "mk-marquee--reverse"].filter(Boolean).join(" ");
2576
+ scope.attr(root, "class", [root.getAttribute("class"), classes].filter(Boolean).join(" "));
2577
+ const items = Math.min(group.children.length || 1, t.maxItems);
2578
+ root.style.setProperty("--mk-marquee-duration", `${items * speed}s`);
2579
+ scope.add(() => {
2580
+ root.style.removeProperty("--mk-marquee-duration");
2581
+ if (root.getAttribute("style") === "") root.removeAttribute("style");
2582
+ });
2583
+ let copies = [];
2584
+ const refresh = () => {
2585
+ for (const copy of copies.splice(0)) copy.remove();
2586
+ if (!entrances()) return;
2587
+ const size = axis === "x" ? group.offsetWidth : group.offsetHeight;
2588
+ const room = axis === "x" ? root.clientWidth : root.clientHeight;
2589
+ const count = size > 0 ? Math.max(1, Math.ceil(room / size)) + 1 : 5;
2590
+ for (let i = 0; i < count; i++) {
2591
+ const copy = group.cloneNode(true);
2592
+ copy.setAttribute("aria-hidden", "true");
2593
+ copy.inert = true;
2594
+ for (const el of copy.querySelectorAll("[id]")) el.removeAttribute("id");
2595
+ root.append(copy);
2596
+ copies.push(copy);
2597
+ }
2598
+ };
2599
+ refresh();
2600
+ scope.add(() => {
2601
+ for (const copy of copies.splice(0)) copy.remove();
2602
+ });
2603
+ root.dataset.mkReady = "";
2604
+ return {
2605
+ refresh,
2606
+ destroy() {
2607
+ scope.dispose();
2608
+ delete root.dataset.mkReady;
2609
+ }
2610
+ };
2611
+ }
2612
+
2613
+ // src/recipes/card-dock/create-card-dock.ts
2614
+ var TABLET_TOUCH = "(min-width: 768px) and (max-width: 1024px) and (hover: none)";
2615
+ function createCardDock(root, options = {}) {
2616
+ if (!root) {
2617
+ warnOnce("card-dock: missing element");
2618
+ return inert({ expand() {
2619
+ }, collapse() {
2620
+ }, expanded: false });
2621
+ }
2622
+ const t = TIMING.cardDock;
2623
+ const intro = Math.max(0, options.intro ?? t.intro);
2624
+ const scope = createScope();
2625
+ let expanded = false;
2626
+ let resizeTimer;
2627
+ const set = (name, on) => scope.attr(root, name, on ? "" : null);
2628
+ const expand = () => {
2629
+ if (scope.disposed) return;
2630
+ expanded = true;
2631
+ set("data-mk-expanded", true);
2632
+ };
2633
+ const collapse = () => {
2634
+ if (scope.disposed) return;
2635
+ expanded = false;
2636
+ set("data-mk-expanded", false);
2637
+ };
2638
+ const settle = () => {
2639
+ set("data-mk-spread", false);
2640
+ set("data-mk-settled", true);
2641
+ };
2642
+ const tablet = () => typeof matchMedia === "function" && matchMedia(TABLET_TOUCH).matches;
2643
+ if (intro > 0 && !reduced()) {
2644
+ set("data-mk-spread", true);
2645
+ scope.timeout(settle, intro);
2646
+ } else settle();
2647
+ scope.on(
2648
+ root,
2649
+ "click",
2650
+ (event) => {
2651
+ if (expanded || !tablet()) return;
2652
+ event.preventDefault();
2653
+ event.stopPropagation();
2654
+ expand();
2655
+ },
2656
+ true
2657
+ );
2658
+ scope.on(document, "click", (event) => {
2659
+ if (expanded && !root.contains(event.target)) collapse();
2660
+ });
2661
+ scope.on(document, "keydown", (event) => {
2662
+ if (expanded && event.key === "Escape") collapse();
2663
+ });
2664
+ scope.on(window, "resize", () => {
2665
+ clearTimeout(resizeTimer);
2666
+ resizeTimer = setTimeout(() => {
2667
+ if (!tablet()) collapse();
2668
+ }, t.resizeDebounce);
2669
+ });
2670
+ scope.add(() => clearTimeout(resizeTimer));
2671
+ scope.attr(root, "data-mk-ready", "");
2672
+ return {
2673
+ expand,
2674
+ collapse,
2675
+ get expanded() {
2676
+ return expanded;
2677
+ },
2678
+ destroy: () => scope.dispose()
2679
+ };
2680
+ }
2681
+
2682
+ // src/recipes/snap-strip/create-snap-strip.ts
2683
+ function createSnapStrip(root, options = {}) {
2684
+ if (!root || root.children.length < 2) {
2685
+ warnOnce("snap-strip: needs a strip with at least two cards");
2686
+ return inert({ next() {
2687
+ }, previous() {
2688
+ }, index: 0 });
2689
+ }
2690
+ const t = TIMING.snapStrip;
2691
+ const { autoplay = true, interval = t.interval, resume = t.resume, delay = t.delay } = options;
2692
+ const scope = createScope();
2693
+ const items = () => [...root.children];
2694
+ const startScroll = root.scrollLeft;
2695
+ let timer;
2696
+ let waiting;
2697
+ let visible = typeof IntersectionObserver !== "function";
2698
+ let touching = false;
2699
+ const offsetOf = (item) => item.getBoundingClientRect().left - root.getBoundingClientRect().left;
2700
+ const nearest = () => {
2701
+ let best = 0;
2702
+ items().forEach((item, i, all) => {
2703
+ if (Math.abs(offsetOf(item)) < Math.abs(offsetOf(all[best]))) best = i;
2704
+ });
2705
+ return best;
2706
+ };
2707
+ const move = (step) => {
2708
+ if (scope.disposed) return;
2709
+ const all = items();
2710
+ const target = all[(nearest() + step + all.length) % all.length];
2711
+ root.scrollTo({ left: offsetOf(target) + root.scrollLeft, behavior: reduced() ? "instant" : "smooth" });
2712
+ };
2713
+ const stop = () => {
2714
+ clearInterval(timer);
2715
+ timer = void 0;
2716
+ };
2717
+ const start = () => {
2718
+ if (scope.disposed || !autoplay || reduced() || !visible || touching || document.hidden || timer) return;
2719
+ timer = setInterval(() => move(1), interval);
2720
+ };
2721
+ const later = (fn, ms) => {
2722
+ clearTimeout(waiting);
2723
+ waiting = setTimeout(fn, ms);
2724
+ };
2725
+ scope.add(() => {
2726
+ stop();
2727
+ clearTimeout(waiting);
2728
+ });
2729
+ const hold = () => {
2730
+ touching = true;
2731
+ stop();
2732
+ };
2733
+ const release = () => {
2734
+ if (!touching) return;
2735
+ later(() => {
2736
+ touching = false;
2737
+ start();
2738
+ }, resume);
2739
+ };
2740
+ scope.on(root, "pointerdown", hold, { passive: true });
2741
+ scope.on(root, "touchstart", hold, { passive: true });
2742
+ scope.on(window, "pointerup", release, { passive: true });
2743
+ scope.on(window, "pointercancel", release, { passive: true });
2744
+ scope.on(window, "touchend", release, { passive: true });
2745
+ scope.on(
2746
+ root,
2747
+ "wheel",
2748
+ () => {
2749
+ hold();
2750
+ release();
2751
+ },
2752
+ { passive: true }
2753
+ );
2754
+ scope.on(document, "visibilitychange", () => {
2755
+ stop();
2756
+ start();
2757
+ });
2758
+ if (typeof IntersectionObserver === "function") {
2759
+ const observer = scope.observe(
2760
+ new IntersectionObserver(([entry]) => {
2761
+ visible = Boolean(entry?.isIntersecting);
2762
+ stop();
2763
+ if (visible) later(start, delay);
2764
+ else clearTimeout(waiting);
2765
+ })
2766
+ );
2767
+ observer.observe(root);
2768
+ } else later(start, delay);
2769
+ scope.add(() => {
2770
+ root.scrollLeft = startScroll;
2771
+ });
2772
+ scope.attr(root, "data-mk-ready", "");
2773
+ return {
2774
+ next: () => move(1),
2775
+ previous: () => move(-1),
2776
+ get index() {
2777
+ return nearest();
2778
+ },
2779
+ destroy: () => scope.dispose()
2780
+ };
2781
+ }
2782
+
2783
+ // src/recipes/fill-text/create-fill-text.ts
2784
+ function createFillText(el, options = {}) {
2785
+ if (!el) {
2786
+ warnOnce("fill-text: missing element");
2787
+ return inert({ seek() {
2788
+ }, progress: 1 });
2789
+ }
2790
+ const t = TIMING.fillText;
2791
+ const { from = t.from, start = t.start, end = t.end, scroll = true } = options;
2792
+ registerMotion();
2793
+ if (reduced()) {
2794
+ el.dataset.mkReady = "";
2795
+ return {
2796
+ seek() {
2797
+ },
2798
+ progress: 1,
2799
+ destroy() {
2800
+ delete el.dataset.mkReady;
2801
+ }
2802
+ };
2803
+ }
2804
+ let timeline = null;
2805
+ let progress = 0;
2806
+ const build2 = (chars) => {
2807
+ timeline?.kill();
2808
+ timeline = gsap.timeline({ paused: true }).fromTo(chars, { opacity: from }, { opacity: 1, ease: "none", duration: t.duration, stagger: t.stagger });
2809
+ timeline.progress(progress);
2810
+ };
2811
+ const split = SplitText.create(el, {
2812
+ type: "words,chars",
2813
+ aria: "auto",
2814
+ autoSplit: true,
2815
+ wordsClass: "mk-fill-word",
2816
+ charsClass: "mk-fill-char",
2817
+ // A new split (resize, fonts) keeps the place it was at.
2818
+ onSplit: (self) => build2(self.chars)
2819
+ });
2820
+ if (!timeline) build2(split.chars);
2821
+ const render = (value) => {
2822
+ progress = value;
2823
+ timeline?.progress(value);
2824
+ };
2825
+ let track = scroll ? trackScroll(el, { start, end, onUpdate: render }) : null;
2826
+ if (!track) render(0);
2827
+ el.dataset.mkReady = "";
2828
+ return {
2829
+ seek(value) {
2830
+ if (track) track.set(value);
2831
+ else render(Math.min(1, Math.max(0, value)));
2832
+ },
2833
+ get progress() {
2834
+ return progress;
2835
+ },
2836
+ destroy() {
2837
+ track?.destroy();
2838
+ track = null;
2839
+ timeline?.kill();
2840
+ split.revert();
2841
+ delete el.dataset.mkReady;
2842
+ }
2843
+ };
2844
+ }
2845
+
2846
+ // src/recipes/ticket-menu/create-ticket-menu.ts
2847
+ var createTicketMenu = (root, options = {}) => createMenu(root, { ...options, ticket: true });
2848
+
2849
+ // src/recipes/infinite-reel/create-infinite-reel.ts
2850
+ function createInfiniteReel(root, options = {}) {
2851
+ const track = root?.querySelector("[data-mk-reel-track]");
2852
+ if (!root || !track) return inert({ moveBy() {
2853
+ }, pause() {
2854
+ }, resume() {
2855
+ }, position: 0 });
2856
+ const scope = createScope();
2857
+ const still = reduced();
2858
+ const vertical = options.axis === "y";
2859
+ scope.attr(root, "data-mk-axis", vertical ? "y" : "x");
2860
+ scope.attr(root, "tabindex", root.getAttribute("tabindex") ?? "0");
2861
+ scope.attr(track, "style", track.getAttribute("style"));
2862
+ const copies = [];
2863
+ let length = 0;
2864
+ let position = 0;
2865
+ let velocity = 0;
2866
+ let paused = false;
2867
+ let focused = false;
2868
+ let drag = null;
2869
+ let suppressClick = false;
2870
+ const coordinate = (event) => vertical ? event.clientY : event.clientX;
2871
+ const draw = () => {
2872
+ if (still) {
2873
+ if (vertical) root.scrollTop = position;
2874
+ else root.scrollLeft = position;
2875
+ return;
2876
+ }
2877
+ [track, ...copies].forEach((group, index) => {
2878
+ const offset = index * length - position;
2879
+ group.style.transform = vertical ? `translateY(${offset}px)` : `translateX(${offset}px)`;
2880
+ });
2881
+ };
2882
+ const measure = () => {
2883
+ copies.splice(0).forEach((copy) => copy.remove());
2884
+ const rect = track.getBoundingClientRect();
2885
+ length = vertical ? track.scrollHeight || rect.height : track.scrollWidth || rect.width;
2886
+ if (!still && length > 0) {
2887
+ const viewport = root.getBoundingClientRect();
2888
+ const count = Math.min(30, Math.ceil((vertical ? viewport.height : viewport.width) / length) + 1);
2889
+ for (let index = 0; index < count; index++) {
2890
+ const copy = track.cloneNode(true);
2891
+ copy.removeAttribute("id");
2892
+ copy.removeAttribute("data-mk-reel-track");
2893
+ copy.removeAttribute("data-mk");
2894
+ copy.setAttribute("data-mk-reel-copy", "");
2895
+ copy.setAttribute("aria-hidden", "true");
2896
+ copy.setAttribute("inert", "");
2897
+ copy.querySelectorAll("[id]").forEach((child) => child.removeAttribute("id"));
2898
+ copy.querySelectorAll("[data-mk]").forEach((child) => child.removeAttribute("data-mk"));
2899
+ root.append(copy);
2900
+ copies.push(copy);
2901
+ }
2902
+ }
2903
+ position = length > 0 ? wrap(position, length) : 0;
2904
+ draw();
2905
+ };
2906
+ const moveBy = (pixels) => {
2907
+ if (scope.disposed || !Number.isFinite(pixels) || length <= 0) return;
2908
+ position = wrap(position + pixels, length);
2909
+ draw();
2910
+ };
2911
+ scope.on(root, "wheel", (event) => {
2912
+ if (still || !length) return;
2913
+ event.preventDefault();
2914
+ const units = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? vertical ? root.clientHeight : root.clientWidth : 1;
2915
+ velocity += (vertical ? event.deltaY : event.deltaX || event.deltaY) * units * TIMING.infiniteReel.wheel;
2916
+ }, { passive: false });
2917
+ scope.on(root, "pointerdown", (event) => {
2918
+ if (event.button !== 0 || drag || still) return;
2919
+ velocity = 0;
2920
+ suppressClick = false;
2921
+ drag = { point: coordinate(event), id: event.pointerId, moved: 0 };
2922
+ });
2923
+ scope.on(window, "pointermove", (event) => {
2924
+ if (!drag || event.pointerId !== drag.id) return;
2925
+ const delta = drag.point - coordinate(event);
2926
+ drag.moved += Math.abs(delta);
2927
+ drag.point = coordinate(event);
2928
+ if (drag.moved > TIMING.infiniteReel.dragThreshold) {
2929
+ suppressClick = true;
2930
+ root.setPointerCapture?.(event.pointerId);
2931
+ moveBy(delta);
2932
+ velocity = delta;
2933
+ }
2934
+ });
2935
+ const release = (event) => {
2936
+ if (!drag || drag.id !== event.pointerId) return;
2937
+ drag = null;
2938
+ if (root.hasPointerCapture?.(event.pointerId)) root.releasePointerCapture(event.pointerId);
2939
+ };
2940
+ scope.on(window, "pointerup", release);
2941
+ scope.on(window, "pointercancel", (event) => {
2942
+ velocity = 0;
2943
+ release(event);
2944
+ });
2945
+ scope.on(root, "lostpointercapture", release);
2946
+ scope.on(root, "click", (event) => {
2947
+ if (suppressClick) {
2948
+ event.preventDefault();
2949
+ event.stopPropagation();
2950
+ suppressClick = false;
2951
+ }
2952
+ }, true);
2953
+ scope.on(root, "focusin", () => {
2954
+ focused = true;
2955
+ });
2956
+ scope.on(root, "focusout", (event) => {
2957
+ focused = root.contains(event.relatedTarget);
2958
+ });
2959
+ scope.on(root, "keydown", (event) => {
2960
+ if (event.target !== root) return;
2961
+ const next = vertical ? "ArrowDown" : "ArrowRight";
2962
+ const previous = vertical ? "ArrowUp" : "ArrowLeft";
2963
+ if (![next, previous, " "].includes(event.key)) return;
2964
+ event.preventDefault();
2965
+ moveBy((event.key === previous ? -1 : 1) * length / Math.max(1, track.children.length));
2966
+ });
2967
+ if (typeof ResizeObserver === "function") {
2968
+ const observer = scope.observe(new ResizeObserver(measure));
2969
+ observer.observe(root);
2970
+ observer.observe(track);
2971
+ }
2972
+ scope.on(window, "resize", measure);
2973
+ scope.on(track, "load", measure, true);
2974
+ const scroll = { left: root.scrollLeft, top: root.scrollTop };
2975
+ scope.add(() => {
2976
+ copies.forEach((copy) => copy.remove());
2977
+ if (drag && root.hasPointerCapture?.(drag.id)) root.releasePointerCapture(drag.id);
2978
+ root.scrollLeft = scroll.left;
2979
+ root.scrollTop = scroll.top;
2980
+ });
2981
+ measure();
2982
+ if (!still) scope.add(whileVisible(root, (dt) => {
2983
+ if (paused || focused || drag || document.hidden) return;
2984
+ moveBy(((options.speed ?? 0) + velocity) * dt);
2985
+ velocity *= Math.pow(Math.max(0, Math.min(0.999, options.friction ?? TIMING.infiniteReel.friction)), dt);
2986
+ }));
2987
+ return { moveBy, pause() {
2988
+ paused = true;
2989
+ }, resume() {
2990
+ paused = false;
2991
+ }, get position() {
2992
+ return position;
2993
+ }, destroy: () => scope.dispose() };
2994
+ }
2995
+
2996
+ // src/recipes/repel-bento/create-repel-bento.ts
2997
+ function createRepelBento(root, options = {}) {
2998
+ if (!root || reduced()) return inert({ play() {
2999
+ }, reset() {
3000
+ } });
3001
+ registerMotion();
3002
+ const scope = createScope();
3003
+ const items = [...root.children];
3004
+ const animations = /* @__PURE__ */ new Map();
3005
+ const defaults = TIMING.repelBento;
3006
+ const enabled = () => innerWidth >= (options.breakpoint ?? defaults.breakpoint) && !matchMedia("(pointer: coarse)").matches;
3007
+ items.forEach((item) => scope.attr(item, "style", item.getAttribute("style")));
3008
+ const animate = (item, vars) => {
3009
+ animations.get(item)?.kill();
3010
+ animations.set(item, gsap.to(item, { ...vars, overwrite: true }));
3011
+ };
3012
+ const reset = () => {
3013
+ if (scope.disposed) return;
3014
+ items.forEach((item) => animate(item, { x: 0, y: 0, scale: 1, opacity: 1, ...motionSpring(...TIMING.springs.bentoRepel) }));
3015
+ };
3016
+ const play = () => {
3017
+ if (scope.disposed || !enabled()) return;
3018
+ items.forEach((item, index) => {
3019
+ const rect = item.getBoundingClientRect();
3020
+ gsap.set(item, { x: (rect.left + rect.width / 2 - innerWidth / 2) * defaults.offset, y: (rect.top + rect.height / 2 - innerHeight / 2) * defaults.offset, opacity: 0 });
3021
+ animate(item, { x: 0, y: 0, opacity: 1, delay: index * defaults.stagger, ...motionSpring(...TIMING.springs.bentoEnter) });
3022
+ });
3023
+ };
3024
+ items.forEach((hovered) => scope.on(hovered, "pointerenter", () => {
3025
+ if (!enabled()) return;
3026
+ const origin2 = hovered.getBoundingClientRect();
3027
+ items.forEach((item) => {
3028
+ if (item === hovered) {
3029
+ animate(item, { x: 0, y: 0, scale: 1.02, opacity: 1, ...motionSpring(...TIMING.springs.bentoRepel) });
3030
+ return;
3031
+ }
3032
+ const rect = item.getBoundingClientRect();
3033
+ const deltaX = rect.left + rect.width / 2 - origin2.left - origin2.width / 2;
3034
+ const deltaY = rect.top + rect.height / 2 - origin2.top - origin2.height / 2;
3035
+ const distance = Math.hypot(deltaX, deltaY);
3036
+ const magnitude = distance > 0 ? Math.min(defaults.strength / distance, options.distance ?? defaults.distance) : 0;
3037
+ animate(item, { x: distance ? deltaX / distance * magnitude : 0, y: distance ? deltaY / distance * magnitude : 0, scale: 1, opacity: 1, ...motionSpring(...TIMING.springs.bentoRepel) });
3038
+ });
3039
+ }));
3040
+ scope.on(root, "pointerleave", reset);
3041
+ scope.on(window, "resize", reset);
3042
+ scope.add(() => {
3043
+ animations.forEach((animation) => animation.kill());
3044
+ clearStyles(items, ["transform", "translate", "rotate", "scale", "opacity"]);
3045
+ });
3046
+ if (typeof IntersectionObserver === "function") {
3047
+ const observer = scope.observe(new IntersectionObserver(([entry]) => {
3048
+ if (!entry?.isIntersecting) return;
3049
+ play();
3050
+ observer.disconnect();
3051
+ }, { threshold: 0.15 }));
3052
+ observer.observe(root);
3053
+ } else play();
3054
+ return { play, reset, destroy: () => scope.dispose() };
3055
+ }
3056
+
3057
+ // src/recipes/rotator/create-rotator.ts
3058
+ function createRotator(root, options = {}) {
3059
+ const items = root ? [...root.children] : [];
3060
+ if (!root || !items.length) return inert({ select() {
3061
+ }, next() {
3062
+ }, previous() {
3063
+ }, index: 0 });
3064
+ registerMotion();
3065
+ const scope = createScope();
3066
+ const still = reduced();
3067
+ let index = 0;
3068
+ let hovered = false;
3069
+ let focused = false;
3070
+ let timer;
3071
+ let animation;
3072
+ for (const item of items) scope.attr(item, "style", item.getAttribute("style"));
3073
+ const select = (next, notify = true) => {
3074
+ if (scope.disposed || !Number.isFinite(next)) return;
3075
+ index = wrap(Math.round(next), items.length);
3076
+ animation?.kill();
3077
+ for (const [position, item] of items.entries()) {
3078
+ scope.attr(item, "hidden", position === index ? null : "");
3079
+ scope.attr(item, "inert", position === index ? null : "");
3080
+ }
3081
+ if (!still) animation = gsap.fromTo(items[index], { opacity: 0, y: TIMING.rotator.y }, { opacity: 1, y: 0, duration: TIMING.rotator.duration, overwrite: true });
3082
+ if (notify) {
3083
+ options.onChange?.(index);
3084
+ root.dispatchEvent(new CustomEvent("mk:change", { bubbles: true, detail: { index } }));
3085
+ }
3086
+ };
3087
+ const restart = () => {
3088
+ clearInterval(timer);
3089
+ if (scope.disposed || still || options.autoplay === false || hovered || focused || document.hidden || items.length < 2) return;
3090
+ timer = setInterval(() => select(index + 1), Math.max(100, options.interval ?? TIMING.rotator.interval));
3091
+ };
3092
+ const move = (next) => {
3093
+ select(next);
3094
+ restart();
3095
+ };
3096
+ scope.on(root, "pointerenter", () => {
3097
+ hovered = true;
3098
+ restart();
3099
+ });
3100
+ scope.on(root, "pointerleave", () => {
3101
+ hovered = false;
3102
+ restart();
3103
+ });
3104
+ scope.on(root, "focusin", () => {
3105
+ focused = true;
3106
+ restart();
3107
+ });
3108
+ scope.on(root, "focusout", (event) => {
3109
+ focused = root.contains(event.relatedTarget);
3110
+ restart();
3111
+ });
3112
+ scope.on(document, "visibilitychange", restart);
3113
+ scope.add(() => {
3114
+ clearInterval(timer);
3115
+ animation?.kill();
3116
+ clearStyles(items, ["transform", "translate", "rotate", "scale", "opacity"]);
3117
+ });
3118
+ select(options.index ?? 0, false);
3119
+ restart();
3120
+ return { select: move, next: () => move(index + 1), previous: () => move(index - 1), get index() {
3121
+ return index;
3122
+ }, destroy: () => scope.dispose() };
3123
+ }
3124
+
3125
+ // src/recipes/drag-ruler/create-drag-ruler.ts
3126
+ function createDragRuler(root, options = {}) {
3127
+ if (!root) return inert({ set() {
3128
+ }, value: 0 });
3129
+ const scope = createScope();
3130
+ const defaults = TIMING.dragRuler;
3131
+ const min = Number.isFinite(options.min) ? options.min : defaults.min;
3132
+ const max = Math.max(min, Number.isFinite(options.max) ? options.max : defaults.max);
3133
+ const step = Number.isFinite(options.step) && options.step > 0 ? options.step : defaults.step;
3134
+ const pixels = Number.isFinite(options.pixelsPerUnit) && options.pixelsPerUnit > 0 ? options.pixelsPerUnit : defaults.pixelsPerUnit;
3135
+ let value = min;
3136
+ let drag = null;
3137
+ const track = document.createElement("span");
3138
+ track.className = "mk-drag-ruler__track";
3139
+ track.setAttribute("aria-hidden", "true");
3140
+ const count = Math.min(500, Math.ceil((max - min) / step));
3141
+ for (let index = 0; index <= count; index++) {
3142
+ const tick = document.createElement("span");
3143
+ const tickValue = min + index * ((max - min) / Math.max(1, count));
3144
+ tick.className = "mk-drag-ruler__tick";
3145
+ tick.style.left = `${(tickValue - min) * pixels}px`;
3146
+ if (index % 10 === 0) tick.textContent = String(Number(tickValue.toFixed(2)));
3147
+ track.append(tick);
3148
+ }
3149
+ root.append(track);
3150
+ scope.add(() => track.remove());
3151
+ scope.attr(root, "role", "slider");
3152
+ scope.attr(root, "tabindex", root.getAttribute("tabindex") ?? "0");
3153
+ scope.attr(root, "aria-orientation", "horizontal");
3154
+ scope.attr(root, "aria-valuemin", String(min));
3155
+ scope.attr(root, "aria-valuemax", String(max));
3156
+ const set = (next, notify = true) => {
3157
+ if (scope.disposed || !Number.isFinite(next)) return;
3158
+ const snapped = clamp(Number((min + Math.round((next - min) / step) * step).toFixed(8)), min, max);
3159
+ const changed = snapped !== value;
3160
+ value = snapped;
3161
+ scope.attr(root, "aria-valuenow", String(value));
3162
+ track.style.transform = `translateX(${-(value - min) * pixels - 3}px)`;
3163
+ if (changed && notify) {
3164
+ options.onChange?.(value);
3165
+ root.dispatchEvent(new CustomEvent("mk:change", { bubbles: true, detail: { value } }));
3166
+ }
3167
+ };
3168
+ scope.on(root, "keydown", (event) => {
3169
+ const keys = {
3170
+ ArrowLeft: value - step,
3171
+ ArrowDown: value - step,
3172
+ ArrowRight: value + step,
3173
+ ArrowUp: value + step,
3174
+ PageDown: value - step * 10,
3175
+ PageUp: value + step * 10,
3176
+ Home: min,
3177
+ End: max
3178
+ };
3179
+ if (!(event.key in keys)) return;
3180
+ event.preventDefault();
3181
+ set(keys[event.key]);
3182
+ });
3183
+ scope.on(root, "pointerdown", (event) => {
3184
+ if (event.button !== 0 || drag) return;
3185
+ drag = { x: event.clientX, value, id: event.pointerId };
3186
+ root.setPointerCapture?.(event.pointerId);
3187
+ scope.attr(root, "data-mk-dragging", "");
3188
+ root.focus({ preventScroll: true });
3189
+ });
3190
+ scope.on(window, "pointermove", (event) => {
3191
+ if (!drag || event.pointerId !== drag.id) return;
3192
+ set(drag.value - (event.clientX - drag.x) / pixels);
3193
+ });
3194
+ const release = (event) => {
3195
+ if (!drag || event.pointerId !== drag.id) return;
3196
+ if (root.hasPointerCapture?.(drag.id)) root.releasePointerCapture(drag.id);
3197
+ drag = null;
3198
+ scope.attr(root, "data-mk-dragging", null);
3199
+ };
3200
+ scope.on(window, "pointerup", release);
3201
+ scope.on(window, "pointercancel", release);
3202
+ scope.on(root, "lostpointercapture", release);
3203
+ scope.add(() => {
3204
+ if (drag && root.hasPointerCapture?.(drag.id)) root.releasePointerCapture(drag.id);
3205
+ });
3206
+ set(options.value ?? max, false);
3207
+ return { set, get value() {
3208
+ return value;
3209
+ }, destroy: () => scope.dispose() };
3210
+ }
3211
+
3212
+ // src/recipes/dock-pill/create-dock-pill.ts
3213
+ function createDockPill(root, options = {}) {
3214
+ const items = root ? [...root.querySelectorAll("[data-mk-dock-item]")] : [];
3215
+ if (!root || !items.length) return inert({ select() {
3216
+ }, index: 0 });
3217
+ registerMotion();
3218
+ const scope = createScope();
3219
+ const indicator = document.createElement("span");
3220
+ indicator.className = "mk-dock-pill__indicator";
3221
+ indicator.setAttribute("aria-hidden", "true");
3222
+ root.prepend(indicator);
3223
+ scope.add(() => indicator.remove());
3224
+ let index = Math.max(0, items.findIndex((item) => item.getAttribute("aria-current") === "page"));
3225
+ let animation;
3226
+ const measure = (animate = true) => {
3227
+ if (scope.disposed) return;
3228
+ const item = items[index];
3229
+ const target = { x: item.offsetLeft, y: item.offsetTop, width: item.offsetWidth, height: item.offsetHeight };
3230
+ animation?.kill();
3231
+ if (animate && !reduced()) animation = gsap.to(indicator, { ...target, ...motionSpring(...TIMING.springs.dockPill), overwrite: true });
3232
+ else gsap.set(indicator, target);
3233
+ };
3234
+ const select = (next, notify = true) => {
3235
+ if (scope.disposed || !Number.isFinite(next)) return;
3236
+ const candidate = wrap(Math.round(next), items.length);
3237
+ if (items[candidate].matches(':disabled, [aria-disabled="true"]')) return;
3238
+ index = candidate;
3239
+ items.forEach((item, position) => scope.attr(item, "aria-current", position === index ? "page" : null));
3240
+ measure(notify);
3241
+ if (notify) {
3242
+ options.onSelect?.(index, items[index]);
3243
+ root.dispatchEvent(new CustomEvent("mk:select", { bubbles: true, detail: { index, item: items[index] } }));
3244
+ }
3245
+ };
3246
+ scope.on(root, "click", (event) => {
3247
+ const item = event.target.closest("[data-mk-dock-item]");
3248
+ if (item && items.includes(item)) select(items.indexOf(item));
3249
+ });
3250
+ scope.on(root, "keydown", (event) => {
3251
+ const current = items.indexOf(document.activeElement);
3252
+ if (current < 0) return;
3253
+ const direction = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
3254
+ if (!direction && event.key !== "Home" && event.key !== "End") return;
3255
+ event.preventDefault();
3256
+ let next = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : wrap(current + direction, items.length);
3257
+ for (let tries = 0; tries < items.length && items[next].matches(':disabled, [aria-disabled="true"]'); tries++) next = wrap(next + (direction || 1), items.length);
3258
+ select(next);
3259
+ items[index].focus();
3260
+ });
3261
+ if (typeof ResizeObserver === "function") {
3262
+ const observer = scope.observe(new ResizeObserver(() => measure(false)));
3263
+ observer.observe(root);
3264
+ items.forEach((item) => observer.observe(item));
3265
+ }
3266
+ scope.on(window, "resize", () => measure(false));
3267
+ scope.add(() => animation?.kill());
3268
+ select(options.index ?? index, false);
3269
+ return { select, get index() {
3270
+ return index;
3271
+ }, destroy: () => scope.dispose() };
3272
+ }
3273
+
3274
+ export {
3275
+ trackScroll,
3276
+ toggleDetails,
3277
+ installDetailsMotion,
3278
+ slideIndicator,
3279
+ captureLayout,
3280
+ reflowLayout,
3281
+ createSpring,
3282
+ createSpringCards,
3283
+ createCurvedCarousel,
3284
+ pileLayers,
3285
+ pileStyle,
3286
+ createPile,
3287
+ createWords,
3288
+ createFlow,
3289
+ createPop,
3290
+ createFitText,
3291
+ createStretchFooter,
3292
+ createRadialMenu,
3293
+ createTabs,
3294
+ createCounter,
3295
+ createDetails,
3296
+ createReveal,
3297
+ createReflow,
3298
+ createSlotText,
3299
+ createSortable,
3300
+ moveItem,
3301
+ createMaskText,
3302
+ createCascade,
3303
+ createTextRoll,
3304
+ createLetters,
3305
+ createParallax,
3306
+ createDepthGrid,
3307
+ createFrameZoom,
3308
+ createContrast,
3309
+ createMarquee,
3310
+ createCardDock,
3311
+ createSnapStrip,
3312
+ createFillText,
3313
+ createTicketMenu,
3314
+ createInfiniteReel,
3315
+ createRepelBento,
3316
+ createRotator,
3317
+ createDragRuler,
3318
+ createDockPill
3319
+ };
3320
+ //# sourceMappingURL=chunk-FS2HJVVY.js.map