@yewdiel21/motion-kit 0.9.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/README.md +72 -0
  2. package/dist/auto/index.d.ts +72 -3
  3. package/dist/auto/index.js +139 -3
  4. package/dist/auto/index.js.map +1 -1
  5. package/dist/{chunk-BFC27F2G.js → chunk-6P4P7NU6.js} +2 -218
  6. package/dist/chunk-6P4P7NU6.js.map +1 -0
  7. package/dist/chunk-CU6SXW3E.js +20334 -0
  8. package/dist/chunk-CU6SXW3E.js.map +1 -0
  9. package/dist/{chunk-XDWBVFHQ.js → chunk-EY4BJKC4.js} +1203 -29
  10. package/dist/chunk-EY4BJKC4.js.map +1 -0
  11. package/dist/chunk-QP4QJOEN.js +72 -0
  12. package/dist/chunk-QP4QJOEN.js.map +1 -0
  13. package/dist/css/components/activity-feed.css +240 -0
  14. package/dist/css/components/attachment.css +266 -0
  15. package/dist/css/components/breadcrumb.css +110 -0
  16. package/dist/css/components/calendar.css +159 -0
  17. package/dist/css/components/chart-tooltip.css +168 -0
  18. package/dist/css/components/chart.css +296 -0
  19. package/dist/css/components/code-block.css +264 -0
  20. package/dist/css/components/color-area.css +82 -0
  21. package/dist/css/components/color-field.css +67 -0
  22. package/dist/css/components/color-picker.css +131 -0
  23. package/dist/css/components/color-slider.css +113 -0
  24. package/dist/css/components/command-search.css +252 -0
  25. package/dist/css/components/context-menu.css +23 -0
  26. package/dist/css/components/credit-card.css +288 -0
  27. package/dist/css/components/data-table.css +591 -0
  28. package/dist/css/components/date-range-picker.css +168 -0
  29. package/dist/css/components/date-time-picker.css +178 -0
  30. package/dist/css/components/drawer.css +58 -0
  31. package/dist/css/components/editable-table.css +201 -0
  32. package/dist/css/components/empty-state.css +61 -0
  33. package/dist/css/components/file-explorer.css +633 -0
  34. package/dist/css/components/filter-builder.css +232 -0
  35. package/dist/css/components/hold-confirm.css +87 -0
  36. package/dist/css/components/hover-card.css +20 -0
  37. package/dist/css/components/image-preview.css +195 -0
  38. package/dist/css/components/input-otp.css +10 -0
  39. package/dist/css/components/json-viewer.css +272 -0
  40. package/dist/css/components/like-button.css +78 -0
  41. package/dist/css/components/link-preview.css +275 -0
  42. package/dist/css/components/loading-button.css +77 -0
  43. package/dist/css/components/multi-select.css +267 -0
  44. package/dist/css/components/notification-bell.css +127 -0
  45. package/dist/css/components/pagination.css +11 -0
  46. package/dist/css/components/password-strength.css +183 -0
  47. package/dist/css/components/popover.css +25 -0
  48. package/dist/css/components/progress.css +140 -0
  49. package/dist/css/components/quantity.css +16 -0
  50. package/dist/css/components/questionnaire.css +217 -0
  51. package/dist/css/components/range-calendar.css +186 -0
  52. package/dist/css/components/range-slider.css +192 -0
  53. package/dist/css/components/resizable-panel.css +243 -0
  54. package/dist/css/components/rich-text-editor.css +284 -0
  55. package/dist/css/components/scratch-card.css +75 -0
  56. package/dist/css/components/skeleton.css +108 -0
  57. package/dist/css/components/slot-picker.css +126 -0
  58. package/dist/css/components/split-view.css +19 -0
  59. package/dist/css/components/star-rating.css +80 -0
  60. package/dist/css/components/stat-card.css +164 -0
  61. package/dist/css/components/studio-dock.css +35 -0
  62. package/dist/css/components/swipe-actions.css +105 -0
  63. package/dist/css/components/tag-input.css +244 -0
  64. package/dist/css/components/time-field.css +88 -0
  65. package/dist/css/components/timeline.css +219 -0
  66. package/dist/css/components/tooltip.css +79 -0
  67. package/dist/css/components/trailer.css +37 -0
  68. package/dist/css/components/tree-view.css +210 -0
  69. package/dist/css/components/virtual-list.css +195 -0
  70. package/dist/css/controls.css +65 -0
  71. package/dist/css/tokens.css +185 -181
  72. package/dist/elements/index.d.ts +742 -5
  73. package/dist/elements/index.js +1527 -8
  74. package/dist/elements/index.js.map +1 -1
  75. package/dist/{engine-GDGwQnJE.d.ts → engine-Cn6IhE3z.d.ts} +1 -1
  76. package/dist/engine-QECQnGz1.d.ts +33 -0
  77. package/dist/index.css +10113 -181
  78. package/dist/index.d.ts +89 -5
  79. package/dist/index.js +285 -9
  80. package/dist/index.js.map +1 -1
  81. package/dist/react/index.d.ts +678 -23
  82. package/dist/react/index.js +2389 -7
  83. package/dist/react/index.js.map +1 -1
  84. package/dist/{reduced-B98f8LPs.d.ts → reduced-BBUusld6.d.ts} +206 -2
  85. package/dist/reveal-DEvp6U6T.d.ts +2381 -0
  86. package/dist/{store-pHiUvxP8.d.ts → store-DjkTBX7f.d.ts} +2 -2
  87. package/package.json +1 -1
  88. package/registry.json +583 -104
  89. package/skills/motion-kit/SKILL.md +209 -189
  90. package/src/auto/auto-init.ts +136 -0
  91. package/src/components/activity-feed/README.md +56 -0
  92. package/src/components/activity-feed/activity-feed.css +239 -0
  93. package/src/components/activity-feed/element.ts +36 -0
  94. package/src/components/activity-feed/engine.ts +351 -0
  95. package/src/components/activity-feed/react.tsx +32 -0
  96. package/src/components/attachment/README.md +27 -0
  97. package/src/components/attachment/attachment.css +265 -0
  98. package/src/components/attachment/element.ts +50 -0
  99. package/src/components/attachment/engine.ts +469 -0
  100. package/src/components/attachment/react.tsx +65 -0
  101. package/src/components/breadcrumb/README.md +38 -0
  102. package/src/components/breadcrumb/breadcrumb.css +109 -0
  103. package/src/components/breadcrumb/element.ts +27 -0
  104. package/src/components/breadcrumb/engine.ts +249 -0
  105. package/src/components/breadcrumb/react.tsx +30 -0
  106. package/src/components/calendar/README.md +82 -0
  107. package/src/components/calendar/calendar.css +158 -0
  108. package/src/components/calendar/element.ts +27 -0
  109. package/src/components/calendar/engine.ts +412 -0
  110. package/src/components/calendar/react.tsx +69 -0
  111. package/src/components/chart/README.md +24 -0
  112. package/src/components/chart/chart.css +295 -0
  113. package/src/components/chart/element.ts +41 -0
  114. package/src/components/chart/engine.ts +811 -0
  115. package/src/components/chart/react.tsx +50 -0
  116. package/src/components/chart-tooltip/README.md +53 -0
  117. package/src/components/chart-tooltip/chart-tooltip.css +167 -0
  118. package/src/components/chart-tooltip/element.ts +37 -0
  119. package/src/components/chart-tooltip/engine.ts +442 -0
  120. package/src/components/chart-tooltip/react.tsx +47 -0
  121. package/src/components/code-block/README.md +19 -0
  122. package/src/components/code-block/code-block.css +263 -0
  123. package/src/components/code-block/element.ts +34 -0
  124. package/src/components/code-block/engine.ts +254 -0
  125. package/src/components/code-block/highlight.ts +244 -0
  126. package/src/components/code-block/react.tsx +94 -0
  127. package/src/components/color-area/README.md +50 -0
  128. package/src/components/color-area/color-area.css +81 -0
  129. package/src/components/color-area/element.ts +26 -0
  130. package/src/components/color-area/engine.ts +246 -0
  131. package/src/components/color-area/react.tsx +42 -0
  132. package/src/components/color-field/README.md +43 -0
  133. package/src/components/color-field/color-field.css +66 -0
  134. package/src/components/color-field/element.ts +26 -0
  135. package/src/components/color-field/engine.ts +174 -0
  136. package/src/components/color-field/react.tsx +42 -0
  137. package/src/components/color-picker/README.md +45 -0
  138. package/src/components/color-picker/color-picker.css +130 -0
  139. package/src/components/color-picker/element.ts +45 -0
  140. package/src/components/color-picker/engine.ts +314 -0
  141. package/src/components/color-picker/react.tsx +47 -0
  142. package/src/components/color-slider/README.md +55 -0
  143. package/src/components/color-slider/color-slider.css +112 -0
  144. package/src/components/color-slider/element.ts +26 -0
  145. package/src/components/color-slider/engine.ts +234 -0
  146. package/src/components/color-slider/react.tsx +45 -0
  147. package/src/components/command-search/README.md +15 -0
  148. package/src/components/command-search/command-search.css +251 -0
  149. package/src/components/command-search/element.ts +36 -0
  150. package/src/components/command-search/engine.ts +356 -0
  151. package/src/components/command-search/match.ts +53 -0
  152. package/src/components/command-search/react.tsx +66 -0
  153. package/src/components/context-menu/README.md +31 -0
  154. package/src/components/context-menu/context-menu.css +22 -0
  155. package/src/components/context-menu/element.ts +40 -0
  156. package/src/components/context-menu/engine.ts +153 -0
  157. package/src/components/context-menu/react.tsx +57 -0
  158. package/src/components/credit-card/README.md +20 -0
  159. package/src/components/credit-card/card.ts +77 -0
  160. package/src/components/credit-card/credit-card.css +287 -0
  161. package/src/components/credit-card/element.ts +33 -0
  162. package/src/components/credit-card/engine.ts +410 -0
  163. package/src/components/credit-card/react.tsx +36 -0
  164. package/src/components/data-table/README.md +83 -0
  165. package/src/components/data-table/data-table.css +590 -0
  166. package/src/components/data-table/element.ts +54 -0
  167. package/src/components/data-table/engine.ts +1059 -0
  168. package/src/components/data-table/react.tsx +94 -0
  169. package/src/components/date-range-picker/README.md +51 -0
  170. package/src/components/date-range-picker/date-range-picker.css +167 -0
  171. package/src/components/date-range-picker/element.ts +52 -0
  172. package/src/components/date-range-picker/engine.ts +352 -0
  173. package/src/components/date-range-picker/react.tsx +57 -0
  174. package/src/components/date-time-picker/README.md +48 -0
  175. package/src/components/date-time-picker/date-time-picker.css +177 -0
  176. package/src/components/date-time-picker/element.ts +47 -0
  177. package/src/components/date-time-picker/engine.ts +350 -0
  178. package/src/components/date-time-picker/react.tsx +54 -0
  179. package/src/components/dialog/README.md +103 -102
  180. package/src/components/dialog/element.ts +123 -118
  181. package/src/components/dialog/engine.ts +449 -432
  182. package/src/components/drawer/README.md +28 -0
  183. package/src/components/drawer/drawer.css +57 -0
  184. package/src/components/drawer/element.ts +21 -0
  185. package/src/components/drawer/engine.ts +56 -0
  186. package/src/components/drawer/react.tsx +85 -0
  187. package/src/components/editable-table/README.md +67 -0
  188. package/src/components/editable-table/editable-table.css +200 -0
  189. package/src/components/editable-table/element.ts +45 -0
  190. package/src/components/editable-table/engine.ts +775 -0
  191. package/src/components/editable-table/react.tsx +79 -0
  192. package/src/components/empty-state/README.md +41 -0
  193. package/src/components/empty-state/element.ts +23 -0
  194. package/src/components/empty-state/empty-state.css +60 -0
  195. package/src/components/empty-state/engine.ts +232 -0
  196. package/src/components/empty-state/react.tsx +34 -0
  197. package/src/components/file-explorer/README.md +67 -0
  198. package/src/components/file-explorer/element.ts +39 -0
  199. package/src/components/file-explorer/engine.ts +1307 -0
  200. package/src/components/file-explorer/file-explorer.css +632 -0
  201. package/src/components/file-explorer/react.tsx +77 -0
  202. package/src/components/filter-builder/README.md +47 -0
  203. package/src/components/filter-builder/element.ts +34 -0
  204. package/src/components/filter-builder/engine.ts +365 -0
  205. package/src/components/filter-builder/filter-builder.css +231 -0
  206. package/src/components/filter-builder/react.tsx +36 -0
  207. package/src/components/hold-confirm/README.md +13 -0
  208. package/src/components/hold-confirm/element.ts +24 -0
  209. package/src/components/hold-confirm/engine.ts +216 -0
  210. package/src/components/hold-confirm/hold-confirm.css +86 -0
  211. package/src/components/hold-confirm/react.tsx +28 -0
  212. package/src/components/hover-card/README.md +20 -0
  213. package/src/components/hover-card/element.ts +8 -0
  214. package/src/components/hover-card/engine.ts +131 -0
  215. package/src/components/hover-card/hover-card.css +19 -0
  216. package/src/components/hover-card/react.tsx +19 -0
  217. package/src/components/image-preview/README.md +16 -0
  218. package/src/components/image-preview/element.ts +26 -0
  219. package/src/components/image-preview/engine.ts +325 -0
  220. package/src/components/image-preview/image-preview.css +194 -0
  221. package/src/components/image-preview/react.tsx +43 -0
  222. package/src/components/input-otp/README.md +18 -0
  223. package/src/components/input-otp/element.ts +14 -0
  224. package/src/components/input-otp/engine.ts +106 -0
  225. package/src/components/input-otp/input-otp.css +9 -0
  226. package/src/components/input-otp/react.tsx +21 -0
  227. package/src/components/json-viewer/README.md +56 -0
  228. package/src/components/json-viewer/element.ts +37 -0
  229. package/src/components/json-viewer/engine.ts +440 -0
  230. package/src/components/json-viewer/json-viewer.css +271 -0
  231. package/src/components/json-viewer/react.tsx +56 -0
  232. package/src/components/like-button/README.md +13 -0
  233. package/src/components/like-button/element.ts +47 -0
  234. package/src/components/like-button/engine.ts +145 -0
  235. package/src/components/like-button/like-button.css +77 -0
  236. package/src/components/like-button/react.tsx +39 -0
  237. package/src/components/link-preview/README.md +19 -0
  238. package/src/components/link-preview/element.ts +29 -0
  239. package/src/components/link-preview/engine.ts +192 -0
  240. package/src/components/link-preview/link-preview.css +274 -0
  241. package/src/components/link-preview/react.tsx +101 -0
  242. package/src/components/loading-button/README.md +45 -0
  243. package/src/components/loading-button/element.ts +34 -0
  244. package/src/components/loading-button/engine.ts +201 -0
  245. package/src/components/loading-button/loading-button.css +76 -0
  246. package/src/components/loading-button/react.tsx +43 -0
  247. package/src/components/multi-select/README.md +48 -0
  248. package/src/components/multi-select/element.ts +28 -0
  249. package/src/components/multi-select/engine.ts +294 -0
  250. package/src/components/multi-select/multi-select.css +266 -0
  251. package/src/components/multi-select/react.tsx +39 -0
  252. package/src/components/notification-bell/README.md +14 -0
  253. package/src/components/notification-bell/element.ts +48 -0
  254. package/src/components/notification-bell/engine.ts +202 -0
  255. package/src/components/notification-bell/notification-bell.css +126 -0
  256. package/src/components/notification-bell/react.tsx +57 -0
  257. package/src/components/pagination/README.md +17 -0
  258. package/src/components/pagination/element.ts +14 -0
  259. package/src/components/pagination/engine.ts +102 -0
  260. package/src/components/pagination/pagination.css +10 -0
  261. package/src/components/pagination/react.tsx +22 -0
  262. package/src/components/password-strength/README.md +17 -0
  263. package/src/components/password-strength/element.ts +22 -0
  264. package/src/components/password-strength/engine.ts +212 -0
  265. package/src/components/password-strength/password-strength.css +182 -0
  266. package/src/components/password-strength/react.tsx +36 -0
  267. package/src/components/password-strength/score.ts +62 -0
  268. package/src/components/popover/README.md +44 -0
  269. package/src/components/popover/element.ts +31 -0
  270. package/src/components/popover/engine.ts +114 -0
  271. package/src/components/popover/popover.css +24 -0
  272. package/src/components/popover/react.tsx +50 -0
  273. package/src/components/progress/README.md +17 -0
  274. package/src/components/progress/element.ts +30 -0
  275. package/src/components/progress/engine.ts +173 -0
  276. package/src/components/progress/progress.css +139 -0
  277. package/src/components/progress/react.tsx +35 -0
  278. package/src/components/quantity/README.md +20 -0
  279. package/src/components/quantity/element.ts +14 -0
  280. package/src/components/quantity/engine.ts +85 -0
  281. package/src/components/quantity/quantity.css +15 -0
  282. package/src/components/quantity/react.tsx +21 -0
  283. package/src/components/questionnaire/README.md +32 -0
  284. package/src/components/questionnaire/element.ts +118 -0
  285. package/src/components/questionnaire/engine.ts +255 -0
  286. package/src/components/questionnaire/questionnaire.css +216 -0
  287. package/src/components/questionnaire/react.tsx +140 -0
  288. package/src/components/range-calendar/README.md +58 -0
  289. package/src/components/range-calendar/element.ts +42 -0
  290. package/src/components/range-calendar/engine.ts +390 -0
  291. package/src/components/range-calendar/range-calendar.css +185 -0
  292. package/src/components/range-calendar/react.tsx +56 -0
  293. package/src/components/range-slider/README.md +23 -0
  294. package/src/components/range-slider/element.ts +53 -0
  295. package/src/components/range-slider/engine.ts +316 -0
  296. package/src/components/range-slider/range-slider.css +191 -0
  297. package/src/components/range-slider/react.tsx +66 -0
  298. package/src/components/resizable-panel/README.md +61 -0
  299. package/src/components/resizable-panel/element.ts +36 -0
  300. package/src/components/resizable-panel/engine.ts +551 -0
  301. package/src/components/resizable-panel/react.tsx +74 -0
  302. package/src/components/resizable-panel/resizable-panel.css +242 -0
  303. package/src/components/rich-text-editor/README.md +57 -0
  304. package/src/components/rich-text-editor/element.ts +34 -0
  305. package/src/components/rich-text-editor/engine.ts +673 -0
  306. package/src/components/rich-text-editor/react.tsx +60 -0
  307. package/src/components/rich-text-editor/rich-text-editor.css +283 -0
  308. package/src/components/scratch-card/README.md +15 -0
  309. package/src/components/scratch-card/element.ts +30 -0
  310. package/src/components/scratch-card/engine.ts +257 -0
  311. package/src/components/scratch-card/react.tsx +40 -0
  312. package/src/components/scratch-card/scratch-card.css +74 -0
  313. package/src/components/skeleton/README.md +15 -0
  314. package/src/components/skeleton/element.ts +32 -0
  315. package/src/components/skeleton/engine.ts +104 -0
  316. package/src/components/skeleton/react.tsx +41 -0
  317. package/src/components/skeleton/skeleton.css +107 -0
  318. package/src/components/slot-picker/README.md +68 -0
  319. package/src/components/slot-picker/element.ts +27 -0
  320. package/src/components/slot-picker/engine.ts +412 -0
  321. package/src/components/slot-picker/react.tsx +76 -0
  322. package/src/components/slot-picker/slot-picker.css +125 -0
  323. package/src/components/split-view/README.md +50 -0
  324. package/src/components/split-view/element.ts +34 -0
  325. package/src/components/split-view/engine.ts +114 -0
  326. package/src/components/split-view/react.tsx +63 -0
  327. package/src/components/split-view/split-view.css +18 -0
  328. package/src/components/star-rating/README.md +14 -0
  329. package/src/components/star-rating/element.ts +26 -0
  330. package/src/components/star-rating/engine.ts +217 -0
  331. package/src/components/star-rating/react.tsx +33 -0
  332. package/src/components/star-rating/star-rating.css +79 -0
  333. package/src/components/stat-card/README.md +42 -0
  334. package/src/components/stat-card/element.ts +40 -0
  335. package/src/components/stat-card/engine.ts +283 -0
  336. package/src/components/stat-card/react.tsx +36 -0
  337. package/src/components/stat-card/stat-card.css +163 -0
  338. package/src/components/studio-dock/README.md +44 -0
  339. package/src/components/studio-dock/element.ts +11 -0
  340. package/src/components/studio-dock/engine.ts +212 -0
  341. package/src/components/studio-dock/react.tsx +23 -0
  342. package/src/components/studio-dock/studio-dock.css +34 -0
  343. package/src/components/swipe-actions/README.md +19 -0
  344. package/src/components/swipe-actions/element.ts +26 -0
  345. package/src/components/swipe-actions/engine.ts +260 -0
  346. package/src/components/swipe-actions/react.tsx +80 -0
  347. package/src/components/swipe-actions/swipe-actions.css +104 -0
  348. package/src/components/tag-input/README.md +42 -0
  349. package/src/components/tag-input/element.ts +39 -0
  350. package/src/components/tag-input/engine.ts +313 -0
  351. package/src/components/tag-input/react.tsx +54 -0
  352. package/src/components/tag-input/tag-input.css +243 -0
  353. package/src/components/time-field/README.md +54 -0
  354. package/src/components/time-field/element.ts +38 -0
  355. package/src/components/time-field/engine.ts +110 -0
  356. package/src/components/time-field/react.tsx +45 -0
  357. package/src/components/time-field/time-field.css +87 -0
  358. package/src/components/timeline/README.md +57 -0
  359. package/src/components/timeline/element.ts +31 -0
  360. package/src/components/timeline/engine.ts +300 -0
  361. package/src/components/timeline/react.tsx +59 -0
  362. package/src/components/timeline/timeline.css +218 -0
  363. package/src/components/tooltip/README.md +46 -0
  364. package/src/components/tooltip/element.ts +22 -0
  365. package/src/components/tooltip/engine.ts +288 -0
  366. package/src/components/tooltip/react.tsx +30 -0
  367. package/src/components/tooltip/tooltip.css +78 -0
  368. package/src/components/trailer/README.md +28 -0
  369. package/src/components/trailer/element.ts +13 -0
  370. package/src/components/trailer/engine.ts +189 -0
  371. package/src/components/trailer/react.tsx +16 -0
  372. package/src/components/trailer/trailer.css +36 -0
  373. package/src/components/tree-view/README.md +61 -0
  374. package/src/components/tree-view/element.ts +32 -0
  375. package/src/components/tree-view/engine.ts +569 -0
  376. package/src/components/tree-view/react.tsx +78 -0
  377. package/src/components/tree-view/tree-view.css +209 -0
  378. package/src/components/virtual-list/README.md +81 -0
  379. package/src/components/virtual-list/element.ts +42 -0
  380. package/src/components/virtual-list/engine.ts +678 -0
  381. package/src/components/virtual-list/react.tsx +101 -0
  382. package/src/components/virtual-list/virtual-list.css +194 -0
  383. package/src/core/chips.ts +196 -0
  384. package/src/core/color.ts +304 -0
  385. package/src/core/date-range.ts +67 -0
  386. package/src/core/dates.ts +108 -0
  387. package/src/core/files.ts +107 -0
  388. package/src/core/filter.ts +229 -0
  389. package/src/core/gsap.ts +80 -65
  390. package/src/core/listbox.ts +332 -0
  391. package/src/core/mark.ts +29 -0
  392. package/src/core/position.ts +89 -0
  393. package/src/core/relative-time.ts +77 -0
  394. package/src/core/segment-field.ts +373 -0
  395. package/src/core/segments.ts +153 -0
  396. package/src/core/tree.ts +116 -0
  397. package/src/core/virtual.ts +79 -0
  398. package/src/css/controls/README.md +14 -0
  399. package/src/css/controls/status.css +63 -0
  400. package/src/css/tokens.css +186 -182
  401. package/src/elements/index.ts +171 -0
  402. package/src/index.ts +97 -0
  403. package/src/motion/micro.ts +248 -0
  404. package/src/motion/sheet.ts +88 -65
  405. package/src/motion/surface.ts +9 -0
  406. package/src/react/index.ts +78 -0
  407. package/src/react/use-action-button.ts +6 -0
  408. package/src/react/use-editorial-reveal.ts +7 -0
  409. package/src/react/use-film-hero.ts +6 -0
  410. package/src/react/use-font-cycle.ts +7 -0
  411. package/src/react/use-image-zoom.ts +5 -0
  412. package/src/react/use-joined-cta.ts +5 -0
  413. package/src/react/use-local-clock.ts +5 -0
  414. package/src/react/use-scroll-dock.ts +5 -0
  415. package/src/react/use-sticker.ts +5 -0
  416. package/src/react/use-studio-dock.ts +8 -0
  417. package/src/react/use-thumbnail-nav.ts +7 -0
  418. package/src/react/use-trailer.ts +6 -0
  419. package/src/react/use-weight-hover.ts +5 -0
  420. package/src/recipes/action-button/README.md +31 -0
  421. package/src/recipes/action-button/action-button.css +113 -0
  422. package/src/recipes/action-button/create-action-button.ts +56 -0
  423. package/src/recipes/editorial-reveal/README.md +35 -0
  424. package/src/recipes/editorial-reveal/create-editorial-reveal.ts +91 -0
  425. package/src/recipes/editorial-reveal/editorial-reveal.css +17 -0
  426. package/src/recipes/film-hero/README.md +23 -0
  427. package/src/recipes/film-hero/create-film-hero.ts +65 -0
  428. package/src/recipes/film-hero/film-hero.css +29 -0
  429. package/src/recipes/font-cycle/README.md +11 -0
  430. package/src/recipes/font-cycle/create-font-cycle.ts +126 -0
  431. package/src/recipes/font-cycle/font-cycle.css +36 -0
  432. package/src/recipes/image-zoom/README.md +16 -0
  433. package/src/recipes/image-zoom/create-image-zoom.ts +27 -0
  434. package/src/recipes/image-zoom/image-zoom.css +7 -0
  435. package/src/recipes/joined-cta/README.md +13 -0
  436. package/src/recipes/joined-cta/create-joined-cta.ts +22 -0
  437. package/src/recipes/joined-cta/joined-cta.css +17 -0
  438. package/src/recipes/local-clock/README.md +31 -0
  439. package/src/recipes/local-clock/create-local-clock.ts +61 -0
  440. package/src/recipes/scroll-dock/README.md +46 -0
  441. package/src/recipes/scroll-dock/create-scroll-dock.ts +72 -0
  442. package/src/recipes/scroll-dock/scroll-dock.css +70 -0
  443. package/src/recipes/sticker/README.md +59 -0
  444. package/src/recipes/sticker/create-sticker.ts +490 -0
  445. package/src/recipes/sticker/sticker.css +112 -0
  446. package/src/recipes/thumbnail-nav/README.md +16 -0
  447. package/src/recipes/thumbnail-nav/create-thumbnail-nav.ts +165 -0
  448. package/src/recipes/thumbnail-nav/thumbnail-nav.css +108 -0
  449. package/src/recipes/weight-hover/README.md +37 -0
  450. package/src/recipes/weight-hover/create-weight-hover.ts +136 -0
  451. package/src/recipes/weight-hover/weight-hover.css +7 -0
  452. package/dist/chunk-BFC27F2G.js.map +0 -1
  453. package/dist/chunk-J6OCFNGU.js +0 -177
  454. package/dist/chunk-J6OCFNGU.js.map +0 -1
  455. package/dist/chunk-JQMJQSXD.js +0 -2374
  456. package/dist/chunk-JQMJQSXD.js.map +0 -1
  457. package/dist/chunk-XDWBVFHQ.js.map +0 -1
  458. package/dist/reveal-C0hCDcB_.d.ts +0 -415
