@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,202 @@
1
+ import { gsap, ELASTIC, 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 { blurIn, blurOut, rollDigits } from '../../motion/micro';
6
+
7
+ /*
8
+ * Notification bell: a bell with a badge on a button. notify() rings it — the
9
+ * bell swings from its top in a damped arc (±18°) with the clapper a beat
10
+ * behind, waves ripple out, the badge pops in elastically (or its number rolls
11
+ * up), and an optional message rises beside it for a few seconds. The button's
12
+ * name carries the count and a polite status announces new ones.
13
+ */
14
+
15
+ export interface NotificationBellOptions {
16
+ count?: number;
17
+ /** Accessible name before the count (default 'Notificaciones'). */
18
+ label?: string;
19
+ /** How long a message stays, in ms (default 3200). */
20
+ messageDuration?: number;
21
+ /** Above this the badge reads n+ (default 99). */
22
+ max?: number;
23
+ onOpen?: () => void;
24
+ }
25
+
26
+ export interface NotificationBellInstance extends Instance {
27
+ /** Ring with a new notification (and show `message`, as text). */
28
+ notify(message?: string): void;
29
+ setCount(count: number): void;
30
+ clear(): void;
31
+ readonly count: number;
32
+ }
33
+
34
+ const SVG = 'http://www.w3.org/2000/svg';
35
+ const BODY = 'M6 16.5V11a6 6 0 0 1 12 0v5.5l1.5 2H4.5z';
36
+
37
+ export function createNotificationBell(root: HTMLElement | null, options: NotificationBellOptions = {}): NotificationBellInstance {
38
+ const button = root ? ((root.matches('button') ? root : root.querySelector('button')) as HTMLButtonElement | null) : null;
39
+ if (!root || !button) return inert({ notify() {}, setCount() {}, clear() {}, count: 0 });
40
+ registerMotion();
41
+ const scope = createScope();
42
+ const max = Number.isFinite(options.max) && options.max! > 0 ? Math.floor(options.max!) : 99;
43
+ const label = options.label ?? 'Notificaciones';
44
+ const stay = Number.isFinite(options.messageDuration) ? Math.max(0, options.messageDuration!) : 3200;
45
+ let count = Math.max(0, Math.round(Number.isFinite(options.count) ? options.count! : 0));
46
+
47
+ const made: Element[] = [];
48
+ scope.add(() => made.forEach((element) => element.remove()));
49
+ const icon = document.createElementNS(SVG, 'svg');
50
+ icon.setAttribute('viewBox', '0 0 24 24');
51
+ icon.setAttribute('aria-hidden', 'true');
52
+ icon.dataset.mkBellIcon = '';
53
+ const bell = document.createElementNS(SVG, 'g');
54
+ bell.dataset.mkPart = 'bell';
55
+ const body = document.createElementNS(SVG, 'path');
56
+ body.setAttribute('d', BODY);
57
+ const top = document.createElementNS(SVG, 'path');
58
+ top.setAttribute('d', 'M12 3v2');
59
+ bell.append(body, top);
60
+ const clapper = document.createElementNS(SVG, 'path');
61
+ clapper.setAttribute('d', 'M10 20.5a2 2 0 0 0 4 0');
62
+ clapper.dataset.mkPart = 'clapper';
63
+ icon.append(bell, clapper);
64
+ const waves = document.createElement('span');
65
+ waves.dataset.mkBellWaves = '';
66
+ waves.setAttribute('aria-hidden', 'true');
67
+ const badge = document.createElement('span');
68
+ badge.dataset.mkBellBadge = '';
69
+ badge.setAttribute('aria-hidden', 'true');
70
+ button.prepend(waves, icon);
71
+ button.append(badge);
72
+ const bubble = document.createElement('span');
73
+ bubble.dataset.mkBellMessage = '';
74
+ bubble.setAttribute('aria-hidden', 'true');
75
+ bubble.hidden = true;
76
+ const live = document.createElement('span');
77
+ live.className = 'mk-sr-only';
78
+ live.setAttribute('role', 'status');
79
+ (root === button ? button : root).append(bubble, live);
80
+ made.push(waves, icon, badge, bubble, live);
81
+
82
+ scope.attr(button, 'type', button.getAttribute('type') ?? 'button');
83
+ scope.attr(button, 'data-mk-bell', '');
84
+ scope.attr(root, 'data-mk-ready', '');
85
+
86
+ let hideTimer: ReturnType<typeof setTimeout> | undefined;
87
+ const effects = new Set<gsap.core.Animation>();
88
+ const keep = (animation: gsap.core.Animation | null) => {
89
+ if (!animation) return;
90
+ effects.add(animation);
91
+ // Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
92
+ for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
93
+ };
94
+ scope.add(() => {
95
+ clearTimeout(hideTimer);
96
+ for (const effect of effects) effect.kill();
97
+ effects.clear();
98
+ gsap.killTweensOf([bell, clapper, badge, bubble]);
99
+ });
100
+
101
+ const text = (n: number) => (n > max ? `${max}+` : String(n));
102
+ const name = () => (count ? `${label}, ${count} sin leer` : label);
103
+ const paint = () => {
104
+ scope.attr(button, 'aria-label', name());
105
+ badge.hidden = count === 0;
106
+ };
107
+
108
+ const showBadge = (previous: number) => {
109
+ if (count === 0) {
110
+ if (reduced() || previous === 0) {
111
+ badge.hidden = true;
112
+ return;
113
+ }
114
+ keep(gsap.to(badge, { scale: 0, duration: 0.2, ease: 'power2.in', onComplete: () => void (badge.hidden = true), clearProps: 'scale,transform' }));
115
+ return;
116
+ }
117
+ if (previous === 0) {
118
+ badge.textContent = text(count);
119
+ badge.hidden = false;
120
+ if (!reduced()) keep(gsap.fromTo(badge, { scale: 0 }, { scale: 1, duration: 0.8, ease: ELASTIC, clearProps: 'scale,transform' }));
121
+ } else keep(rollDigits(badge, text(count), count >= previous ? 1 : -1, 0.4));
122
+ };
123
+
124
+ const ring = () => {
125
+ if (reduced()) return;
126
+ gsap.killTweensOf([bell, clapper]);
127
+ keep(gsap.fromTo(bell, { rotation: 0 }, { keyframes: { rotation: [18, -14, 10, -6, 3, 0] }, duration: 1, ease: 'power1.out', svgOrigin: '12 4', clearProps: 'rotation,transform' }));
128
+ keep(gsap.fromTo(clapper, { x: 0 }, { keyframes: { x: [0, -2.5, 2, -1.4, 0.8, 0] }, duration: 1, delay: 0.06, ease: 'power1.out', clearProps: 'x,transform' }));
129
+ for (let i = 0; i < 2; i++) {
130
+ const wave = document.createElement('i');
131
+ waves.append(wave);
132
+ keep(gsap.fromTo(wave, { scale: 0.6, opacity: 0.55 }, { scale: 1.9, opacity: 0, duration: 0.8, delay: i * 0.18, ease: 'power2.out', onComplete: () => wave.remove() }));
133
+ }
134
+ };
135
+
136
+ // The message rises on the side with room inside the bell's container (and the window), so a bell at the
137
+ // right edge of a card speaks to its left instead of running over the next thing.
138
+ const place = () => {
139
+ bubble.style.maxWidth = '';
140
+ const box = root.getBoundingClientRect();
141
+ const frame = root.parentElement?.getBoundingClientRect();
142
+ const right = Math.min(frame?.right ?? innerWidth, innerWidth) - box.right;
143
+ const left = box.left - Math.max(frame?.left ?? 0, 0);
144
+ const side = right >= bubble.offsetWidth + 12 || right >= left ? 'right' : 'left';
145
+ bubble.dataset.mkSide = side;
146
+ bubble.style.maxWidth = `min(16rem, ${Math.max(140, (side === 'right' ? right : left) - 12)}px)`;
147
+ };
148
+
149
+ const say = (message: string) => {
150
+ clearTimeout(hideTimer);
151
+ gsap.killTweensOf(bubble);
152
+ bubble.textContent = message;
153
+ bubble.hidden = false;
154
+ place();
155
+ keep(blurIn(bubble, { from: 'pop' }));
156
+ hideTimer = setTimeout(() => {
157
+ if (scope.disposed) return;
158
+ const out = blurOut(bubble, {
159
+ to: 'up',
160
+ onComplete: () => {
161
+ bubble.hidden = true;
162
+ gsap.set(bubble, { clearProps: 'all' });
163
+ },
164
+ });
165
+ keep(out);
166
+ }, stay);
167
+ };
168
+
169
+ const setCount = (next: number, announce = false) => {
170
+ if (scope.disposed || !Number.isFinite(next)) return;
171
+ const previous = count;
172
+ count = Math.max(0, Math.round(next));
173
+ if (count === previous) return;
174
+ paint();
175
+ showBadge(previous);
176
+ if (announce) live.textContent = name();
177
+ root.dispatchEvent(new CustomEvent('mk:count', { bubbles: true, detail: { count } }));
178
+ };
179
+
180
+ scope.on(button, 'click', () => {
181
+ options.onOpen?.();
182
+ root.dispatchEvent(new CustomEvent('mk:open', { bubbles: true, detail: { count } }));
183
+ });
184
+ badge.textContent = text(count);
185
+ paint();
186
+
187
+ return {
188
+ notify(message) {
189
+ if (scope.disposed) return;
190
+ setCount(count + 1, true);
191
+ ring();
192
+ if (message) say(String(message));
193
+ if (message) live.textContent = `${name()}. ${message}`;
194
+ },
195
+ setCount: (next) => setCount(next),
196
+ clear: () => setCount(0),
197
+ get count() {
198
+ return count;
199
+ },
200
+ destroy: () => scope.dispose(),
201
+ };
202
+ }
@@ -0,0 +1,126 @@
1
+ /* notification-bell — a round Melonmind icon button (surface tone) with a bell, a badge in the error tone and a
2
+ message that rises beside it in the inverse glass. */
3
+ .mk-notification-bell,
4
+ mk-notification-bell {
5
+ position: relative;
6
+ display: inline-flex;
7
+ }
8
+
9
+ [data-mk-bell] {
10
+ position: relative;
11
+ display: grid;
12
+ place-items: center;
13
+ width: 3rem;
14
+ height: 3rem;
15
+ padding: 0;
16
+ border: 0;
17
+ border-radius: 50%;
18
+ background: var(--mk-surface-3);
19
+ color: var(--mk-text);
20
+ cursor: pointer;
21
+ transition: background-color 0.2s ease;
22
+ }
23
+
24
+ [data-mk-bell]:hover {
25
+ background: var(--mk-surface-4);
26
+ }
27
+
28
+ [data-mk-bell]:active {
29
+ animation: mk-bell-press 500ms cubic-bezier(0.37, 1.42, 0.37, 1);
30
+ }
31
+
32
+ [data-mk-bell]:focus-visible {
33
+ outline: 2px solid var(--mk-accent);
34
+ outline-offset: 3px;
35
+ }
36
+
37
+ [data-mk-bell] [data-mk-bell-icon] {
38
+ width: 1.5rem;
39
+ height: 1.5rem;
40
+ overflow: visible;
41
+ fill: none;
42
+ stroke: currentColor;
43
+ stroke-width: 1.8;
44
+ stroke-linecap: round;
45
+ stroke-linejoin: round;
46
+ }
47
+
48
+ [data-mk-bell] [data-mk-bell-waves] {
49
+ position: absolute;
50
+ inset: 0;
51
+ display: grid;
52
+ place-items: center;
53
+ pointer-events: none;
54
+ }
55
+
56
+ [data-mk-bell] [data-mk-bell-waves] i {
57
+ position: absolute;
58
+ inset: 0;
59
+ border-radius: 50%;
60
+ box-shadow: inset 0 0 0 2px var(--mk-text);
61
+ }
62
+
63
+ [data-mk-bell] [data-mk-bell-badge] {
64
+ position: absolute;
65
+ top: 0.15rem;
66
+ right: 0.1rem;
67
+ display: inline-flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ min-width: 1.25rem;
71
+ height: 1.25rem;
72
+ padding: 0 0.3rem;
73
+ border-radius: 64px;
74
+ background: var(--mk-error);
75
+ box-shadow: 0 0 0 2px var(--mk-bg);
76
+ color: var(--mk-on-error);
77
+ font-size: 0.6875rem;
78
+ font-weight: 600;
79
+ font-variant-numeric: tabular-nums;
80
+ line-height: 1;
81
+ }
82
+
83
+ [data-mk-bell] [data-mk-bell-badge][hidden] {
84
+ display: none;
85
+ }
86
+
87
+ :is(.mk-notification-bell, mk-notification-bell) [data-mk-bell-message] {
88
+ position: absolute;
89
+ top: 50%;
90
+ left: calc(100% + 0.75rem);
91
+ width: max-content;
92
+ max-width: 16rem;
93
+ padding: 0.5rem 0.85rem;
94
+ border-radius: 16px;
95
+ background: var(--mk-glass-inverse);
96
+ box-shadow: var(--mk-glass-bevel);
97
+ backdrop-filter: blur(16px);
98
+ color: var(--mk-on-glass-inverse);
99
+ font-size: 0.8125rem;
100
+ translate: 0 -50%;
101
+ pointer-events: none;
102
+ transform-origin: 0 50%;
103
+ }
104
+
105
+ :is(.mk-notification-bell, mk-notification-bell) [data-mk-bell-message][data-mk-side='left'] {
106
+ right: calc(100% + 0.75rem);
107
+ left: auto;
108
+ transform-origin: 100% 50%;
109
+ }
110
+
111
+ :is(.mk-notification-bell, mk-notification-bell) [data-mk-bell-message][hidden] {
112
+ display: none;
113
+ }
114
+
115
+ @keyframes mk-bell-press {
116
+ 50% {
117
+ transform: scale(0.92);
118
+ }
119
+ }
120
+
121
+ @media (prefers-reduced-motion: reduce) {
122
+ [data-mk-bell] {
123
+ animation: none !important;
124
+ transition: none;
125
+ }
126
+ }
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+ import { useRef, type ButtonHTMLAttributes, type MutableRefObject } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createNotificationBell, type NotificationBellInstance } from './engine';
5
+
6
+ export interface NotificationBellProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
7
+ /** Unread count. A higher count than before rings the bell. */
8
+ count?: number;
9
+ /** The newest message: a new value rings the bell and shows it. */
10
+ message?: string;
11
+ label?: string;
12
+ messageDuration?: number;
13
+ max?: number;
14
+ onOpen?: () => void;
15
+ instanceRef?: MutableRefObject<NotificationBellInstance | null>;
16
+ }
17
+
18
+ /** <NotificationBell count={unread} message={latest} onOpen={openPanel} />. */
19
+ export function NotificationBell({ count = 0, message, label, messageDuration, max, onOpen, instanceRef, className, ...props }: NotificationBellProps) {
20
+ const ref = useRef<HTMLSpanElement>(null);
21
+ const instance = useRef<NotificationBellInstance | null>(null);
22
+ const open = useLatest(onOpen);
23
+ const initial = useRef(count);
24
+ useIsomorphicLayoutEffect(() => {
25
+ const created = createNotificationBell(ref.current, { count: initial.current, label, messageDuration, max, onOpen: () => open.current?.() });
26
+ instance.current = created;
27
+ if (instanceRef) instanceRef.current = created;
28
+ return () => {
29
+ initial.current = created.count;
30
+ created.destroy();
31
+ instance.current = null;
32
+ if (instanceRef) instanceRef.current = null;
33
+ };
34
+ // eslint-disable-next-line react-hooks/exhaustive-deps
35
+ }, [label, messageDuration, max]);
36
+ const seen = useRef({ count, message });
37
+ useIsomorphicLayoutEffect(() => {
38
+ const bell = instance.current;
39
+ if (!bell) return;
40
+ const before = seen.current;
41
+ seen.current = { count, message };
42
+ const fresh = message !== undefined && message !== before.message;
43
+ if (count > bell.count) {
44
+ // One ring for the newest; the rest of the jump is set at once.
45
+ bell.setCount(count - 1);
46
+ bell.notify(fresh ? message : undefined);
47
+ } else {
48
+ bell.setCount(count);
49
+ if (fresh && message) bell.notify(message);
50
+ }
51
+ }, [count, message]);
52
+ return (
53
+ <span ref={ref} className={['mk-notification-bell', className].filter(Boolean).join(' ')}>
54
+ <button type="button" {...props} />
55
+ </span>
56
+ );
57
+ }
@@ -0,0 +1,17 @@
1
+ # pagination
2
+
3
+ Paginación con ventana acotada de números, extremos, elipsis y respuesta de elevación/presión.
4
+
5
+ ```html
6
+ <mk-pagination class="mk-pagination" total-pages="30" page="4" role="navigation" aria-label="Artículos">
7
+ <button type="button" data-mk-pagination-previous aria-label="Página anterior">←</button>
8
+ <span data-mk-pagination-pages></span>
9
+ <button type="button" data-mk-pagination-next aria-label="Página siguiente">→</button>
10
+ </mk-pagination>
11
+ ```
12
+
13
+ `defineElements()`, `createPagination(root, options)`, o `<Pagination totalPages={30} page={page} onChange={setPage} />`.
14
+
15
+ Opciones: `totalPages=10`, `page=1`, `siblings=1` (0…10), `pageLabel(n)`, `onChange(n)`. Total mínimo 1. API: `setPage(n, notify=true)`, `setTotalPages(n)`, `page`, `totalPages`, `destroy()`. `mk:change` lleva `{page,totalPages}`. Los cambios del total acotan la página y conservan el foco.
16
+
17
+ Flechas izquierda/derecha, Home y End cambian la selección; botones nativos y aria-current=page. La aplicación decide cómo cargar los resultados. Sin animación con movimiento reducido.
@@ -0,0 +1,14 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createPagination, type PaginationInstance } from './engine';
3
+ /** Light DOM: supply the same child markup shown in the README. */
4
+ export class MkPaginationElement extends MkElement<PaginationInstance> {
5
+ static options: Record<string, OptionType> = { totalPages: 'number', page: 'number', siblings: 'number' };
6
+ protected create(options: Record<string, unknown>) { return createPagination(this, options); }
7
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
8
+ if (previous === next || !this.isConnected || !this.instance) return;
9
+ const page = this.instance.page;
10
+ this.instance.destroy();
11
+ this.instance = this.create({ ...(!this.hasAttribute('page') ? { page } : {}), ...this.options });
12
+ }
13
+ }
14
+ export const definePagination = () => defineElement('mk-pagination', MkPaginationElement);
@@ -0,0 +1,102 @@
1
+ import { createScope } from '../../core/scope';
2
+ import { inert, type Instance } from '../../core/types';
3
+
4
+ export interface PaginationOptions {
5
+ totalPages?: number;
6
+ page?: number;
7
+ siblings?: number;
8
+ pageLabel?: (page: number) => string;
9
+ onChange?: (page: number) => void;
10
+ }
11
+ export interface PaginationInstance extends Instance {
12
+ setPage(page: number, notify?: boolean): void;
13
+ setTotalPages(total: number): void;
14
+ readonly page: number;
15
+ readonly totalPages: number;
16
+ }
17
+ // Reserve room for the page-window arithmetic so incrementing always progresses.
18
+ const positive = (n: number | undefined, fallback: number) => Number.isFinite(n) ? Math.max(1, Math.min(Number.MAX_SAFE_INTEGER - 32, Math.floor(n!))) : fallback;
19
+
20
+ /** Renders a bounded window of pages, even when totalPages is very large. */
21
+ export function createPagination(root: HTMLElement | null, options: PaginationOptions = {}): PaginationInstance {
22
+ const pages = root?.querySelector<HTMLElement>('[data-mk-pagination-pages]');
23
+ if (!root || !pages) return inert({ setPage() {}, setTotalPages() {}, page: 1, totalPages: 1 });
24
+ const scope = createScope();
25
+ const original = [...pages.childNodes];
26
+ const previous = root.querySelector<HTMLButtonElement>('[data-mk-pagination-previous]');
27
+ const next = root.querySelector<HTMLButtonElement>('[data-mk-pagination-next]');
28
+ let total = positive(options.totalPages, 10);
29
+ let page = Math.min(total, positive(options.page, 1));
30
+ const siblings = Math.min(10, Math.max(0, Number.isFinite(options.siblings) ? Math.floor(options.siblings!) : 1));
31
+ scope.attr(root, 'data-mk-ready', '');
32
+ const render = () => {
33
+ const hadFocus = pages.contains(document.activeElement);
34
+ const selected = new Set([1, total]);
35
+ for (let n = Math.max(1, page - siblings); n <= Math.min(total, page + siblings); n++) selected.add(n);
36
+ const fragment = document.createDocumentFragment();
37
+ let last = 0;
38
+ for (const n of [...selected].sort((a, b) => a - b)) {
39
+ if (last && n - last > 1) {
40
+ const gap = document.createElement('span');
41
+ gap.className = 'mk-pagination__gap';
42
+ gap.textContent = '…';
43
+ gap.setAttribute('aria-hidden', 'true');
44
+ fragment.append(gap);
45
+ }
46
+ const button = document.createElement('button');
47
+ button.type = 'button';
48
+ button.dataset.mkPage = String(n);
49
+ button.textContent = String(n);
50
+ button.setAttribute('aria-label', options.pageLabel?.(n) ?? `Página ${n}`);
51
+ if (n === page) button.setAttribute('aria-current', 'page');
52
+ fragment.append(button);
53
+ last = n;
54
+ }
55
+ pages.replaceChildren(fragment);
56
+ if (hadFocus) pages.querySelector<HTMLButtonElement>('[aria-current]')?.focus();
57
+ if (previous) scope.attr(previous, 'disabled', page === 1 ? '' : null);
58
+ if (next) scope.attr(next, 'disabled', page === total ? '' : null);
59
+ };
60
+ const setPage = (value: number, notify = true) => {
61
+ if (scope.disposed || !Number.isFinite(value)) return;
62
+ const candidate = Math.max(1, Math.min(total, Math.round(value)));
63
+ if (candidate === page) return;
64
+ const focused = pages.contains(document.activeElement);
65
+ page = candidate;
66
+ render();
67
+ if (focused) pages.querySelector<HTMLButtonElement>('[aria-current]')?.focus();
68
+ if (notify) {
69
+ options.onChange?.(page);
70
+ root.dispatchEvent(new CustomEvent('mk:change', { bubbles: true, detail: { page, totalPages: total } }));
71
+ }
72
+ };
73
+ scope.on(root, 'click', (event) => {
74
+ const target = (event.target as Element).closest<HTMLButtonElement>('button');
75
+ if (!target || target.disabled) return;
76
+ if (target === previous) setPage(page - 1);
77
+ else if (target === next) setPage(page + 1);
78
+ else if (target.dataset.mkPage) setPage(Number(target.dataset.mkPage));
79
+ });
80
+ scope.on(root, 'keydown', (event) => {
81
+ if (!(event.target instanceof HTMLButtonElement)) return;
82
+ const to = event.key === 'ArrowRight' ? page + 1 : event.key === 'ArrowLeft' ? page - 1 : event.key === 'Home' ? 1 : event.key === 'End' ? total : null;
83
+ if (to === null) return;
84
+ event.preventDefault();
85
+ setPage(to);
86
+ pages.querySelector<HTMLButtonElement>('[aria-current]')?.focus();
87
+ });
88
+ scope.add(() => pages.replaceChildren(...original));
89
+ render();
90
+ return {
91
+ setPage,
92
+ setTotalPages(value) {
93
+ if (scope.disposed || !Number.isFinite(value)) return;
94
+ total = positive(value, 1);
95
+ if (page > total) setPage(total);
96
+ else render();
97
+ },
98
+ get page() { return page; },
99
+ get totalPages() { return total; },
100
+ destroy: () => scope.dispose(),
101
+ };
102
+ }
@@ -0,0 +1,10 @@
1
+ .mk-pagination, mk-pagination { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: .35rem; }
2
+ .mk-pagination [data-mk-pagination-pages], mk-pagination [data-mk-pagination-pages] { display: flex; align-items: center; gap: .25rem; }
3
+ .mk-pagination button, mk-pagination button { min-width: 2.5rem; height: 2.5rem; border: 0; border-radius: var(--mk-radius-full); background: var(--mk-surface-2); color: var(--mk-text); font: inherit; cursor: pointer; transition: transform .3s, background .3s, color .3s; }
4
+ .mk-pagination button[aria-current], mk-pagination button[aria-current] { background: var(--mk-accent); color: var(--mk-on-accent); }
5
+ .mk-pagination button:hover:not(:disabled), mk-pagination button:hover:not(:disabled) { transform: translateY(-3px); }
6
+ .mk-pagination button:active:not(:disabled), mk-pagination button:active:not(:disabled) { transform: scale(.9); }
7
+ .mk-pagination button:focus-visible, mk-pagination button:focus-visible { outline: 2px solid var(--mk-accent); outline-offset: 3px; }
8
+ .mk-pagination button:disabled, mk-pagination button:disabled { opacity: .35; cursor: default; }
9
+ .mk-pagination__gap { padding: 0 .15rem; color: var(--mk-muted); }
10
+ @media (prefers-reduced-motion: reduce) { .mk-pagination button, mk-pagination button { transition: none; transform: none !important; } }
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { useRef, type HTMLAttributes } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createPagination, type PaginationOptions } from './engine';
5
+ export interface PaginationProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange'>, PaginationOptions {
6
+ previousLabel?: string; nextLabel?: string;
7
+ }
8
+ export function Pagination({ totalPages, page, siblings, pageLabel, onChange, previousLabel = 'Página anterior', nextLabel = 'Página siguiente', className, ...props }: PaginationProps) {
9
+ const ref = useRef<HTMLElement>(null);
10
+ const selected = useRef<number | undefined>(undefined);
11
+ const change = useLatest(onChange);
12
+ const label = useLatest(pageLabel);
13
+ useIsomorphicLayoutEffect(() => {
14
+ const instance = createPagination(ref.current, { totalPages, page: page ?? selected.current, siblings, pageLabel: (n) => label.current?.(n) ?? `Página ${n}`, onChange: (n) => change.current?.(n) });
15
+ return () => { selected.current = instance.page; instance.destroy(); };
16
+ }, [totalPages, page, siblings, pageLabel]);
17
+ return <nav aria-label="Paginación" {...props} ref={ref} className={['mk-pagination', className].filter(Boolean).join(' ')}>
18
+ <button type="button" data-mk-pagination-previous aria-label={previousLabel}>←</button>
19
+ <span data-mk-pagination-pages />
20
+ <button type="button" data-mk-pagination-next aria-label={nextLabel}>→</button>
21
+ </nav>;
22
+ }
@@ -0,0 +1,17 @@
1
+ # password-strength
2
+
3
+ Campo de contraseña nativo con fortaleza en vivo: botón mostrar/ocultar (el ojo cambia con blur y su barra se dibuja), medidor de cuatro segmentos que se encienden en cascada y cambian de color por nivel (débil → aceptable → fuerte → muy fuerte), etiqueta que cambia con blur y lista de requisitos cuyos ✓ se dibujan. Un estado educado anuncia el nivel cuando dejas de escribir.
4
+
5
+ ```html
6
+ <mk-password-strength class="mk-password-strength">
7
+ <input type="password" name="password" autocomplete="new-password" aria-label="Contraseña" />
8
+ </mk-password-strength>
9
+ ```
10
+
11
+ `defineElements()`, `createPasswordStrength(root, options)` o `<PasswordStrength name="password" requireScore={3} onStrength={setResult} />` (el campo sigue sin control; `onValueChange` da el texto).
12
+
13
+ Opciones: `minLength=8`, `score(value)` (sustituye la puntuación), `requireScore` (por debajo, el campo se marca inválido con `setCustomValidity`), `labels {show, hide, invalid, checks}`, `onChange(result)`. API: `score`, `value`, `destroy()`. Evento `mk:strength` con `{score, label, met}`.
14
+
15
+ Utilidad pura `scorePassword(value, minLength)`: la longitud y la variedad suben la puntuación; contraseñas comunes, secuencias (abc, 123) y repeticiones (aaa) la bajan. Es orientativa: el servidor decide qué acepta.
16
+
17
+ Variables: `--mk-password-weak`, `--mk-password-fair`, `--mk-password-good`, `--mk-password-strong`. Movimiento reducido: los segmentos y la etiqueta cambian sin animación.
@@ -0,0 +1,22 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createPasswordStrength, type PasswordStrengthInstance } from './engine';
3
+
4
+ /**
5
+ * <mk-password-strength class="mk-password-strength" min-length="10">
6
+ * <input type="password" name="password" autocomplete="new-password" aria-label="Contraseña" />
7
+ * </mk-password-strength>
8
+ * Emits mk:strength ({score, label, met}).
9
+ */
10
+ export class MkPasswordStrengthElement extends MkElement<PasswordStrengthInstance> {
11
+ static options: Record<string, OptionType> = { minLength: 'number', requireScore: 'number', labels: 'json' };
12
+
13
+ protected create(options: Record<string, unknown>) {
14
+ return createPasswordStrength(this, options);
15
+ }
16
+
17
+ get score() {
18
+ return this.instance?.score ?? 0;
19
+ }
20
+ }
21
+
22
+ export const definePasswordStrength = () => defineElement('mk-password-strength', MkPasswordStrengthElement);