@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,24 @@
1
+ /* popover — an anchored .mk-dialog (needs dialog.css): 20 px corners on surface-1, as wide as its content up to
2
+ 22rem, 1rem padding. Styled by class: the copy that folds back into the trigger keeps classes only. */
3
+ .mk-popover,
4
+ mk-popover {
5
+ display: inline-block;
6
+ }
7
+
8
+ .mk-dialog.mk-popover-panel {
9
+ --mk-dialog-radius: 20px;
10
+ --mk-dialog-bg: var(--mk-surface-1);
11
+ --mk-dialog-shadow: 0 16px 48px -12px color-mix(in srgb, var(--mk-shadow-color, #000) 35%, transparent);
12
+ gap: 0.75rem;
13
+ width: max-content;
14
+ min-width: min(14rem, calc(100vw - 32px));
15
+ max-width: min(22rem, calc(100vw - 32px));
16
+ padding: 1rem;
17
+ }
18
+
19
+ /* Phones: the anchored dialog is centred by dialog.css; keep it a panel, not a sheet. */
20
+ @media (max-width: 679px) {
21
+ .mk-dialog.mk-popover-panel {
22
+ max-width: calc(100vw - 32px);
23
+ }
24
+ }
@@ -0,0 +1,50 @@
1
+ 'use client';
2
+ import { forwardRef, useImperativeHandle, useRef, type HTMLAttributes, type ReactNode } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createPopover, type PopoverInstance, type PopoverOptions } from './engine';
5
+
6
+ export interface PopoverProps extends Omit<HTMLAttributes<HTMLDivElement>, 'content'>, PopoverOptions {
7
+ /** The control that opens it (a button). */
8
+ trigger: ReactNode;
9
+ /** Controlled open state (optional). */
10
+ open?: boolean;
11
+ /** Extra class for the panel. */
12
+ panelClassName?: string;
13
+ }
14
+
15
+ export interface PopoverHandle {
16
+ open(): void;
17
+ close(): void;
18
+ }
19
+
20
+ /** <Popover trigger={<button>Filtros</button>}>…</Popover>: the panel grows out of the trigger. */
21
+ export const Popover = forwardRef<PopoverHandle, PopoverProps>(function Popover({ trigger, open, label, onOpenChange, panelClassName, className, children, ...props }, handle) {
22
+ const ref = useRef<HTMLDivElement>(null);
23
+ const instance = useRef<PopoverInstance | null>(null);
24
+ const change = useLatest(onOpenChange);
25
+ useImperativeHandle(handle, () => ({ open: () => instance.current?.open(), close: () => instance.current?.close() }), []);
26
+ useIsomorphicLayoutEffect(() => {
27
+ const created = createPopover(ref.current, { label, onOpenChange: (value) => change.current?.(value) });
28
+ instance.current = created;
29
+ return () => {
30
+ created.destroy();
31
+ instance.current = null;
32
+ };
33
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34
+ }, [label]);
35
+ useIsomorphicLayoutEffect(() => {
36
+ if (open === undefined || !instance.current || instance.current.isOpen === open) return;
37
+ if (open) instance.current.open();
38
+ else instance.current.close();
39
+ }, [open]);
40
+ return (
41
+ <div {...props} ref={ref} className={['mk-popover', className].filter(Boolean).join(' ')}>
42
+ <span data-mk-popover-trigger-slot="" style={{ display: 'contents' }}>
43
+ {trigger}
44
+ </span>
45
+ <dialog data-mk-popover-panel="" className={panelClassName}>
46
+ {children}
47
+ </dialog>
48
+ </div>
49
+ );
50
+ });
@@ -0,0 +1,17 @@
1
+ # progress
2
+
3
+ Barra o anillo de progreso con valor determinado o indeterminado. El anillo sigue el `MetricsRing` de Melon Mind: radio `(tamaño − trazo) / 2`, `stroke-dasharray` derivado del ratio y arranque a las 12.
4
+
5
+ ```html
6
+ <mk-progress class="mk-progress" value="40" label="Subiendo informe" show-value></mk-progress>
7
+ <mk-progress class="mk-progress" variant="ring" value="72" label="Meta mensual" show-value></mk-progress>
8
+ <mk-progress class="mk-progress" label="Procesando"></mk-progress> <!-- indeterminado -->
9
+ ```
10
+
11
+ `defineElements()` desde `/elements`, `createProgress(root, options)` o `<Progress value={42} label="Subida" showValue />` desde `/react`.
12
+
13
+ Opciones: `value` (número o `null` = indeterminado; por defecto `min`), `min=0`, `max=100`, `label` (aria-label), `variant='linear'|'ring'`, `showValue`, `formatValue(value, ratio)` (texto visible y `aria-valuetext`), `duration=.7` (segundos del deslizamiento). API: `setValue(number | null)`, `value`, `destroy()`.
14
+
15
+ El root recibe `role="progressbar"`, `aria-valuemin/max/now` (sin `aria-valuenow` cuando es indeterminado). Valores no finitos se ignoran y los demás se acotan a los límites. No hay región viva: el valor se lee al llegar a la barra, sin anunciar cada cambio.
16
+
17
+ El dibujo es un único hijo decorativo `[data-mk-progress-visual]`; el contenido que ya tenga el root se conserva y `destroy()` restaura atributos y estilos. Al subir o bajar, el relleno (o el arco) se desliza con GSAP, las cifras ruedan, la punta brilla al avanzar, el anillo respira y llegar al máximo lanza un estallido. Variables: `--mk-progress-height` (10px), `--mk-progress-size` (anillo, 4.5rem), `--mk-progress-track`, `--mk-progress-fill`. Movimiento reducido: el valor cambia sin transición y el indeterminado queda como relleno parcial estático.
@@ -0,0 +1,30 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createProgress, type ProgressInstance } from './engine';
3
+
4
+ /**
5
+ * <mk-progress class="mk-progress" value="40" label="Subida" show-value></mk-progress>
6
+ * Without `value` (or value="indeterminate") it is indeterminate. A new value moves the fill.
7
+ */
8
+ export class MkProgressElement extends MkElement<ProgressInstance> {
9
+ static options: Record<string, OptionType> = { value: 'number', min: 'number', max: 'number', label: 'string', variant: 'string', showValue: 'boolean' };
10
+
11
+ protected create(options: Record<string, unknown>) {
12
+ return createProgress(this, { ...options, value: typeof options.value === 'number' ? options.value : null });
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(next === null || !Number.isFinite(Number(next)) ? null : Number(next));
18
+ else super.attributeChangedCallback(name, previous, next);
19
+ }
20
+
21
+ setValue(value: number | null) {
22
+ this.instance?.setValue(value);
23
+ }
24
+
25
+ get value() {
26
+ return this.instance?.value ?? null;
27
+ }
28
+ }
29
+
30
+ export const defineProgress = () => defineElement('mk-progress', MkProgressElement);
@@ -0,0 +1,173 @@
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 { burst, rollDigits } from '../../motion/micro';
6
+
7
+ /*
8
+ * Progress: a bar or a ring (Melon Mind's MetricsRing: radius (size - stroke) / 2,
9
+ * dasharray from the ratio, starting at 12 o'clock). The root is the progressbar;
10
+ * the drawing is one decorative [data-mk-progress-visual] child. ARIA follows a
11
+ * new value at once; the drawing glides to it with GSAP (Melonmind's playful
12
+ * swing, .38,.49,0,1.5): going up, a light runs at the head and the figure rolls
13
+ * up; going down, the figure rolls down. Reaching the maximum fires a small burst.
14
+ * null is indeterminate. Nothing is live: assistive tech reads the value on arrival.
15
+ */
16
+
17
+ export type ProgressVariant = 'linear' | 'ring';
18
+
19
+ export interface ProgressOptions {
20
+ /** Current value; null is indeterminate. Default: min. */
21
+ value?: number | null;
22
+ min?: number;
23
+ max?: number;
24
+ label?: string;
25
+ variant?: ProgressVariant;
26
+ /** Write the percentage beside the bar (inside the ring). */
27
+ showValue?: boolean;
28
+ /** Text for the value (also aria-valuetext). Default: rounded percentage. */
29
+ formatValue?: (value: number, ratio: number) => string;
30
+ /** Glide duration in seconds (default .7). */
31
+ duration?: number;
32
+ }
33
+
34
+ export interface ProgressInstance extends Instance {
35
+ setValue(value: number | null): void;
36
+ readonly value: number | null;
37
+ }
38
+
39
+ const SVG = 'http://www.w3.org/2000/svg';
40
+ const RING_SIZE = 48;
41
+ const RING_STROKE = 5;
42
+ const RADIUS = (RING_SIZE - RING_STROKE) / 2;
43
+ const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
44
+
45
+ const finite = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value);
46
+
47
+ export function createProgress(root: HTMLElement | null, options: ProgressOptions = {}): ProgressInstance {
48
+ if (!root) return inert({ setValue() {}, value: null });
49
+ registerMotion();
50
+ const scope = createScope();
51
+ const min = finite(options.min) ? options.min : 0;
52
+ const max = finite(options.max) && options.max > min ? options.max : Math.max(min + 1, 100);
53
+ const variant: ProgressVariant = options.variant === 'ring' ? 'ring' : 'linear';
54
+ const duration = finite(options.duration) ? Math.max(0, options.duration) : 0.7;
55
+ const clamp = (n: number) => Math.min(max, Math.max(min, n));
56
+ let value: number | null = options.value === null ? null : clamp(finite(options.value) ? options.value : min);
57
+
58
+ const visual = document.createElement('span');
59
+ visual.dataset.mkProgressVisual = '';
60
+ visual.setAttribute('aria-hidden', 'true');
61
+ let arc: SVGCircleElement | null = null;
62
+ let head: HTMLElement | null = null;
63
+ let ring: SVGSVGElement | null = null;
64
+ if (variant === 'ring') {
65
+ ring = document.createElementNS(SVG, 'svg');
66
+ ring.setAttribute('viewBox', `0 0 ${RING_SIZE} ${RING_SIZE}`);
67
+ ring.dataset.mkPart = 'ring';
68
+ const circle = (part: string) => {
69
+ const element = document.createElementNS(SVG, 'circle');
70
+ element.setAttribute('cx', String(RING_SIZE / 2));
71
+ element.setAttribute('cy', String(RING_SIZE / 2));
72
+ element.setAttribute('r', String(RADIUS));
73
+ element.setAttribute('fill', 'none');
74
+ element.setAttribute('stroke-width', String(RING_STROKE));
75
+ element.dataset.mkPart = part;
76
+ ring!.append(element);
77
+ return element;
78
+ };
79
+ circle('track');
80
+ arc = circle('arc');
81
+ arc.setAttribute('stroke-linecap', 'round');
82
+ visual.append(ring);
83
+ } else {
84
+ const track = document.createElement('span');
85
+ track.dataset.mkPart = 'track';
86
+ const bar = document.createElement('span');
87
+ bar.dataset.mkPart = 'bar';
88
+ head = document.createElement('span');
89
+ head.dataset.mkPart = 'head';
90
+ track.append(bar, head);
91
+ visual.append(track);
92
+ }
93
+ const text = options.showValue ? document.createElement('span') : null;
94
+ if (text) {
95
+ text.dataset.mkPart = 'value';
96
+ visual.append(text);
97
+ }
98
+ root.append(visual);
99
+
100
+ scope.attr(root, 'style', root.getAttribute('style'));
101
+ scope.attr(root, 'role', 'progressbar');
102
+ scope.attr(root, 'aria-valuemin', String(min));
103
+ scope.attr(root, 'aria-valuemax', String(max));
104
+ if (options.label) scope.attr(root, 'aria-label', options.label);
105
+ scope.attr(root, 'data-mk-variant', variant);
106
+ scope.attr(root, 'data-mk-ready', '');
107
+
108
+ const ratioOf = (n: number | null) => (n === null ? 0 : (n - min) / (max - min));
109
+ const labelOf = (n: number | null) => (n === null ? '' : options.formatValue ? options.formatValue(n, ratioOf(n)) : `${Math.round(ratioOf(n) * 100)} %`);
110
+ /** What is drawn now (it trails `value` while gliding). */
111
+ const shown = { ratio: ratioOf(value) };
112
+ const draw = () => {
113
+ const ratio = Math.min(1, Math.max(0, shown.ratio));
114
+ root.style.setProperty('--mk-progress', ratio.toFixed(4));
115
+ if (arc) {
116
+ const length = value === null ? CIRCUMFERENCE * 0.25 : CIRCUMFERENCE * ratio;
117
+ arc.setAttribute('stroke-dasharray', `${length.toFixed(2)} ${CIRCUMFERENCE.toFixed(2)}`);
118
+ }
119
+ };
120
+ let glide: gsap.core.Tween | null = null;
121
+ const motion: gsap.core.Animation[] = [];
122
+ scope.add(() => {
123
+ glide?.kill();
124
+ for (const animation of motion.splice(0)) animation.progress(1).kill();
125
+ visual.remove();
126
+ });
127
+
128
+ const render = (previous: number | null, animate: boolean) => {
129
+ const label = labelOf(value);
130
+ scope.attr(root, 'aria-valuenow', value === null ? null : String(value));
131
+ scope.attr(root, 'aria-valuetext', value !== null && options.formatValue ? label : null);
132
+ scope.attr(root, 'data-mk-indeterminate', value === null ? '' : null);
133
+ scope.attr(root, 'data-mk-complete', value !== null && value >= max ? '' : null);
134
+ const target = ratioOf(value);
135
+ const direction: 1 | -1 = previous === null || value === null || value >= previous ? 1 : -1;
136
+ glide?.kill();
137
+ if (!animate || reduced() || value === null || previous === null || duration === 0) {
138
+ shown.ratio = target;
139
+ draw();
140
+ if (text) text.textContent = label;
141
+ return;
142
+ }
143
+ glide = gsap.to(shown, { ratio: target, duration, ease: EASE.swatch, onUpdate: draw });
144
+ if (text) {
145
+ const roll = rollDigits(text, label, direction);
146
+ if (roll) motion.push(roll);
147
+ }
148
+ // Going up: a light runs at the head of the bar; the ring breathes.
149
+ if (direction > 0 && head) motion.push(gsap.fromTo(head, { opacity: 1, scale: 0.4 }, { opacity: 0, scale: 1.6, duration: 0.6, ease: 'power2.out' }));
150
+ if (ring) motion.push(gsap.fromTo(ring, { scale: direction > 0 ? 1.06 : 0.95 }, { scale: 1, duration: 0.5, ease: EASE.click, clearProps: 'scale,transform' }));
151
+ if (value >= max && previous < max) {
152
+ const done = burst(variant === 'ring' ? visual : (text ?? visual), { count: 6, distance: 22, ring: variant === 'ring' });
153
+ if (done) motion.push(done);
154
+ }
155
+ if (motion.length > 12) motion.splice(0, motion.length - 12);
156
+ };
157
+ render(null, false);
158
+
159
+ return {
160
+ setValue(next) {
161
+ if (scope.disposed) return;
162
+ const previous = value;
163
+ if (next === null) value = null;
164
+ else if (finite(next)) value = clamp(next);
165
+ else return;
166
+ render(previous, true);
167
+ },
168
+ get value() {
169
+ return value;
170
+ },
171
+ destroy: () => scope.dispose(),
172
+ };
173
+ }
@@ -0,0 +1,139 @@
1
+ /* progress — Melonmind line: a neutral track, the accent only for the value; a ring (MetricsRing) with the figure inside.
2
+ The fill is drawn from --mk-progress, which the engine glides with GSAP. */
3
+ .mk-progress,
4
+ mk-progress {
5
+ --mk-progress-height: 10px;
6
+ --mk-progress-size: 4.5rem;
7
+ --mk-progress-track: var(--mk-surface-4);
8
+ --mk-progress-fill: var(--mk-accent);
9
+ display: block;
10
+ color: var(--mk-text);
11
+ font-family: var(--mk-font);
12
+ }
13
+
14
+ :is(.mk-progress, mk-progress) [data-mk-progress-visual] {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 0.875rem;
18
+ }
19
+
20
+ /* Linear */
21
+ :is(.mk-progress, mk-progress) span[data-mk-part='track'] {
22
+ position: relative;
23
+ flex: 1;
24
+ height: var(--mk-progress-height);
25
+ border-radius: var(--mk-radius-full);
26
+ background: var(--mk-progress-track);
27
+ }
28
+
29
+ :is(.mk-progress, mk-progress) [data-mk-part='bar'] {
30
+ position: absolute;
31
+ inset: 0;
32
+ border-radius: inherit;
33
+ background: var(--mk-progress-fill);
34
+ clip-path: inset(0 calc((1 - var(--mk-progress, 0)) * 100%) 0 0 round var(--mk-radius-full));
35
+ }
36
+
37
+ /* The light at the head of the bar (flashes when the value goes up). */
38
+ :is(.mk-progress, mk-progress) [data-mk-part='head'] {
39
+ position: absolute;
40
+ top: 50%;
41
+ left: calc(var(--mk-progress, 0) * 100%);
42
+ width: calc(var(--mk-progress-height) * 2.4);
43
+ aspect-ratio: 1;
44
+ border-radius: 50%;
45
+ background: radial-gradient(circle, color-mix(in srgb, var(--mk-progress-fill) 70%, white) 0%, transparent 70%);
46
+ opacity: 0;
47
+ pointer-events: none;
48
+ translate: -50% -50%;
49
+ }
50
+
51
+ :is(.mk-progress, mk-progress)[data-mk-indeterminate] [data-mk-part='bar'] {
52
+ clip-path: none;
53
+ width: 38%;
54
+ animation: mk-progress-slide 1.4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
55
+ }
56
+
57
+ :is(.mk-progress, mk-progress) [data-mk-part='value'] {
58
+ min-width: 4.5ch;
59
+ color: var(--mk-muted);
60
+ font-size: 0.875rem;
61
+ font-weight: 500;
62
+ font-variant-numeric: tabular-nums;
63
+ text-align: right;
64
+ }
65
+
66
+ :is(.mk-progress, mk-progress)[data-mk-complete] [data-mk-part='value'] {
67
+ color: var(--mk-text);
68
+ }
69
+
70
+ /* Ring */
71
+ :is(.mk-progress, mk-progress)[data-mk-variant='ring'] {
72
+ display: inline-block;
73
+ width: var(--mk-progress-size);
74
+ }
75
+
76
+ :is(.mk-progress, mk-progress)[data-mk-variant='ring'] [data-mk-progress-visual] {
77
+ position: relative;
78
+ display: grid;
79
+ place-items: center;
80
+ aspect-ratio: 1;
81
+ }
82
+
83
+ :is(.mk-progress, mk-progress) [data-mk-part='ring'] {
84
+ grid-area: 1 / 1;
85
+ width: 100%;
86
+ height: 100%;
87
+ overflow: visible;
88
+ rotate: -90deg;
89
+ }
90
+
91
+ :is(.mk-progress, mk-progress) circle[data-mk-part='track'] {
92
+ stroke: var(--mk-progress-track);
93
+ }
94
+
95
+ :is(.mk-progress, mk-progress) [data-mk-part='arc'] {
96
+ stroke: var(--mk-progress-fill);
97
+ }
98
+
99
+ :is(.mk-progress, mk-progress)[data-mk-variant='ring'][data-mk-indeterminate] [data-mk-part='ring'] {
100
+ animation: mk-progress-spin 1.1s linear infinite;
101
+ }
102
+
103
+ :is(.mk-progress, mk-progress)[data-mk-variant='ring'] [data-mk-part='value'] {
104
+ grid-area: 1 / 1;
105
+ min-width: 0;
106
+ color: var(--mk-text);
107
+ font-size: calc(var(--mk-progress-size) * 0.24);
108
+ letter-spacing: -0.02em;
109
+ text-align: center;
110
+ }
111
+
112
+ @keyframes mk-progress-slide {
113
+ from {
114
+ translate: -100% 0;
115
+ }
116
+ to {
117
+ translate: 265% 0;
118
+ }
119
+ }
120
+
121
+ @keyframes mk-progress-spin {
122
+ from {
123
+ rotate: -90deg;
124
+ }
125
+ to {
126
+ rotate: 270deg;
127
+ }
128
+ }
129
+
130
+ /* Reduced motion: values jump; indeterminate becomes a still, partial fill. */
131
+ @media (prefers-reduced-motion: reduce) {
132
+ :is(.mk-progress, mk-progress) :is([data-mk-part='bar'], [data-mk-part='ring']) {
133
+ animation: none !important;
134
+ }
135
+
136
+ :is(.mk-progress, mk-progress)[data-mk-indeterminate] [data-mk-part='bar'] {
137
+ opacity: 0.55;
138
+ }
139
+ }
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { useRef, type HTMLAttributes } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createProgress, type ProgressInstance, type ProgressOptions } from './engine';
5
+
6
+ export interface ProgressProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, ProgressOptions {}
7
+
8
+ /** <Progress value={42} label="Subida" showValue />; value={null} is indeterminate. The engine owns the drawing and ARIA. */
9
+ export function Progress({ value, min, max, label, variant, showValue, formatValue, className, ...props }: ProgressProps) {
10
+ const ref = useRef<HTMLDivElement>(null);
11
+ const instance = useRef<ProgressInstance | null>(null);
12
+ const current = useLatest(value);
13
+ const format = useLatest(formatValue);
14
+ const formatted = formatValue !== undefined;
15
+ useIsomorphicLayoutEffect(() => {
16
+ const created = createProgress(ref.current, {
17
+ value: current.current,
18
+ min,
19
+ max,
20
+ label,
21
+ variant,
22
+ showValue,
23
+ formatValue: formatted ? (n, ratio) => format.current?.(n, ratio) ?? '' : undefined,
24
+ });
25
+ instance.current = created;
26
+ return () => {
27
+ created.destroy();
28
+ instance.current = null;
29
+ };
30
+ }, [min, max, label, variant, showValue, formatted]);
31
+ useIsomorphicLayoutEffect(() => {
32
+ instance.current?.setValue(value === undefined ? (min ?? 0) : value);
33
+ }, [value]);
34
+ return <div {...props} ref={ref} className={['mk-progress', className].filter(Boolean).join(' ')} />;
35
+ }
@@ -0,0 +1,20 @@
1
+ # quantity
2
+
3
+ Selector inspirado en [Bernice](https://bernicebakery.com/collections/cakes). Botones nativos, mordida geométrica propia y giro de 15° en .4 s. La forma adapta el original; no usa su SVG.
4
+
5
+ ```html
6
+ <mk-quantity class="mk-quantity" min="1" max="9">
7
+ <button type="button" data-mk-quantity-decrease aria-label="Disminuir">−</button>
8
+ <input data-mk-quantity-input type="number" value="1" aria-label="Cantidad" />
9
+ <button type="button" data-mk-quantity-increase aria-label="Aumentar">+</button>
10
+ </mk-quantity>
11
+ ```
12
+
13
+ Registrar con `defineElements()` de `/elements`, o usar `createQuantity(root, options)`. React: `<Quantity min={1} max={9} value={value} onChange={setValue} />` desde `/react`.
14
+
15
+ Opciones: `min=1`, `max=999`, `step=1`, `value`, `disabled`, `onChange(value)`. API: `setValue(number, notify=true)`, `value`, `destroy()`. El input acepta edición: un número válido y dentro del rango se aplica mientras escribes (sin tocar el texto); uno parcial o fuera de rango se ajusta al paso desde min y se acota al confirmar o salir del campo. Evento `mk:change` con `{value, previous}`. No realiza operaciones de carrito.
16
+
17
+ Variables: `--mk-quantity-size` (3rem), colores semánticos. Movimiento reducido conserva controles sin rotación. React acepta label/decreaseLabel/increaseLabel/name y props HTML. Desmontar llama destroy y restaura marcado/valores originales.
18
+
19
+ El reset nativo del formulario sincroniza el control sin emitir callbacks de edición. Los atributos de custom elements conservan el estado editado si value no está presente; value explícito tiene precedencia.
20
+ Si max no coincide con un paso desde min, el límite efectivo es el mayor min+n×step que no supere max, para mantener validez HTML nativa.
@@ -0,0 +1,14 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createQuantity, type QuantityInstance } from './engine';
3
+ /** Light DOM: supply the same child markup shown in the README. */
4
+ export class MkQuantityElement extends MkElement<QuantityInstance> {
5
+ static options: Record<string, OptionType> = { min: 'number', max: 'number', step: 'number', value: 'number', disabled: 'boolean' };
6
+ protected create(options: Record<string, unknown>) { return createQuantity(this, options); }
7
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
8
+ if (previous === next || !this.isConnected || !this.instance) return;
9
+ const value = this.instance.value;
10
+ this.instance.destroy();
11
+ this.instance = this.create({ ...(!this.hasAttribute('value') ? { value } : {}), ...this.options });
12
+ }
13
+ }
14
+ export const defineQuantity = () => defineElement('mk-quantity', MkQuantityElement);
@@ -0,0 +1,85 @@
1
+ import { createScope } from '../../core/scope';
2
+ import { inert, type Instance } from '../../core/types';
3
+
4
+ export interface QuantityOptions {
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ value?: number;
9
+ disabled?: boolean;
10
+ onChange?: (value: number) => void;
11
+ }
12
+ export interface QuantityInstance extends Instance {
13
+ setValue(value: number, notify?: boolean): void;
14
+ readonly value: number;
15
+ }
16
+
17
+ /** Bounded numeric control. Bite/rotation is CSS; buttons remain native. */
18
+ export function createQuantity(root: HTMLElement | null, options: QuantityOptions = {}): QuantityInstance {
19
+ const input = root?.querySelector<HTMLInputElement>('[data-mk-quantity-input]');
20
+ if (!root || !input) return inert({ setValue() {}, value: 0 });
21
+ const scope = createScope();
22
+ const min = Number.isFinite(options.min) ? options.min! : 1;
23
+ const max = Number.isFinite(options.max) ? Math.max(min, options.max!) : Math.max(min, 999);
24
+ const step = Number.isFinite(options.step) && options.step! > 0 ? options.step! : 1;
25
+ const steps = Math.floor((max - min) / step + 1e-9);
26
+ const upper = Math.min(max, Number((min + steps * step).toFixed(10)));
27
+ const normalize = (n: number) => Math.max(min, Math.min(upper, Number((min + Math.round((n - min) / step) * step).toFixed(10))));
28
+ const original = input.value;
29
+ const disabled = options.disabled ?? input.disabled;
30
+ const decrease = root.querySelector<HTMLButtonElement>('[data-mk-quantity-decrease]');
31
+ const increase = root.querySelector<HTMLButtonElement>('[data-mk-quantity-increase]');
32
+ scope.attr(input, 'min', String(min));
33
+ scope.attr(input, 'max', String(max));
34
+ scope.attr(input, 'step', String(step));
35
+ scope.attr(input, 'disabled', disabled ? '' : null);
36
+ scope.attr(root, 'data-mk-ready', '');
37
+ const initial = input.value.trim() ? Number(input.value) : NaN;
38
+ let value = normalize(Number.isFinite(options.value) ? options.value! : Number.isFinite(initial) ? initial : min);
39
+ let pendingReset: Event | null = null;
40
+ const typed = () => (input.value.trim() && Number.isFinite(input.valueAsNumber) ? input.valueAsNumber : NaN);
41
+ const render = (syncInput = true) => {
42
+ if (pendingReset) {
43
+ if (pendingReset.eventPhase !== 0) return;
44
+ if (!pendingReset.defaultPrevented) value = normalize(Number.isFinite(typed()) ? typed() : min);
45
+ pendingReset = null;
46
+ }
47
+ if (syncInput) input.value = String(value);
48
+ if (decrease) scope.attr(decrease, 'disabled', disabled || value <= min ? '' : null);
49
+ if (increase) scope.attr(increase, 'disabled', disabled || value >= upper ? '' : null);
50
+ };
51
+ const commit = (next: number, notify: boolean, syncInput: boolean) => {
52
+ if (scope.disposed || !Number.isFinite(next)) return;
53
+ if (pendingReset?.eventPhase === 0) pendingReset = null;
54
+ const previous = value;
55
+ value = normalize(next);
56
+ render(syncInput);
57
+ if (notify && value !== previous) {
58
+ options.onChange?.(value);
59
+ root.dispatchEvent(new CustomEvent('mk:change', { bubbles: true, detail: { value, previous } }));
60
+ }
61
+ };
62
+ const setValue = (next: number, notify = true) => commit(next, notify, true);
63
+ if (decrease) scope.on(decrease, 'click', () => { if (!decrease.disabled) setValue(value - step); });
64
+ if (increase) scope.on(increase, 'click', () => { if (!increase.disabled) setValue(value + step); });
65
+ // While typing, commit only numbers that are already valid and leave the text alone;
66
+ // partial or out-of-range input settles on change/blur.
67
+ scope.on(input, 'input', () => {
68
+ const n = typed();
69
+ if (Number.isFinite(n) && normalize(n) === n) commit(n, true, false);
70
+ });
71
+ scope.on(input, 'change', () => {
72
+ if (Number.isFinite(typed())) setValue(typed());
73
+ else render();
74
+ });
75
+ scope.on(input, 'blur', () => render());
76
+ if (input.form) scope.on(input.form, 'reset', (event) => {
77
+ // reset's native default action runs after its listeners. Match the final input silently.
78
+ render();
79
+ pendingReset = event;
80
+ scope.timeout(() => { if (pendingReset === event) render(); }, 0);
81
+ });
82
+ scope.add(() => { input.value = original; });
83
+ render();
84
+ return { setValue, get value() { return value; }, destroy: () => scope.dispose() };
85
+ }
@@ -0,0 +1,15 @@
1
+ .mk-quantity, mk-quantity { display: inline-flex; align-items: center; gap: .5rem; --mk-quantity-size: 3rem; }
2
+ .mk-quantity [data-mk-quantity-decrease], .mk-quantity [data-mk-quantity-increase], mk-quantity button {
3
+ width: var(--mk-quantity-size); height: var(--mk-quantity-size); border: 0; border-radius: 50%;
4
+ background: var(--mk-secondary-soft); color: var(--mk-on-secondary-soft); font: inherit; font-size: 1.65rem;
5
+ cursor: pointer; transition: transform .4s cubic-bezier(.28,.71,0,.98), opacity .2s;
6
+ }
7
+ .mk-quantity [data-mk-quantity-increase], mk-quantity [data-mk-quantity-increase] { background: var(--mk-accent-soft); color: var(--mk-on-accent-soft); }
8
+ .mk-quantity button:is(:hover,:focus-visible):not(:disabled), mk-quantity button:is(:hover,:focus-visible):not(:disabled) {
9
+ transform: rotate(15deg); mask-image: radial-gradient(circle at 100% 0, transparent 22%, #000 23%);
10
+ }
11
+ .mk-quantity button:focus-visible, mk-quantity button:focus-visible { outline: 2px solid var(--mk-accent); outline-offset: 4px; }
12
+ .mk-quantity button:disabled, mk-quantity button:disabled { opacity: .4; cursor: default; }
13
+ .mk-quantity input, mk-quantity input { width: 3.5rem; padding: .4rem 0; border: 0; border-radius: var(--mk-radius-sm); text-align: center; font: inherit; font-variant-numeric: tabular-nums; background: transparent; color: var(--mk-text); appearance: textfield; }
14
+ .mk-quantity input::-webkit-inner-spin-button, mk-quantity input::-webkit-inner-spin-button { appearance: none; }
15
+ @media (prefers-reduced-motion: reduce) { .mk-quantity button, mk-quantity button { transition: none; transform: none !important; } }
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { useRef, type HTMLAttributes } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createQuantity, type QuantityOptions } from './engine';
5
+ export interface QuantityProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'>, QuantityOptions {
6
+ label?: string; decreaseLabel?: string; increaseLabel?: string; name?: string;
7
+ }
8
+ export function Quantity({ min, max, step, value, disabled, onChange, label = 'Cantidad', decreaseLabel = 'Disminuir', increaseLabel = 'Aumentar', name, className, ...props }: QuantityProps) {
9
+ const ref = useRef<HTMLDivElement>(null);
10
+ const edited = useRef<number | undefined>(undefined);
11
+ const change = useLatest(onChange);
12
+ useIsomorphicLayoutEffect(() => {
13
+ const instance = createQuantity(ref.current, { min, max, step, value: value ?? edited.current, disabled, onChange: (n) => change.current?.(n) });
14
+ return () => { edited.current = instance.value; instance.destroy(); };
15
+ }, [min, max, step, value, disabled]);
16
+ return <div {...props} ref={ref} className={['mk-quantity', className].filter(Boolean).join(' ')} role="group" aria-label={label}>
17
+ <button type="button" data-mk-quantity-decrease aria-label={decreaseLabel}>−</button>
18
+ <input data-mk-quantity-input type="number" aria-label={label} name={name} defaultValue={value ?? min ?? 1} disabled={disabled} />
19
+ <button type="button" data-mk-quantity-increase aria-label={increaseLabel}>+</button>
20
+ </div>;
21
+ }