@@ -0,0 +1,551 @@
1
+ import { gsap, EASE, registerMotion } from '../../core/gsap';
2
+ import { reduced } from '../../core/reduced';
3
+ import { createScope } from '../../core/scope';
4
+ import { inert, type Instance } from '../../core/types';
5
+ import { fromJson } from '../../core/dates';
6
+ import { requestFrame } from '../../core/frame';
7
+
8
+ /*
9
+ * Resizable panels: panels side by side (or stacked) with a separator between
10
+ * each pair that drags (mouse, pen, touch) and moves with the keyboard (the
11
+ * WAI-ARIA window splitter). A separator is a hairline with a grip that grows
12
+ * in the accent on hover and while dragging, and a bubble shows the two sizes
13
+ * ("32 % · 68 %") while it moves. Past a panel's limit the drag gives a quarter
14
+ * of the way (rubber band) and springs back on release (.45 s, swing). A
15
+ * collapsible panel dragged well under its minimum snaps shut (.3 s, swing)
16
+ * and opens again when dragged back out, with Enter on its separator, or with
17
+ * the separator's buttons (handleButtons). A double click on a separator puts
18
+ * the first sizes back (.45 s, signature). Sizes can be remembered.
19
+ */
20
+
21
+ export type PanelOrientation = 'horizontal' | 'vertical';
22
+
23
+ export interface ResizablePanelOptions {
24
+ /** 'horizontal' (default): side by side; 'vertical': stacked. */
25
+ orientation?: PanelOrientation;
26
+ /** Sizes in percent, one per panel (default: data-size on the panels, else equal). */
27
+ sizes?: number[];
28
+ /** Smallest size of a panel in percent (default 10; data-min on a panel wins). */
29
+ min?: number;
30
+ /** Largest size of a panel in percent (default 100; data-max on a panel wins). */
31
+ max?: number;
32
+ /** Panels (indexes) that snap shut when dragged well under their minimum; data-collapsible on a panel too. */
33
+ collapsible?: number[];
34
+ /** Panels that start shut. */
35
+ collapsed?: number[];
36
+ /** Arrow keys move this many percent (default 5; Shift: four times). */
37
+ step?: number;
38
+ /** Remember sizes in localStorage under this key. */
39
+ storageKey?: string;
40
+ /** Buttons on each separator to shut and open the panels beside it. */
41
+ handleButtons?: boolean;
42
+ /** Names of the panels, for the separators' labels (default: data-label, else "panel 1"…). */
43
+ labels?: string[];
44
+ /** While it moves (once a frame). */
45
+ onResize?: (sizes: number[]) => void;
46
+ /** When a drag, a key or an animation ends. */
47
+ onResizeEnd?: (sizes: number[]) => void;
48
+ onCollapse?: (index: number, collapsed: boolean) => void;
49
+ }
50
+
51
+ export interface ResizablePanelInstance extends Instance {
52
+ setSizes(sizes: number[], animate?: boolean): void;
53
+ collapse(index: number): void;
54
+ expand(index: number): void;
55
+ toggle(index: number): void;
56
+ /** The first sizes again. */
57
+ reset(): void;
58
+ setOrientation(orientation: PanelOrientation): void;
59
+ readonly sizes: number[];
60
+ readonly collapsed: number[];
61
+ readonly orientation: PanelOrientation;
62
+ }
63
+
64
+ const CHEVRON = (path: string) => `<svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="${path}"/></svg>`;
65
+ const ARROWS = { left: CHEVRON('M15 6l-6 6 6 6'), right: CHEVRON('M9 6l6 6-6 6'), up: CHEVRON('M6 15l6-6 6 6'), down: CHEVRON('M6 9l6 6 6-6') };
66
+ let sequence = 0;
67
+
68
+ const round = (n: number) => Math.round(n * 10) / 10;
69
+
70
+ /** Sizes that add up to 100: missing ones share what is left, then all scale to 100 (all missing: equal). */
71
+ export function normaliseSizes(sizes: Array<number | undefined>, count: number): number[] {
72
+ const given = Array.from({ length: count }, (_, i) => {
73
+ const size = sizes[i];
74
+ return typeof size === 'number' && Number.isFinite(size) && size >= 0 ? size : null;
75
+ });
76
+ const known = given.reduce<number>((sum, size) => sum + (size ?? 0), 0);
77
+ const missing = given.filter((size) => size === null).length;
78
+ const share = missing ? Math.max(0, 100 - known) / missing : 0;
79
+ const list = given.map((size) => size ?? share);
80
+ const total = list.reduce((sum, size) => sum + size, 0);
81
+ if (!total) return Array.from({ length: count }, () => 100 / count);
82
+ return list.map((size) => (size / total) * 100);
83
+ }
84
+
85
+ export function createResizablePanel(root: HTMLElement | null, options: ResizablePanelOptions = {}): ResizablePanelInstance {
86
+ if (!root) {
87
+ return inert({ setSizes() {}, collapse() {}, expand() {}, toggle() {}, reset() {}, setOrientation() {}, sizes: [], collapsed: [], orientation: 'horizontal' as PanelOrientation });
88
+ }
89
+ registerMotion();
90
+ const scope = createScope();
91
+ const uid = `mk-rp-${++sequence}`;
92
+ const panels = [...root.children].filter((child): child is HTMLElement => child instanceof HTMLElement && !child.hasAttribute('data-mk-resize-handle'));
93
+ const count = panels.length;
94
+ let orientation: PanelOrientation = options.orientation === 'vertical' ? 'vertical' : 'horizontal';
95
+ const step = options.step && options.step > 0 ? options.step : 5;
96
+ const number = (value: string | undefined) => (value !== undefined && value !== '' && Number.isFinite(Number(value)) ? Number(value) : undefined);
97
+ const mins = panels.map((panel) => Math.max(0, number(panel.dataset.min) ?? options.min ?? 10));
98
+ const maxs = panels.map((panel) => Math.min(100, number(panel.dataset.max) ?? options.max ?? 100));
99
+ const collapsibleList = fromJson(options.collapsible) ?? [];
100
+ const canCollapse = panels.map((panel, i) => panel.hasAttribute('data-collapsible') || collapsibleList.includes(i));
101
+ const names = panels.map((panel, i) => options.labels?.[i] ?? panel.dataset.label ?? panel.getAttribute('aria-label') ?? `panel ${i + 1}`);
102
+ const initial = normaliseSizes(fromJson(options.sizes) ?? panels.map((panel) => number(panel.dataset.size)), count);
103
+ let sizes = [...initial];
104
+ const collapsed = new Set<number>((fromJson(options.collapsed) ?? []).filter((i) => canCollapse[i]));
105
+ /** What a panel had before it shut, to open back to. */
106
+ const before = new Map<number, number>();
107
+ const effects = new Set<gsap.core.Animation>();
108
+ const keep = (animation: gsap.core.Animation | null) => {
109
+ if (!animation) return;
110
+ effects.add(animation);
111
+ // Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
112
+ for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
113
+ };
114
+
115
+ // Remembered sizes win over the given ones.
116
+ const storage = (() => {
117
+ try {
118
+ return options.storageKey ? window.localStorage : null;
119
+ } catch {
120
+ return null;
121
+ }
122
+ })();
123
+ if (storage && options.storageKey) {
124
+ try {
125
+ const saved = JSON.parse(storage.getItem(options.storageKey) ?? 'null') as { sizes?: number[]; collapsed?: number[] } | null;
126
+ if (saved?.sizes?.length === count) {
127
+ sizes = normaliseSizes(saved.sizes, count);
128
+ collapsed.clear();
129
+ for (const i of saved.collapsed ?? []) if (canCollapse[i]) collapsed.add(i);
130
+ }
131
+ } catch {
132
+ // Unreadable: the given sizes stand.
133
+ }
134
+ }
135
+ // A shut panel gives its room to its neighbour; it opens back to what it had.
136
+ for (const i of collapsed) {
137
+ if (sizes[i]! > 0) {
138
+ before.set(i, sizes[i]!);
139
+ const neighbour = i + 1 < count ? i + 1 : i - 1;
140
+ if (neighbour >= 0) sizes[neighbour]! += sizes[i]!;
141
+ sizes[i] = 0;
142
+ }
143
+ }
144
+
145
+ // ── The parts ────────────────────────────────────────────────────────────
146
+ scope.attr(root, 'data-mk-ready', '');
147
+ scope.attr(root, 'data-mk-orientation', orientation);
148
+ panels.forEach((panel, i) => {
149
+ scope.attr(panel, 'data-mk-panel', '');
150
+ if (!panel.id) scope.attr(panel, 'id', `${uid}-panel-${i}`);
151
+ scope.attr(panel, 'style', panel.getAttribute('style'));
152
+ });
153
+ const bubble = document.createElement('span');
154
+ bubble.dataset.mkResizeBubble = '';
155
+ bubble.setAttribute('aria-hidden', 'true');
156
+ bubble.hidden = true;
157
+ const handles = panels.slice(0, -1).map((panel, i) => {
158
+ const handle = document.createElement('div');
159
+ handle.dataset.mkResizeHandle = '';
160
+ handle.dataset.index = String(i);
161
+ handle.setAttribute('role', 'separator');
162
+ handle.tabIndex = 0;
163
+ handle.setAttribute('aria-controls', panel.id);
164
+ handle.setAttribute('aria-label', `Redimensionar ${names[i]} y ${names[i + 1]}`);
165
+ const grip = document.createElement('span');
166
+ grip.dataset.mkResizeGrip = '';
167
+ grip.setAttribute('aria-hidden', 'true');
168
+ handle.append(grip);
169
+ if (options.handleButtons) {
170
+ for (const [side, target] of [['start', i], ['end', i + 1]] as const) {
171
+ if (!canCollapse[target]) continue;
172
+ const button = document.createElement('button');
173
+ button.type = 'button';
174
+ button.tabIndex = -1;
175
+ button.dataset.mkResizeButton = side;
176
+ handle.append(button);
177
+ }
178
+ }
179
+ panel.after(handle);
180
+ return handle;
181
+ });
182
+ root.append(bubble);
183
+ scope.add(() => {
184
+ for (const effect of effects) effect.kill();
185
+ effects.clear();
186
+ for (const handle of handles) handle.remove();
187
+ bubble.remove();
188
+ for (const panel of panels) panel.removeAttribute('data-mk-collapsed');
189
+ });
190
+
191
+ // ── Drawing the sizes ────────────────────────────────────────────────────
192
+ const horizontal = () => orientation === 'horizontal';
193
+ const apply = (next: number[]) => {
194
+ panels.forEach((panel, i) => {
195
+ const size = Math.max(0, next[i]!);
196
+ panel.style.flex = `${round(size * 100) / 100} 1 0px`;
197
+ panel.toggleAttribute('data-mk-collapsed', collapsed.has(i) && size < 0.5);
198
+ });
199
+ handles.forEach((handle, i) => {
200
+ // A separator pushed to an edge by a shut panel keeps its buttons inside.
201
+ const edge = i === count - 2 && collapsed.has(i + 1) && next[i + 1]! < 0.5 ? 'end' : i === 0 && collapsed.has(0) && next[0]! < 0.5 ? 'start' : null;
202
+ if (edge) handle.dataset.edge = edge;
203
+ else delete handle.dataset.edge;
204
+ handle.setAttribute('aria-valuenow', String(Math.round(Math.max(0, next[i]!))));
205
+ handle.setAttribute('aria-valuemin', String(canCollapse[i] ? 0 : Math.round(mins[i]!)));
206
+ handle.setAttribute('aria-valuemax', String(Math.round(Math.min(maxs[i]!, 100 - (canCollapse[i + 1] ? 0 : mins[i + 1]!)))));
207
+ handle.setAttribute('aria-valuetext', `${Math.round(Math.max(0, next[i]!))} %${collapsed.has(i) ? ', cerrado' : ''}`);
208
+ for (const button of handle.querySelectorAll<HTMLButtonElement>('[data-mk-resize-button]')) {
209
+ const start = button.dataset.mkResizeButton === 'start';
210
+ const index = start ? i : i + 1;
211
+ const shut = collapsed.has(index);
212
+ // The arrow points where the panel goes: toward its edge to shut it, away to open it.
213
+ button.innerHTML = horizontal() ? (start === !shut ? ARROWS.left : ARROWS.right) : start === !shut ? ARROWS.up : ARROWS.down;
214
+ const label = `${shut ? 'Abrir' : 'Cerrar'} ${names[index]}`;
215
+ button.setAttribute('aria-label', label);
216
+ button.title = label;
217
+ }
218
+ });
219
+ };
220
+ const paintOrientation = () => {
221
+ root.setAttribute('data-mk-orientation', orientation);
222
+ for (const handle of handles) handle.setAttribute('aria-orientation', horizontal() ? 'vertical' : 'horizontal');
223
+ };
224
+ paintOrientation();
225
+ apply(sizes);
226
+
227
+ const save = () => {
228
+ if (!storage || !options.storageKey) return;
229
+ try {
230
+ storage.setItem(options.storageKey, JSON.stringify({ sizes: sizes.map(round), collapsed: [...collapsed] }));
231
+ } catch {
232
+ // Full or blocked: not remembered.
233
+ }
234
+ };
235
+ let pendingNotify: (() => void) | null = null;
236
+ const notify = () => {
237
+ if (pendingNotify || !options.onResize) return;
238
+ pendingNotify = requestFrame(() => {
239
+ pendingNotify = null;
240
+ options.onResize?.(sizes.map(round));
241
+ });
242
+ };
243
+ scope.add(() => pendingNotify?.());
244
+ const settle = () => {
245
+ save();
246
+ options.onResizeEnd?.(sizes.map(round));
247
+ root.dispatchEvent(new CustomEvent('mk:resize', { bubbles: true, detail: { sizes: sizes.map(round) } }));
248
+ };
249
+
250
+ let tween: gsap.core.Tween | null = null;
251
+ /** Glide to `target` (a spring by default). */
252
+ const animateTo = (target: number[], { duration = 0.45, ease = EASE.signature, done }: { duration?: number; ease?: string; done?: () => void } = {}) => {
253
+ tween?.kill();
254
+ if (reduced()) {
255
+ sizes = target;
256
+ apply(sizes);
257
+ done?.();
258
+ return;
259
+ }
260
+ const from = [...sizes];
261
+ const progress = { t: 0 };
262
+ tween = gsap.to(progress, {
263
+ t: 1,
264
+ duration,
265
+ ease,
266
+ onUpdate: () => {
267
+ sizes = from.map((size, i) => Math.max(0, size + (target[i]! - size) * progress.t));
268
+ apply(sizes);
269
+ notify();
270
+ },
271
+ onComplete: () => {
272
+ sizes = [...target];
273
+ apply(sizes);
274
+ tween = null;
275
+ done?.();
276
+ },
277
+ });
278
+ keep(tween);
279
+ };
280
+
281
+ // ── Limits ───────────────────────────────────────────────────────────────
282
+ const lowest = (i: number) => (collapsed.has(i) ? 0 : mins[i]!);
283
+ /** How far the separator `i` may move (in percent) from `start` sizes: [most negative, most positive]. */
284
+ const room = (i: number, start: number[]) => {
285
+ const a = start[i]!;
286
+ const b = start[i + 1]!;
287
+ const down = Math.max(lowest(i) - a, b - maxs[i + 1]!);
288
+ const up = Math.min(maxs[i]! - a, b - lowest(i + 1));
289
+ return [Math.min(0, down), Math.max(0, up)] as const;
290
+ };
291
+
292
+ const setCollapsed = (index: number, shut: boolean, animate = true) => {
293
+ if (!canCollapse[index] || collapsed.has(index) === shut) return;
294
+ // The neighbour that gives or takes the room: the next panel, or the one before the last.
295
+ const neighbour = index + 1 < count ? index + 1 : index - 1;
296
+ const target = [...sizes];
297
+ if (shut) {
298
+ before.set(index, sizes[index]! > 0.5 ? sizes[index]! : before.get(index) ?? initial[index]!);
299
+ target[neighbour] = sizes[neighbour]! + sizes[index]!;
300
+ target[index] = 0;
301
+ collapsed.add(index);
302
+ } else {
303
+ collapsed.delete(index);
304
+ const wanted = Math.max(mins[index]!, before.get(index) ?? initial[index]!);
305
+ const give = Math.min(wanted, Math.max(0, sizes[neighbour]! - mins[neighbour]!));
306
+ target[index] = give;
307
+ target[neighbour] = sizes[neighbour]! - give;
308
+ }
309
+ const finish = () => {
310
+ settle();
311
+ options.onCollapse?.(index, shut);
312
+ root.dispatchEvent(new CustomEvent('mk:collapse', { bubbles: true, detail: { index, collapsed: shut } }));
313
+ };
314
+ status.textContent = `${names[index]} ${shut ? 'cerrado' : 'abierto'}`;
315
+ if (animate) animateTo(target, { duration: 0.3, ease: EASE.swatch, done: finish });
316
+ else {
317
+ sizes = target;
318
+ apply(sizes);
319
+ finish();
320
+ }
321
+ };
322
+
323
+ const status = document.createElement('span');
324
+ status.className = 'mk-sr-only';
325
+ status.setAttribute('role', 'status');
326
+ root.append(status);
327
+ scope.add(() => status.remove());
328
+
329
+ // ── Dragging ─────────────────────────────────────────────────────────────
330
+ interface Drag {
331
+ index: number;
332
+ pointerId: number;
333
+ start: number[];
334
+ origin: number;
335
+ /** Pixels the panels share (the root without its separators). */
336
+ space: number;
337
+ moved: boolean;
338
+ }
339
+ let drag: Drag | null = null;
340
+ const spaceOf = () => {
341
+ const box = root.getBoundingClientRect();
342
+ const handlesSize = handles.reduce((sum, handle) => sum + (horizontal() ? handle.offsetWidth : handle.offsetHeight), 0);
343
+ return Math.max(1, (horizontal() ? box.width : box.height) - handlesSize);
344
+ };
345
+ const showBubble = (i: number, at: number) => {
346
+ bubble.hidden = false;
347
+ bubble.textContent = `${Math.round(sizes[i]!)} % · ${Math.round(sizes[i + 1]!)} %`;
348
+ const handle = handles[i]!;
349
+ const box = handle.getBoundingClientRect();
350
+ const rootBox = root.getBoundingClientRect();
351
+ if (horizontal()) {
352
+ bubble.style.left = `${box.left - rootBox.left + box.width / 2}px`;
353
+ bubble.style.top = `${Math.max(16, Math.min(rootBox.height - 16, at - rootBox.top))}px`;
354
+ } else {
355
+ bubble.style.top = `${box.top - rootBox.top + box.height / 2}px`;
356
+ bubble.style.left = `${Math.max(40, Math.min(rootBox.width - 40, at - rootBox.left))}px`;
357
+ }
358
+ };
359
+ const hideBubble = () => {
360
+ if (bubble.hidden) return;
361
+ if (reduced()) {
362
+ bubble.hidden = true;
363
+ return;
364
+ }
365
+ // Opacity only: the bubble is centred with CSS translate, which transforms would reset.
366
+ keep(gsap.to(bubble, { opacity: 0, duration: 0.18, ease: 'power1.in', onComplete: () => {
367
+ bubble.hidden = true;
368
+ gsap.set(bubble, { clearProps: 'opacity' });
369
+ } }));
370
+ };
371
+
372
+ const move = (event: PointerEvent) => {
373
+ if (!drag || event.pointerId !== drag.pointerId) return;
374
+ event.preventDefault();
375
+ const i = drag.index;
376
+ const position = horizontal() ? event.clientX : event.clientY;
377
+ const wanted = ((position - drag.origin) / drag.space) * 100;
378
+ if (!drag.moved && Math.abs(wanted) > 0.2) {
379
+ drag.moved = true;
380
+ handles[i]!.setAttribute('data-mk-dragging', '');
381
+ if (!reduced()) keep(gsap.fromTo(bubble, { opacity: 0 }, { opacity: 1, duration: 0.2, ease: 'power1.out', clearProps: 'opacity' }));
382
+ }
383
+ // A collapsible panel dragged well under its minimum snaps shut; dragged back out, it opens.
384
+ for (const [index, sign] of [[i, 1], [i + 1, -1]] as const) {
385
+ if (!canCollapse[index]) continue;
386
+ const size = drag.start[index]! + sign * wanted;
387
+ const threshold = mins[index]! / 2;
388
+ if (!collapsed.has(index) && size < threshold) {
389
+ collapsed.add(index);
390
+ before.set(index, drag.start[index]! > 0.5 ? drag.start[index]! : before.get(index) ?? initial[index]!);
391
+ const target = [...drag.start];
392
+ target[index] = 0;
393
+ target[index === i ? i + 1 : i] = drag.start[i]! + drag.start[i + 1]!;
394
+ // If the drag is over when it lands, it settles then.
395
+ animateTo(target, { duration: 0.3, ease: EASE.swatch, done: () => (drag ? undefined : settle()) });
396
+ status.textContent = `${names[index]} cerrado`;
397
+ options.onCollapse?.(index, true);
398
+ return;
399
+ }
400
+ if (collapsed.has(index)) {
401
+ // Still shut while the pointer stays near it.
402
+ if (size <= threshold) return;
403
+ collapsed.delete(index);
404
+ status.textContent = `${names[index]} abierto`;
405
+ options.onCollapse?.(index, false);
406
+ }
407
+ }
408
+ if (tween) return;
409
+ const [down, up] = room(i, drag.start);
410
+ // Past a limit the panel gives a quarter of the way (rubber band).
411
+ const delta = wanted < down ? down + (wanted - down) / 4 : wanted > up ? up + (wanted - up) / 4 : wanted;
412
+ const next = [...drag.start];
413
+ next[i] = Math.max(0, drag.start[i]! + delta);
414
+ next[i + 1] = Math.max(0, drag.start[i + 1]! - delta);
415
+ sizes = next;
416
+ apply(sizes);
417
+ showBubble(i, horizontal() ? event.clientY : event.clientX);
418
+ notify();
419
+ };
420
+ const end = (event: PointerEvent) => {
421
+ if (!drag || event.pointerId !== drag.pointerId) return;
422
+ const { index } = drag;
423
+ const moved = drag.moved;
424
+ drag = null;
425
+ window.removeEventListener('pointermove', move);
426
+ window.removeEventListener('pointerup', end);
427
+ window.removeEventListener('pointercancel', end);
428
+ handles[index]!.removeAttribute('data-mk-dragging');
429
+ root.removeAttribute('data-mk-resizing');
430
+ hideBubble();
431
+ // A snap on its way settles when it lands.
432
+ if (!moved || tween) return;
433
+ // Back inside the limits with a spring, if the drag went past them.
434
+ const clamped = [...sizes];
435
+ for (const i of [index, index + 1]) {
436
+ if (collapsed.has(i)) continue;
437
+ const fixed = Math.min(maxs[i]!, Math.max(mins[i]!, clamped[i]!));
438
+ const other = i === index ? index + 1 : index;
439
+ clamped[other] = clamped[other]! + clamped[i]! - fixed;
440
+ clamped[i] = fixed;
441
+ }
442
+ if (clamped.some((size, i) => Math.abs(size - sizes[i]!) > 0.05)) animateTo(clamped, { duration: 0.45, ease: EASE.swatch, done: settle });
443
+ else settle();
444
+ // The grip settles with a little pop.
445
+ const grip = handles[index]!.querySelector('[data-mk-resize-grip]');
446
+ if (grip && !reduced()) keep(gsap.fromTo(grip, { scale: 1.25 }, { scale: 1, duration: 0.4, ease: EASE.pop, clearProps: 'transform' }));
447
+ };
448
+ scope.add(() => {
449
+ window.removeEventListener('pointermove', move);
450
+ window.removeEventListener('pointerup', end);
451
+ window.removeEventListener('pointercancel', end);
452
+ });
453
+
454
+ handles.forEach((handle, i) => {
455
+ scope.on(handle, 'pointerdown', (event) => {
456
+ const pointer = event as PointerEvent;
457
+ if (pointer.button !== 0 || (pointer.target as Element).closest('[data-mk-resize-button]')) return;
458
+ pointer.preventDefault();
459
+ handle.focus({ preventScroll: true });
460
+ tween?.kill();
461
+ tween = null;
462
+ drag = { index: i, pointerId: pointer.pointerId, start: [...sizes], origin: horizontal() ? pointer.clientX : pointer.clientY, space: spaceOf(), moved: false };
463
+ root.setAttribute('data-mk-resizing', '');
464
+ window.addEventListener('pointermove', move, { passive: false });
465
+ window.addEventListener('pointerup', end);
466
+ window.addEventListener('pointercancel', end);
467
+ });
468
+ scope.on(handle, 'dblclick', (event) => {
469
+ if ((event.target as Element).closest('[data-mk-resize-button]')) return;
470
+ collapsed.clear();
471
+ status.textContent = 'Tamaños iniciales';
472
+ animateTo([...initial], { done: settle });
473
+ });
474
+ scope.on(handle, 'click', (event) => {
475
+ const button = (event.target as Element).closest<HTMLElement>('[data-mk-resize-button]');
476
+ if (!button) return;
477
+ const index = button.dataset.mkResizeButton === 'start' ? i : i + 1;
478
+ setCollapsed(index, !collapsed.has(index));
479
+ });
480
+ scope.on(handle, 'keydown', (event) => {
481
+ const key = event as KeyboardEvent;
482
+ const less = horizontal() ? 'ArrowLeft' : 'ArrowUp';
483
+ const more = horizontal() ? 'ArrowRight' : 'ArrowDown';
484
+ let delta: number | null = null;
485
+ const [down, up] = room(i, sizes);
486
+ if (key.key === less) delta = -step * (key.shiftKey ? 4 : 1);
487
+ else if (key.key === more) delta = step * (key.shiftKey ? 4 : 1);
488
+ else if (key.key === 'Home') delta = down;
489
+ else if (key.key === 'End') delta = up;
490
+ else if (key.key === 'Enter') {
491
+ key.preventDefault();
492
+ // The panel before it shuts or opens (or the one after, when only that one can).
493
+ const index = canCollapse[i] ? i : canCollapse[i + 1] ? i + 1 : -1;
494
+ if (index >= 0) setCollapsed(index, !collapsed.has(index));
495
+ return;
496
+ }
497
+ if (delta === null) return;
498
+ key.preventDefault();
499
+ const moveBy = Math.max(down, Math.min(up, delta));
500
+ if (Math.abs(moveBy) < 0.01) {
501
+ // At the limit: a nudge says so.
502
+ if (!reduced()) keep(gsap.fromTo(handle.querySelector('[data-mk-resize-grip]'), { [horizontal() ? 'x' : 'y']: Math.sign(delta) * 3 }, { [horizontal() ? 'x' : 'y']: 0, duration: 0.4, ease: EASE.pop, clearProps: 'transform' }));
503
+ return;
504
+ }
505
+ const target = [...sizes];
506
+ target[i] = sizes[i]! + moveBy;
507
+ target[i + 1] = sizes[i + 1]! - moveBy;
508
+ animateTo(target, { duration: 0.2, ease: 'power2.out', done: settle });
509
+ });
510
+ });
511
+
512
+ return {
513
+ setSizes(next, animate = true) {
514
+ if (scope.disposed) return;
515
+ const target = normaliseSizes(next, count);
516
+ collapsed.clear();
517
+ target.forEach((size, i) => {
518
+ if (size < 0.5 && canCollapse[i]) collapsed.add(i);
519
+ });
520
+ if (animate) animateTo(target, { done: settle });
521
+ else {
522
+ tween?.kill();
523
+ sizes = target;
524
+ apply(sizes);
525
+ }
526
+ },
527
+ collapse: (index) => setCollapsed(index, true),
528
+ expand: (index) => setCollapsed(index, false),
529
+ toggle: (index) => setCollapsed(index, !collapsed.has(index)),
530
+ reset() {
531
+ collapsed.clear();
532
+ animateTo([...initial], { done: settle });
533
+ },
534
+ setOrientation(next) {
535
+ if (next === orientation || scope.disposed) return;
536
+ orientation = next;
537
+ paintOrientation();
538
+ apply(sizes);
539
+ },
540
+ get sizes() {
541
+ return sizes.map(round);
542
+ },
543
+ get collapsed() {
544
+ return [...collapsed];
545
+ },
546
+ get orientation() {
547
+ return orientation;
548
+ },
549
+ destroy: () => scope.dispose(),
550
+ };
551
+ }
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+ import { Children, forwardRef, useImperativeHandle, useRef, type HTMLAttributes } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createResizablePanel, type ResizablePanelInstance, type ResizablePanelOptions } from './engine';
5
+
6
+ export interface ResizablePanelProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onResize'>, ResizablePanelOptions {}
7
+
8
+ export type ResizablePanelHandle = Pick<ResizablePanelInstance, 'setSizes' | 'collapse' | 'expand' | 'toggle' | 'reset'>;
9
+
10
+ /**
11
+ * <ResizablePanel sizes={[25, 75]} collapsible={[0]} onResizeEnd={save}><aside /><main /></ResizablePanel>.
12
+ * New `sizes` glide there; adding or taking away a panel starts it again.
13
+ */
14
+ export const ResizablePanel = forwardRef<ResizablePanelHandle, ResizablePanelProps>(function ResizablePanel(
15
+ { orientation, sizes, min, max, collapsible, collapsed, step, storageKey, handleButtons, labels, onResize, onResizeEnd, onCollapse, className, children, ...props },
16
+ handle,
17
+ ) {
18
+ const ref = useRef<HTMLDivElement>(null);
19
+ const instance = useRef<ResizablePanelInstance | null>(null);
20
+ const resize = useLatest(onResize);
21
+ const resizeEnd = useLatest(onResizeEnd);
22
+ const collapse = useLatest(onCollapse);
23
+ const shown = useRef(sizes);
24
+ useImperativeHandle(
25
+ handle,
26
+ () => ({
27
+ setSizes: (next, animate) => instance.current?.setSizes(next, animate),
28
+ collapse: (index) => instance.current?.collapse(index),
29
+ expand: (index) => instance.current?.expand(index),
30
+ toggle: (index) => instance.current?.toggle(index),
31
+ reset: () => instance.current?.reset(),
32
+ }),
33
+ [],
34
+ );
35
+ const count = Children.count(children);
36
+ useIsomorphicLayoutEffect(() => {
37
+ const created = createResizablePanel(ref.current, {
38
+ orientation,
39
+ sizes: shown.current,
40
+ min,
41
+ max,
42
+ collapsible,
43
+ collapsed,
44
+ step,
45
+ storageKey,
46
+ handleButtons,
47
+ labels,
48
+ onResize: (next) => resize.current?.(next),
49
+ onResizeEnd: (next) => resizeEnd.current?.(next),
50
+ onCollapse: (index, shut) => collapse.current?.(index, shut),
51
+ });
52
+ instance.current = created;
53
+ return () => {
54
+ created.destroy();
55
+ instance.current = null;
56
+ };
57
+ // eslint-disable-next-line react-hooks/exhaustive-deps
58
+ }, [count, min, max, String(collapsible), step, storageKey, handleButtons, String(labels)]);
59
+ useIsomorphicLayoutEffect(() => {
60
+ if (orientation) instance.current?.setOrientation(orientation);
61
+ }, [orientation]);
62
+ const sizesKey = JSON.stringify(sizes ?? null);
63
+ useIsomorphicLayoutEffect(() => {
64
+ if (!sizes || JSON.stringify(shown.current ?? null) === sizesKey) return;
65
+ shown.current = sizes;
66
+ instance.current?.setSizes(sizes);
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ }, [sizesKey]);
69
+ return (
70
+ <div {...props} ref={ref} className={['mk-resizable-panel', className].filter(Boolean).join(' ')}>
71
+ {children}
72
+ </div>
73
+ );
74
+ });