@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,34 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createSplitView, type SplitViewInstance } from './engine';
3
+
4
+ /**
5
+ * <mk-split-view class="mk-split-view" ratio="0.35" stack-below="640" labels="Bandeja,Mensaje">
6
+ * <section>…</section><section>…</section>
7
+ * </mk-split-view>. Emits mk:resize and mk:collapse from its separator.
8
+ */
9
+ export class MkSplitViewElement extends MkElement<SplitViewInstance> {
10
+ static options: Record<string, OptionType> = {
11
+ orientation: 'string',
12
+ ratio: 'number',
13
+ min: 'number',
14
+ collapsible: 'string',
15
+ stackBelow: 'number',
16
+ storageKey: 'string',
17
+ labels: 'list',
18
+ };
19
+
20
+ protected create(options: Record<string, unknown>) {
21
+ const raw = this.getAttribute('collapsible');
22
+ const collapsible = raw === null || raw === '' || raw === 'true' ? true : raw === 'false' ? false : raw === 'start' || raw === 'end' ? raw : true;
23
+ const labels = Array.isArray(options.labels) && options.labels.length === 2 ? (options.labels as [string, string]) : undefined;
24
+ return createSplitView(this, { ...options, collapsible, labels });
25
+ }
26
+
27
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
28
+ if (previous === next || !this.instance) return;
29
+ if (name === 'ratio' && next !== null && Number.isFinite(Number(next))) this.instance.setRatio(Number(next));
30
+ else super.attributeChangedCallback(name, previous, next);
31
+ }
32
+ }
33
+
34
+ export const defineSplitView = () => defineElement('mk-split-view', MkSplitViewElement);
@@ -0,0 +1,114 @@
1
+ import { registerMotion } from '../../core/gsap';
2
+ import { createScope } from '../../core/scope';
3
+ import { inert, type Instance } from '../../core/types';
4
+ import { createResizablePanel, type PanelOrientation } from '../resizable-panel/engine';
5
+
6
+ /*
7
+ * Split view: two areas side by side (or one over the other) with
8
+ * resizable-panel's separator between them: it drags with a rubber band at the
9
+ * limits, keys, snaps a side shut when dragged well past its minimum, and a
10
+ * double click centres it back. Its buttons shut either side and open it
11
+ * again (.3 s, swing). setRatio() glides to a split (.45 s, signature). Under
12
+ * `stackBelow` pixels of width the areas stack, the separator turning with
13
+ * them. The split can be remembered (storageKey).
14
+ */
15
+
16
+ export type SplitSide = 'start' | 'end';
17
+
18
+ export interface SplitViewOptions {
19
+ /** 'horizontal' (default): side by side; 'vertical': one over the other. */
20
+ orientation?: PanelOrientation;
21
+ /** The first area's share, 0–1 (default .5). */
22
+ ratio?: number;
23
+ /** Smallest share of an area in percent (default 15). */
24
+ min?: number;
25
+ /** Which sides can shut: true (default) both, 'start', 'end' or false. */
26
+ collapsible?: boolean | SplitSide;
27
+ /** Stack the areas when the view is narrower than this many pixels (default 0: never). */
28
+ stackBelow?: number;
29
+ storageKey?: string;
30
+ /** Names of the two areas (default 'Primera área', 'Segunda área'; or their data-label). */
31
+ labels?: [string, string];
32
+ /** After a change of the split ends. */
33
+ onChange?: (ratio: number) => void;
34
+ onCollapse?: (side: SplitSide | null) => void;
35
+ }
36
+
37
+ export interface SplitViewInstance extends Instance {
38
+ setRatio(ratio: number, animate?: boolean): void;
39
+ collapse(side: SplitSide): void;
40
+ /** Open the side that is shut. */
41
+ expand(): void;
42
+ toggle(side: SplitSide): void;
43
+ readonly ratio: number;
44
+ readonly collapsed: SplitSide | null;
45
+ readonly orientation: PanelOrientation;
46
+ }
47
+
48
+ export function createSplitView(root: HTMLElement | null, options: SplitViewOptions = {}): SplitViewInstance {
49
+ const areas = root ? [...root.children].filter((child): child is HTMLElement => child instanceof HTMLElement) : [];
50
+ if (!root || areas.length !== 2) {
51
+ return inert({ setRatio() {}, collapse() {}, expand() {}, toggle() {}, ratio: 0.5, collapsed: null, orientation: 'horizontal' as PanelOrientation });
52
+ }
53
+ registerMotion();
54
+ const scope = createScope();
55
+ const ratio = Math.min(1, Math.max(0, Number.isFinite(options.ratio) ? options.ratio! : 0.5));
56
+ const sides = options.collapsible === false ? [] : options.collapsible === 'start' ? [0] : options.collapsible === 'end' ? [1] : [0, 1];
57
+ const preferred: PanelOrientation = options.orientation === 'vertical' ? 'vertical' : 'horizontal';
58
+ const sideOf = (index: number): SplitSide => (index === 0 ? 'start' : 'end');
59
+ scope.attr(root, 'data-mk-split', '');
60
+ const panel = createResizablePanel(root, {
61
+ orientation: preferred,
62
+ sizes: [ratio * 100, (1 - ratio) * 100],
63
+ min: options.min ?? 15,
64
+ collapsible: sides,
65
+ handleButtons: sides.length > 0,
66
+ storageKey: options.storageKey,
67
+ labels: options.labels ?? [areas[0]!.dataset.label ?? 'Primera área', areas[1]!.dataset.label ?? 'Segunda área'],
68
+ onResizeEnd: (sizes) => options.onChange?.(Math.round(sizes[0]! * 10) / 1000),
69
+ onCollapse: (index, shut) => options.onCollapse?.(shut ? sideOf(index) : null),
70
+ });
71
+ scope.add(() => panel.destroy());
72
+
73
+ // Narrow: stacked.
74
+ if (options.stackBelow && options.stackBelow > 0 && typeof ResizeObserver === 'function') {
75
+ const limit = options.stackBelow;
76
+ scope.observe(
77
+ new ResizeObserver(() => {
78
+ const width = root.getBoundingClientRect().width;
79
+ panel.setOrientation(width && width < limit ? 'vertical' : preferred);
80
+ }),
81
+ ).observe(root);
82
+ }
83
+
84
+ const collapsedSide = () => (panel.collapsed.includes(0) ? 'start' : panel.collapsed.includes(1) ? 'end' : null);
85
+ return {
86
+ setRatio(next, animate = true) {
87
+ const share = Math.min(1, Math.max(0, next));
88
+ panel.setSizes([share * 100, (1 - share) * 100], animate);
89
+ },
90
+ collapse(side) {
91
+ const other = side === 'start' ? 1 : 0;
92
+ if (panel.collapsed.includes(other)) panel.expand(other);
93
+ panel.collapse(side === 'start' ? 0 : 1);
94
+ },
95
+ expand() {
96
+ for (const index of panel.collapsed) panel.expand(index);
97
+ },
98
+ toggle(side) {
99
+ const index = side === 'start' ? 0 : 1;
100
+ if (panel.collapsed.includes(index)) panel.expand(index);
101
+ else this.collapse(side);
102
+ },
103
+ get ratio() {
104
+ return Math.round(panel.sizes[0]! * 10) / 1000;
105
+ },
106
+ get collapsed() {
107
+ return collapsedSide();
108
+ },
109
+ get orientation() {
110
+ return panel.orientation;
111
+ },
112
+ destroy: () => scope.dispose(),
113
+ };
114
+ }
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+ import { forwardRef, useImperativeHandle, useRef, type HTMLAttributes, type ReactNode } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createSplitView, type SplitViewInstance, type SplitViewOptions } from './engine';
5
+
6
+ export interface SplitViewProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'children'>, SplitViewOptions {
7
+ /** The two areas. */
8
+ children: [ReactNode, ReactNode];
9
+ }
10
+
11
+ export type SplitViewHandle = Pick<SplitViewInstance, 'setRatio' | 'collapse' | 'expand' | 'toggle'>;
12
+
13
+ /** <SplitView ratio={0.35} stackBelow={640} labels={['Bandeja', 'Mensaje']}><List /><Detail /></SplitView>. A new `ratio` glides there. */
14
+ export const SplitView = forwardRef<SplitViewHandle, SplitViewProps>(function SplitView(
15
+ { orientation, ratio, min, collapsible, stackBelow, storageKey, labels, onChange, onCollapse, className, children, ...props },
16
+ handle,
17
+ ) {
18
+ const ref = useRef<HTMLDivElement>(null);
19
+ const instance = useRef<SplitViewInstance | null>(null);
20
+ const change = useLatest(onChange);
21
+ const collapse = useLatest(onCollapse);
22
+ const shown = useRef(ratio);
23
+ useImperativeHandle(
24
+ handle,
25
+ () => ({
26
+ setRatio: (next, animate) => instance.current?.setRatio(next, animate),
27
+ collapse: (side) => instance.current?.collapse(side),
28
+ expand: () => instance.current?.expand(),
29
+ toggle: (side) => instance.current?.toggle(side),
30
+ }),
31
+ [],
32
+ );
33
+ useIsomorphicLayoutEffect(() => {
34
+ const created = createSplitView(ref.current, {
35
+ orientation,
36
+ ratio: shown.current,
37
+ min,
38
+ collapsible,
39
+ stackBelow,
40
+ storageKey,
41
+ labels,
42
+ onChange: (next) => change.current?.(next),
43
+ onCollapse: (side) => collapse.current?.(side),
44
+ });
45
+ instance.current = created;
46
+ return () => {
47
+ created.destroy();
48
+ instance.current = null;
49
+ };
50
+ // eslint-disable-next-line react-hooks/exhaustive-deps
51
+ }, [orientation, min, String(collapsible), stackBelow, storageKey, String(labels)]);
52
+ useIsomorphicLayoutEffect(() => {
53
+ if (ratio === undefined || ratio === shown.current) return;
54
+ shown.current = ratio;
55
+ instance.current?.setRatio(ratio);
56
+ }, [ratio]);
57
+ return (
58
+ <div {...props} ref={ref} className={['mk-split-view', className].filter(Boolean).join(' ')}>
59
+ {children[0]}
60
+ {children[1]}
61
+ </div>
62
+ );
63
+ });
@@ -0,0 +1,18 @@
1
+ /* split-view — two areas on a 20 px panel (surface-1, outline-soft border) with resizable-panel's separator between
2
+ them (its hairline, grip, bubble and the buttons that shut a side). Give it a height; each area scrolls on its own. */
3
+ .mk-split-view,
4
+ mk-split-view {
5
+ position: relative;
6
+ box-sizing: border-box;
7
+ display: flex;
8
+ min-width: 0;
9
+ min-height: 12rem;
10
+ overflow: hidden;
11
+ border: 1px solid var(--mk-outline-soft);
12
+ border-radius: 20px;
13
+ background: var(--mk-surface-1);
14
+ }
15
+
16
+ :is(.mk-split-view, mk-split-view)[data-mk-orientation='vertical'] {
17
+ flex-direction: column;
18
+ }
@@ -0,0 +1,14 @@
1
+ # star-rating
2
+
3
+ Valoración con estrellas sobre radios nativos (flechas del teclado y formularios incluidos). Al señalar, las estrellas se previsualizan en cascada de 20 ms; al elegir, la estrella rebota desde 1.4 con curva elástica, salta un destello y la etiqueta («Excelente») cambia con blur. `allowClear` permite quitar la valoración pulsando la estrella elegida. `readonly` dibuja un valor fijo (con fracciones) como imagen con su valor de nombre.
4
+
5
+ ```html
6
+ <mk-star-rating class="mk-star-rating" name="valoracion" value="4" allow-clear></mk-star-rating>
7
+ <mk-star-rating class="mk-star-rating" readonly value="4.5"></mk-star-rating>
8
+ ```
9
+
10
+ `defineElements()`, `createStarRating(root, options)` o `<StarRating value={stars} onChange={setStars} />` (con `value` es controlado; `defaultValue` si no).
11
+
12
+ Opciones: `max=5`, `value=0`, `name` (formulario), `label='Valoración'`, `labels` (un texto por estrella; por defecto Mala → Excelente), `readonly`, `allowClear`, `disabled`, `onChange(value)`. API: `setValue(n, notify=false)`, `value`, `destroy()`. Evento `mk:change` con `{value}`.
13
+
14
+ Variables: `--mk-star-size` (2rem), `--mk-star-color` (#f5b301), `--mk-star-empty`. Movimiento reducido: sin rebote, cascada ni destello.
@@ -0,0 +1,26 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createStarRating, type StarRatingInstance } from './engine';
3
+
4
+ /**
5
+ * <mk-star-rating class="mk-star-rating" name="valoracion" value="4" allow-clear></mk-star-rating>
6
+ * readonly with value="4.5" draws a fixed rating. Emits mk:change ({value}).
7
+ */
8
+ export class MkStarRatingElement extends MkElement<StarRatingInstance> {
9
+ static options: Record<string, OptionType> = { max: 'number', value: 'number', name: 'string', label: 'string', labels: 'list', readonly: 'boolean', allowClear: 'boolean', disabled: 'boolean' };
10
+
11
+ protected create(options: Record<string, unknown>) {
12
+ return createStarRating(this, options);
13
+ }
14
+
15
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
16
+ if (previous === next || !this.instance) return;
17
+ if (name === 'value') this.instance.setValue(Number(next) || 0);
18
+ else super.attributeChangedCallback(name, previous, next);
19
+ }
20
+
21
+ get value() {
22
+ return this.instance?.value ?? 0;
23
+ }
24
+ }
25
+
26
+ export const defineStarRating = () => defineElement('mk-star-rating', MkStarRatingElement);
@@ -0,0 +1,217 @@
1
+ import { gsap, ELASTIC, registerMotion } from '../../core/gsap';
2
+ import { reduced } from '../../core/reduced';
3
+ import { numberFormat } from '../../core/dates';
4
+ import { createScope } from '../../core/scope';
5
+ import { inert, type Instance } from '../../core/types';
6
+ import { burst } from '../../motion/micro';
7
+
8
+ /*
9
+ * Star rating on native radios (arrow keys and forms come from the platform).
10
+ * Pointing previews the stars in a 20 ms cascade; choosing one springs it from
11
+ * 1.4 (elastic), throws a small burst and the label ("Excelente") changes with
12
+ * a blur. allowClear lets a click on the chosen star clear it. readonly draws a
13
+ * value (fractions too) as an image with its value as the name.
14
+ */
15
+
16
+ export interface StarRatingOptions {
17
+ max?: number;
18
+ value?: number;
19
+ /** Form name (default 'rating'). */
20
+ name?: string;
21
+ /** Accessible name of the group (default 'Valoración'). */
22
+ label?: string;
23
+ /** One text per star, shown and read for the chosen value. */
24
+ labels?: string[];
25
+ readonly?: boolean;
26
+ allowClear?: boolean;
27
+ disabled?: boolean;
28
+ onChange?: (value: number) => void;
29
+ }
30
+
31
+ export interface StarRatingInstance extends Instance {
32
+ setValue(value: number, notify?: boolean): void;
33
+ readonly value: number;
34
+ }
35
+
36
+ const STAR = 'M12 3.2l2.6 5.4 5.9.8-4.3 4.1 1 5.9L12 16.6l-5.2 2.8 1-5.9-4.3-4.1 5.9-.8z';
37
+ const LABELS = ['Mala', 'Regular', 'Buena', 'Muy buena', 'Excelente'];
38
+ let sequence = 0;
39
+
40
+ const star = () => {
41
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
42
+ svg.setAttribute('viewBox', '0 0 24 24');
43
+ svg.setAttribute('aria-hidden', 'true');
44
+ const back = document.createElementNS('http://www.w3.org/2000/svg', 'path');
45
+ back.setAttribute('d', STAR);
46
+ back.dataset.mkPart = 'empty';
47
+ const front = document.createElementNS('http://www.w3.org/2000/svg', 'path');
48
+ front.setAttribute('d', STAR);
49
+ front.dataset.mkPart = 'full';
50
+ svg.append(back, front);
51
+ return svg;
52
+ };
53
+
54
+ export function createStarRating(root: HTMLElement | null, options: StarRatingOptions = {}): StarRatingInstance {
55
+ if (!root) return inert({ setValue() {}, value: 0 });
56
+ registerMotion();
57
+ const scope = createScope();
58
+ const max = Math.max(1, Math.min(10, Math.round(Number.isFinite(options.max) ? options.max! : 5)));
59
+ const texts = options.labels ?? (max === 5 ? LABELS : []);
60
+ const clamp = (n: number) => Math.max(0, Math.min(max, n));
61
+ let value = clamp(Number.isFinite(options.value) ? options.value! : 0);
62
+ const name = options.name ?? `rating-${++sequence}`;
63
+
64
+ const group = document.createElement('div');
65
+ group.dataset.mkStars = '';
66
+ const caption = document.createElement('span');
67
+ caption.dataset.mkStarLabel = '';
68
+ root.append(group, caption);
69
+ scope.add(() => {
70
+ group.remove();
71
+ caption.remove();
72
+ });
73
+ scope.attr(root, 'data-mk-ready', '');
74
+ scope.attr(root, 'style', root.getAttribute('style'));
75
+
76
+ const stars: HTMLElement[] = [];
77
+ const inputs: HTMLInputElement[] = [];
78
+ if (options.readonly) {
79
+ group.setAttribute('role', 'img');
80
+ for (let i = 1; i <= max; i++) {
81
+ const item = document.createElement('span');
82
+ item.dataset.mkStar = String(i);
83
+ item.append(star());
84
+ group.append(item);
85
+ stars.push(item);
86
+ }
87
+ } else {
88
+ group.setAttribute('role', 'radiogroup');
89
+ group.setAttribute('aria-label', options.label ?? 'Valoración');
90
+ for (let i = 1; i <= max; i++) {
91
+ const item = document.createElement('label');
92
+ item.dataset.mkStar = String(i);
93
+ const input = document.createElement('input');
94
+ input.type = 'radio';
95
+ input.name = name;
96
+ input.value = String(i);
97
+ input.className = 'mk-sr-only';
98
+ input.disabled = Boolean(options.disabled);
99
+ input.setAttribute('aria-label', texts[i - 1] ? `${i} de ${max}: ${texts[i - 1]}` : `${i} de ${max}`);
100
+ item.append(input, star());
101
+ group.append(item);
102
+ stars.push(item);
103
+ inputs.push(input);
104
+ }
105
+ }
106
+
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
+ scope.add(() => {
115
+ for (const effect of effects) effect.kill();
116
+ effects.clear();
117
+ });
118
+
119
+ const svgs = () => stars.map((item) => item.querySelector('svg')!);
120
+ /** Light the stars up to `upto` (fractions fill the last one partly). */
121
+ const light = (upto: number) => {
122
+ stars.forEach((item, i) => {
123
+ const fill = Math.max(0, Math.min(1, upto - i));
124
+ item.style.setProperty('--mk-star-fill', `${(fill * 100).toFixed(1)}%`);
125
+ item.toggleAttribute('data-mk-on', fill >= 1);
126
+ });
127
+ };
128
+ const describe = () => {
129
+ const text = value ? texts[Math.ceil(value) - 1] ?? '' : '';
130
+ if (options.readonly) {
131
+ const shown = numberFormat(root, { maximumFractionDigits: 1 }).format(value);
132
+ group.setAttribute('aria-label', `${options.label ?? 'Valoración'}: ${shown} de ${max}`);
133
+ caption.textContent = shown;
134
+ return;
135
+ }
136
+ if (caption.textContent === text) return;
137
+ if (reduced() || !caption.textContent) {
138
+ caption.textContent = text;
139
+ return;
140
+ }
141
+ gsap.killTweensOf(caption);
142
+ keep(
143
+ gsap
144
+ .timeline()
145
+ .to(caption, { opacity: 0, filter: 'blur(4px)', y: -3, duration: 0.12 })
146
+ .call(() => {
147
+ caption.textContent = text;
148
+ })
149
+ .fromTo(caption, { opacity: 0, filter: 'blur(4px)', y: 3 }, { opacity: 1, filter: 'blur(0px)', y: 0, duration: 0.25, clearProps: 'all' }),
150
+ );
151
+ };
152
+ const paint = () => {
153
+ inputs.forEach((input, i) => (input.checked = i + 1 === value));
154
+ light(value);
155
+ describe();
156
+ };
157
+
158
+ const choose = (next: number, notify: boolean, animate: boolean) => {
159
+ if (scope.disposed) return;
160
+ const target = clamp(Math.round(next));
161
+ if (target === value) return;
162
+ value = target;
163
+ paint();
164
+ if (animate && target && !reduced()) {
165
+ const item = stars[target - 1]!;
166
+ keep(gsap.fromTo(item.querySelector('svg'), { scale: 1.45, rotation: -12 }, { scale: 1, rotation: 0, duration: 0.8, ease: ELASTIC, clearProps: 'all' }));
167
+ keep(gsap.fromTo(svgs().slice(0, target - 1), { scale: 0.8 }, { scale: 1, duration: 0.4, ease: 'back.out(3)', stagger: 0.03, clearProps: 'all' }));
168
+ keep(burst(item, { count: 6, distance: 22, size: 5, colors: ['var(--mk-star-color)'] }));
169
+ }
170
+ if (!notify) return;
171
+ options.onChange?.(value);
172
+ root.dispatchEvent(new CustomEvent('mk:change', { bubbles: true, detail: { value } }));
173
+ };
174
+
175
+ if (!options.readonly) {
176
+ let preview = 0;
177
+ scope.on(group, 'pointerover', (event) => {
178
+ const item = (event.target as Element | null)?.closest<HTMLElement>('[data-mk-star]');
179
+ if (!item || options.disabled) return;
180
+ const upto = Number(item.dataset.mkStar);
181
+ if (upto === preview) return;
182
+ const from = preview;
183
+ preview = upto;
184
+ scope.attr(root, 'data-mk-preview', String(upto));
185
+ light(upto);
186
+ if (!reduced() && upto > from) keep(gsap.fromTo(svgs().slice(from, upto), { scale: 0.85 }, { scale: 1, duration: 0.3, ease: 'back.out(3)', stagger: 0.02, clearProps: 'all' }));
187
+ });
188
+ scope.on(group, 'pointerleave', () => {
189
+ preview = 0;
190
+ scope.attr(root, 'data-mk-preview', null);
191
+ light(value);
192
+ });
193
+ // A click on the chosen star (allowClear) clears it: radios cannot uncheck themselves.
194
+ let before = 0;
195
+ scope.on(group, 'pointerdown', () => {
196
+ before = value;
197
+ });
198
+ scope.on(group, 'click', (event) => {
199
+ const input = (event.target as Element | null)?.closest('input');
200
+ if (!input || !options.allowClear) return;
201
+ if (Number(input.value) === before && before === value) {
202
+ before = -1;
203
+ choose(0, true, false);
204
+ }
205
+ });
206
+ for (const input of inputs) scope.on(input, 'change', () => choose(Number(input.value), true, true));
207
+ }
208
+ paint();
209
+
210
+ return {
211
+ setValue: (next, notify = false) => choose(next, notify, !notify),
212
+ get value() {
213
+ return value;
214
+ },
215
+ destroy: () => scope.dispose(),
216
+ };
217
+ }
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+ import { useRef, type HTMLAttributes } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createStarRating, type StarRatingInstance, type StarRatingOptions } from './engine';
5
+
6
+ export interface StarRatingProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'>, Omit<StarRatingOptions, 'value'> {
7
+ /** Controlled value; without it the stars keep their own (defaultValue). */
8
+ value?: number;
9
+ defaultValue?: number;
10
+ }
11
+
12
+ /** <StarRating value={stars} onChange={setStars} allowClear /> or <StarRating readonly value={4.5} />. */
13
+ export function StarRating({ value, defaultValue, max, name, label, labels, readonly, allowClear, disabled, onChange, className, ...props }: StarRatingProps) {
14
+ const ref = useRef<HTMLDivElement>(null);
15
+ const instance = useRef<StarRatingInstance | null>(null);
16
+ const change = useLatest(onChange);
17
+ const start = useRef(value ?? defaultValue ?? 0);
18
+ const key = JSON.stringify(labels ?? null);
19
+ useIsomorphicLayoutEffect(() => {
20
+ const created = createStarRating(ref.current, { value: value ?? start.current, max, name, label, labels, readonly, allowClear, disabled, onChange: (next) => change.current?.(next) });
21
+ instance.current = created;
22
+ return () => {
23
+ start.current = created.value;
24
+ created.destroy();
25
+ instance.current = null;
26
+ };
27
+ // eslint-disable-next-line react-hooks/exhaustive-deps
28
+ }, [max, name, label, key, readonly, allowClear, disabled]);
29
+ useIsomorphicLayoutEffect(() => {
30
+ if (value !== undefined && instance.current && instance.current.value !== value) instance.current.setValue(value);
31
+ }, [value]);
32
+ return <div {...props} ref={ref} className={['mk-star-rating', className].filter(Boolean).join(' ')} />;
33
+ }
@@ -0,0 +1,79 @@
1
+ /* star-rating — stars outlined in the surface tone, filled with the star colour; a label beside them.
2
+ Hover previews; the focus ring sits on the star of the focused radio. */
3
+ .mk-star-rating,
4
+ mk-star-rating {
5
+ --mk-star-size: 2rem;
6
+ --mk-star-color: #f5b301;
7
+ --mk-star-empty: var(--mk-surface-4);
8
+ display: inline-flex;
9
+ align-items: center;
10
+ gap: 0.75rem;
11
+ color: var(--mk-text);
12
+ font-family: var(--mk-font);
13
+ }
14
+
15
+ :is(.mk-star-rating, mk-star-rating) [data-mk-stars] {
16
+ display: inline-flex;
17
+ gap: 0.15rem;
18
+ }
19
+
20
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star] {
21
+ position: relative;
22
+ display: grid;
23
+ place-items: center;
24
+ width: calc(var(--mk-star-size) + 0.5rem);
25
+ height: calc(var(--mk-star-size) + 0.5rem);
26
+ border-radius: 50%;
27
+ cursor: pointer;
28
+ }
29
+
30
+ :is(.mk-star-rating, mk-star-rating) [role='img'] [data-mk-star] {
31
+ width: var(--mk-star-size);
32
+ height: var(--mk-star-size);
33
+ cursor: default;
34
+ }
35
+
36
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star] svg {
37
+ width: var(--mk-star-size);
38
+ height: var(--mk-star-size);
39
+ overflow: visible;
40
+ }
41
+
42
+ :is(.mk-star-rating, mk-star-rating) [data-mk-part='empty'] {
43
+ fill: var(--mk-star-empty);
44
+ }
45
+
46
+ :is(.mk-star-rating, mk-star-rating) [data-mk-part='full'] {
47
+ fill: var(--mk-star-color);
48
+ clip-path: inset(0 calc(100% - var(--mk-star-fill, 0%)) 0 0);
49
+ transition: clip-path 0.15s ease;
50
+ }
51
+
52
+ /* While previewing, lit stars are a step lighter. */
53
+ :is(.mk-star-rating, mk-star-rating)[data-mk-preview] [data-mk-part='full'] {
54
+ fill: color-mix(in srgb, var(--mk-star-color) 75%, white);
55
+ }
56
+
57
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star]:has(input:focus-visible) {
58
+ outline: 2px solid var(--mk-accent);
59
+ outline-offset: 1px;
60
+ }
61
+
62
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star]:has(input:disabled) {
63
+ cursor: not-allowed;
64
+ opacity: 0.5;
65
+ }
66
+
67
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star-label] {
68
+ min-width: 5.5rem;
69
+ color: var(--mk-muted);
70
+ font-size: 0.9375rem;
71
+ font-weight: 500;
72
+ font-variant-numeric: tabular-nums;
73
+ }
74
+
75
+ @media (prefers-reduced-motion: reduce) {
76
+ :is(.mk-star-rating, mk-star-rating) [data-mk-part='full'] {
77
+ transition: none;
78
+ }
79
+ }
@@ -0,0 +1,42 @@
1
+ # stat-card
2
+
3
+ Tarjeta de KPI con su tendencia. Cuando entra en pantalla, la cifra cuenta hasta su valor (.9 s) con el formato del idioma de la página (moneda, porcentaje, compacto). La variación respecto a `previous` va en una insignia (▲ o ▼ y el porcentaje): verde cuando va en la buena dirección (`positive` dice cuál), roja cuando no, neutra si no cambia. Aparece con un pop mientras la flecha da un empujón. Una minigráfica de `series` se dibuja sola (.9 s) con un área suave y un punto final que rebota; `target` llena una barra de meta.
4
+
5
+ `update()` cuenta desde la cifra mostrada hasta la nueva, hace rodar los dígitos de la insignia, gira la flecha si cambia la tendencia y transforma la minigráfica hacia los puntos nuevos.
6
+
7
+ ## HTML / Astro
8
+
9
+ ```html
10
+ <mk-stat-card class="mk-stat-card" label="Ingresos" value="128400" previous="118900"
11
+ format='{"style":"currency","currency":"MXN","maximumFractionDigits":0}'
12
+ series="90,98,104,101,118,128" target="150000"></mk-stat-card>
13
+ ```
14
+
15
+ ## React
16
+
17
+ ```tsx
18
+ <StatCard label="Cancelaciones" value={42} previous={51} positive="down" comparison="vs. mes pasado" series={trend} />
19
+ ```
20
+
21
+ ## Opciones
22
+
23
+ | Opción | Atributo | Por defecto | Qué hace |
24
+ |---|---|---|---|
25
+ | `label` | `label` | — | Nombre del indicador. |
26
+ | `value` | `value` | — | Cifra actual. |
27
+ | `previous` | `previous` | — | Cifra con la que se compara. |
28
+ | `format` | `format` (JSON) | 1 decimal | Opciones de `Intl.NumberFormat` o una función. |
29
+ | `locale` | `locale` | `lang` de la página | Idioma de los números. |
30
+ | `positive` | `positive` | `'up'` | Qué dirección es buena (`down` para costos, bajas, latencia). |
31
+ | `comparison` | `comparison` | `'vs. periodo anterior'` | Con qué se compara. |
32
+ | `series` | `series` (lista con comas) | — | Valores de la minigráfica (dos o más). |
33
+ | `target` | `target` | — | Meta: una barra muestra el avance. |
34
+ | `countOnView` | `count-on-view` | `true` | Contar al entrar en pantalla; `false` la muestra al momento. |
35
+
36
+ ## API
37
+
38
+ `update({ value, previous, series, target, … })`, `destroy()`.
39
+
40
+ ## Accesibilidad y estilo
41
+
42
+ La cifra visible es `aria-hidden`; una frase con los valores finales habla por ella («Ingresos: $128,400. Sube 8,0 % vs. periodo anterior.»). Tarjeta de 20 px sobre `--mk-surface-1`. Variables: `--mk-stat-good`, `--mk-stat-bad`, `--mk-stat-line`. Con movimiento reducido todo aparece en su valor final.