@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,56 @@
1
+ # activity-feed
2
+
3
+ Registro de acciones de usuarios en el sistema: lo más reciente arriba, agrupado por día («Hoy», «Ayer», «Lunes, 16 de noviembre»). Cada entrada tiene un avatar (foto, o iniciales sobre un color que sale del nombre, así cada persona conserva el suyo) con una insignia del tipo de acción, la frase «**Ana Gómez** comentó en <u>Tarea 12</u>», una hora relativa que se actualiza cada minuto y, si hace falta, una cita.
4
+
5
+ La actividad nueva (`prepend()`) entra arriba: las entradas de abajo se deslizan (el resorte de layout del kit, .62 s) mientras las nuevas caen saliendo de un blur y conservan un resaltado suave que se apaga. Si quien lee bajó por la lista, en lugar de moverle el contenido aparece una píldora «↑ 3 novedades» (el número rueda con cada llegada); al pulsarla sube y las mete. «Cargar más» al final pide entradas antiguas a `onLoadMore()` y las deja entrar en cascada; cuando ya no hay, lo dice.
6
+
7
+ ## HTML / Astro
8
+
9
+ ```html
10
+ <mk-activity-feed class="mk-activity-feed" style="--mk-af-height: 28rem"
11
+ items='[{"id":"1","actor":"Ana Gómez","action":"comentó en","target":"Tarea 12","href":"/tareas/12","time":"2026-11-23T09:12","kind":"comment","detail":"¿Lo vemos mañana?"},
12
+ {"id":"2","actor":{"name":"Luis Peña","avatar":"/luis.jpg"},"action":"cerró","target":"Tarea 9","time":"2026-11-22T18:40","kind":"complete"}]'></mk-activity-feed>
13
+
14
+ <script>
15
+ const feed = document.querySelector('mk-activity-feed');
16
+ // Llega algo en vivo (un socket, un intervalo…):
17
+ feed.prependItems([{ id: '3', actor: 'Sofía Ruiz', action: 'subió', target: 'propuesta.pdf', time: new Date(), kind: 'upload' }]);
18
+ // Entradas antiguas cuando quieras:
19
+ feed.appendItems(older);
20
+ </script>
21
+ ```
22
+
23
+ ## React
24
+
25
+ ```tsx
26
+ const feed = useRef<ActivityFeedHandle>(null);
27
+
28
+ <ActivityFeed ref={feed} items={recent} onLoadMore={() => api.olderThan(cursor)} style={{ maxHeight: 448 }} />;
29
+
30
+ // después
31
+ feed.current?.prepend([nueva]);
32
+ ```
33
+
34
+ ## Opciones
35
+
36
+ | Opción | Atributo | Por defecto | Qué hace |
37
+ |---|---|---|---|
38
+ | `items` | `items` (JSON) | `[]` | `{ id, actor, action, target?, href?, time, detail?, kind? }[]`; `actor` es un nombre o `{ name, avatar? }`. |
39
+ | `onLoadMore()` | — | — | Muestra «Cargar más»; resuelve con las entradas anteriores o con `[]` cuando no hay más. |
40
+ | `locale` | `locale` | el `lang` más cercano | Idioma de las fechas. |
41
+ | `label` | `label` | `'Actividad'` | Nombre accesible. |
42
+ | `emptyLabel` | `empty-label` | `'Sin actividad todavía'` | Texto sin entradas. |
43
+
44
+ `kind` (la insignia): `comment`, `create`, `update`, `delete`, `upload`, `complete` o `mention`.
45
+
46
+ ## API
47
+
48
+ `prepend(items)` (en el elemento, `prependItems`), `append(items)` (`appendItems`), `setItems(items)`, `items`, `destroy()`.
49
+
50
+ ## Variables CSS
51
+
52
+ `--mk-af-height` (alto máximo antes de desplazarse dentro; también vale `max-height` en el elemento), `--mk-af-avatar-l` y `--mk-af-avatar-ink-l` (luminosidad del fondo y de las iniciales del avatar).
53
+
54
+ ## Accesibilidad
55
+
56
+ Un `role="feed"` con cada entrada como `article`; las horas son `<time datetime>` con la fecha completa en su título. Un estado discreto anuncia lo nuevo («Sofía Ruiz subió propuesta.pdf», «3 novedades») y lo que se cargó. Al pulsar la píldora el foco pasa a la primera entrada nueva. Con movimiento reducido todo cambia sin animación.
@@ -0,0 +1,239 @@
1
+ /* activity-feed — day headings in small caps, entries on 12 px rows: a 2.25rem avatar (a picture, or initials on a hue
2
+ from the name) with a badge for the kind of action, the sentence, a quote on surface-3, a muted time. New entries
3
+ keep an accent-soft highlight that fades; the "novedades" pill sticks to the top. Give the root a max-height (or
4
+ --mk-af-height) to scroll inside it. */
5
+ .mk-activity-feed,
6
+ mk-activity-feed {
7
+ --mk-af-height: none;
8
+ --mk-af-avatar-l: 86%;
9
+ --mk-af-avatar-ink-l: 28%;
10
+ position: relative;
11
+ display: block;
12
+ max-height: var(--mk-af-height);
13
+ overflow-y: auto;
14
+ overscroll-behavior: contain;
15
+ color: var(--mk-text);
16
+ }
17
+
18
+ @media (prefers-color-scheme: dark) {
19
+ :where(:root:not([data-mk-mode='light'])) :is(.mk-activity-feed, mk-activity-feed) {
20
+ --mk-af-avatar-l: 28%;
21
+ --mk-af-avatar-ink-l: 86%;
22
+ }
23
+ }
24
+
25
+ :where(:root[data-mk-mode='dark']) :is(.mk-activity-feed, mk-activity-feed) {
26
+ --mk-af-avatar-l: 28%;
27
+ --mk-af-avatar-ink-l: 86%;
28
+ }
29
+
30
+ [data-mk-af-new] {
31
+ position: sticky;
32
+ top: 0.5rem;
33
+ z-index: 2;
34
+ display: flex;
35
+ align-items: center;
36
+ gap: 0.15rem;
37
+ width: max-content;
38
+ height: 2rem;
39
+ margin: 0 auto 0.5rem;
40
+ padding: 0 0.9rem;
41
+ border: 0;
42
+ border-radius: 999px;
43
+ background: var(--mk-accent);
44
+ color: var(--mk-on-accent);
45
+ box-shadow: 0 8px 20px -8px color-mix(in srgb, var(--mk-shadow-color, #000) 45%, transparent);
46
+ font: inherit;
47
+ font-size: 0.8125rem;
48
+ font-weight: 600;
49
+ cursor: pointer;
50
+ }
51
+
52
+ [data-mk-af-new][hidden] {
53
+ display: none;
54
+ }
55
+
56
+ [data-mk-af-day] + [data-mk-af-day] {
57
+ margin-top: 1rem;
58
+ }
59
+
60
+ /* Section > heading: strong enough for a page's own h3 rules (a card's `.card h3`, say). */
61
+ [data-mk-af-day] > [data-mk-af-heading] {
62
+ margin: 0 0 0.35rem;
63
+ padding: 0 0.5rem;
64
+ color: var(--mk-muted);
65
+ font-size: 0.75rem;
66
+ font-weight: 700;
67
+ letter-spacing: 0.04em;
68
+ text-transform: uppercase;
69
+ }
70
+
71
+ [data-mk-af-day] ul {
72
+ display: grid;
73
+ gap: 0.15rem;
74
+ margin: 0;
75
+ padding: 0;
76
+ list-style: none;
77
+ }
78
+
79
+ [data-mk-af-item] {
80
+ display: grid;
81
+ grid-template-columns: 2.25rem minmax(0, 1fr);
82
+ gap: 0.75rem;
83
+ padding: 0.6rem 0.5rem;
84
+ border-radius: 12px;
85
+ outline: none;
86
+ }
87
+
88
+ [data-mk-af-item]:focus-visible {
89
+ box-shadow: inset 0 0 0 2px var(--mk-accent);
90
+ }
91
+
92
+ [data-mk-af-item][data-mk-fresh] {
93
+ animation: mk-af-fresh 2.4s ease-out both;
94
+ }
95
+
96
+ @keyframes mk-af-fresh {
97
+ from {
98
+ background: color-mix(in srgb, var(--mk-accent-soft) 90%, transparent);
99
+ }
100
+
101
+ to {
102
+ background: transparent;
103
+ }
104
+ }
105
+
106
+ [data-mk-af-avatar] {
107
+ position: relative;
108
+ display: grid;
109
+ place-items: center;
110
+ width: 2.25rem;
111
+ height: 2.25rem;
112
+ border-radius: 50%;
113
+ background: hsl(var(--mk-af-hue, 210) 55% var(--mk-af-avatar-l));
114
+ color: hsl(var(--mk-af-hue, 210) 50% var(--mk-af-avatar-ink-l));
115
+ font-size: 0.8125rem;
116
+ font-weight: 700;
117
+ }
118
+
119
+ [data-mk-af-avatar] img {
120
+ width: 100%;
121
+ height: 100%;
122
+ border-radius: 50%;
123
+ object-fit: cover;
124
+ }
125
+
126
+ [data-mk-af-badge] {
127
+ position: absolute;
128
+ right: -3px;
129
+ bottom: -3px;
130
+ display: grid;
131
+ place-items: center;
132
+ width: 1.05rem;
133
+ height: 1.05rem;
134
+ border-radius: 50%;
135
+ background: var(--mk-text);
136
+ color: var(--mk-surface-1, var(--mk-bg));
137
+ box-shadow: 0 0 0 2px var(--mk-surface-1, var(--mk-bg));
138
+ }
139
+
140
+ [data-mk-af-badge='comment'],
141
+ [data-mk-af-badge='mention'] {
142
+ background: var(--mk-accent);
143
+ color: var(--mk-on-accent);
144
+ }
145
+
146
+ [data-mk-af-badge='complete'] {
147
+ background: #1f9d5b;
148
+ color: #fff;
149
+ }
150
+
151
+ [data-mk-af-badge='delete'] {
152
+ background: var(--mk-error, #c62828);
153
+ color: var(--mk-on-error, #fff);
154
+ }
155
+
156
+ [data-mk-af-line] {
157
+ margin: 0;
158
+ font-size: 0.9375rem;
159
+ line-height: 1.45;
160
+ overflow-wrap: anywhere;
161
+ }
162
+
163
+ [data-mk-af-line] b {
164
+ font-weight: 600;
165
+ }
166
+
167
+ [data-mk-af-target] {
168
+ color: inherit;
169
+ font-weight: 600;
170
+ text-decoration: underline;
171
+ text-decoration-color: color-mix(in srgb, currentColor 30%, transparent);
172
+ text-underline-offset: 3px;
173
+ }
174
+
175
+ a[data-mk-af-target]:hover {
176
+ text-decoration-color: currentColor;
177
+ }
178
+
179
+ [data-mk-af-detail] {
180
+ margin: 0.4rem 0 0;
181
+ padding: 0.5rem 0.75rem;
182
+ border-radius: 10px;
183
+ background: var(--mk-surface-3);
184
+ font-size: 0.875rem;
185
+ line-height: 1.5;
186
+ overflow-wrap: anywhere;
187
+ }
188
+
189
+ [data-mk-af-time] {
190
+ display: block;
191
+ margin-top: 0.2rem;
192
+ color: var(--mk-muted);
193
+ font-size: 0.75rem;
194
+ }
195
+
196
+ [data-mk-af-more] {
197
+ display: block;
198
+ height: 2.25rem;
199
+ margin: 0.75rem auto 0;
200
+ padding: 0 1rem;
201
+ border: 0;
202
+ border-radius: 10px;
203
+ background: var(--mk-surface-3);
204
+ color: var(--mk-text);
205
+ font: inherit;
206
+ font-size: 0.875rem;
207
+ font-weight: 600;
208
+ cursor: pointer;
209
+ }
210
+
211
+ [data-mk-af-more][hidden],
212
+ [data-mk-af-empty][hidden] {
213
+ display: none;
214
+ }
215
+
216
+ [data-mk-af-more][aria-busy='true'] {
217
+ cursor: progress;
218
+ opacity: 0.7;
219
+ }
220
+
221
+ [data-mk-af-more]:focus-visible,
222
+ [data-mk-af-new]:focus-visible {
223
+ outline: 2px solid var(--mk-accent);
224
+ outline-offset: 2px;
225
+ }
226
+
227
+ [data-mk-af-end],
228
+ [data-mk-af-empty] {
229
+ margin: 0.75rem 0 0;
230
+ color: var(--mk-muted);
231
+ font-size: 0.875rem;
232
+ text-align: center;
233
+ }
234
+
235
+ @media (prefers-reduced-motion: reduce) {
236
+ [data-mk-af-item][data-mk-fresh] {
237
+ animation: none;
238
+ }
239
+ }
@@ -0,0 +1,36 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createActivityFeed, type ActivityFeedInstance, type ActivityItem } from './engine';
3
+
4
+ /**
5
+ * <mk-activity-feed class="mk-activity-feed" items='[{"id":"1","actor":"Ana Gómez","action":"comentó en","target":"Tarea 12","time":"2026-11-23T09:12","kind":"comment"}]'>
6
+ * </mk-activity-feed>. element.prepend([...]) / element.append([...]) for live and older activity.
7
+ */
8
+ export class MkActivityFeedElement extends MkElement<ActivityFeedInstance> {
9
+ static options: Record<string, OptionType> = { items: 'json', locale: 'string', label: 'string', emptyLabel: 'string' };
10
+
11
+ protected create(options: Record<string, unknown>) {
12
+ return createActivityFeed(this, options);
13
+ }
14
+
15
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
16
+ if (previous === next || !this.instance) return;
17
+ if (name === 'items') {
18
+ try {
19
+ this.instance.setItems(JSON.parse(next ?? '[]') as ActivityItem[]);
20
+ } catch {
21
+ // Malformed JSON: keep the current entries.
22
+ }
23
+ } else super.attributeChangedCallback(name, previous, next);
24
+ }
25
+
26
+ /** New activity on top (not Element.prepend). */
27
+ prependItems(items: ActivityItem[]) {
28
+ this.instance?.prepend(items);
29
+ }
30
+
31
+ appendItems(items: ActivityItem[]) {
32
+ this.instance?.append(items);
33
+ }
34
+ }
35
+
36
+ export const defineActivityFeed = () => defineElement('mk-activity-feed', MkActivityFeedElement);
@@ -0,0 +1,351 @@
1
+ import { gsap, EASE, Flip, registerMotion } from '../../core/gsap';
2
+ import { reduced } from '../../core/reduced';
3
+ import { createScope } from '../../core/scope';
4
+ import { TIMING } from '../../core/timing';
5
+ import { inert, type Instance } from '../../core/types';
6
+ import { dayKey, fromJson } from '../../core/dates';
7
+ import { dayLabel, everyMinute, fullTime, relativeTime, toTime, type TimeInput } from '../../core/relative-time';
8
+ import { blurIn, press, rollDigits } from '../../motion/micro';
9
+
10
+ /*
11
+ * Activity feed: what people did, newest first, under day headings ("Hoy",
12
+ * "Ayer", "Lunes, 12 de noviembre"). Each entry has an avatar (a picture, or
13
+ * initials on a colour from the name) with a badge for the kind of action,
14
+ * "<b>Ana</b> comentó en <a>Tarea 12</a>", a time ("hace 5 min", refreshed
15
+ * every minute) and an optional quote. New activity (prepend()) comes in at the
16
+ * top: the entries below glide down (layout spring) while the new ones drop in
17
+ * out of a blur and keep a soft highlight that fades. If the reader has
18
+ * scrolled down, a "3 novedades" pill pops in at the top instead (its count
19
+ * rolls); pressing it scrolls up and brings them in. "Cargar más" at the end
20
+ * asks onLoadMore() for older entries (working while it waits) and flows them in.
21
+ */
22
+
23
+ export interface ActivityActor {
24
+ name: string;
25
+ /** A picture URL; without it, the initials. */
26
+ avatar?: string;
27
+ }
28
+
29
+ export type ActivityKind = 'comment' | 'create' | 'update' | 'delete' | 'upload' | 'complete' | 'mention';
30
+
31
+ export interface ActivityItem {
32
+ id: string;
33
+ actor: ActivityActor | string;
34
+ /** The verb: "comentó en", "cerró", "subió". */
35
+ action: string;
36
+ /** What it was done to. */
37
+ target?: string;
38
+ /** A link for the target. */
39
+ href?: string;
40
+ time: TimeInput;
41
+ /** A quote under it (the comment). */
42
+ detail?: string;
43
+ kind?: ActivityKind;
44
+ }
45
+
46
+ export interface ActivityFeedOptions {
47
+ items?: ActivityItem[];
48
+ locale?: string;
49
+ /** Accessible name (default 'Actividad'). */
50
+ label?: string;
51
+ /** Older entries for "Cargar más": resolve with them, or with [] when there are no more. */
52
+ onLoadMore?: () => Promise<ActivityItem[]> | ActivityItem[];
53
+ emptyLabel?: string;
54
+ }
55
+
56
+ export interface ActivityFeedInstance extends Instance {
57
+ /** New activity on top (or behind the "novedades" pill when the reader is scrolled down). */
58
+ prepend(items: ActivityItem[]): void;
59
+ append(items: ActivityItem[]): void;
60
+ setItems(items: ActivityItem[]): void;
61
+ readonly items: ActivityItem[];
62
+ }
63
+
64
+ const SVG = (path: string) => `<svg aria-hidden="true" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="${path}"/></svg>`;
65
+ const KIND_ICON: Record<ActivityKind, string> = {
66
+ comment: SVG('M5 6h14v9H9l-4 4z'),
67
+ create: SVG('M12 5v14M5 12h14'),
68
+ update: SVG('M5 19l1-4L16 5l3 3L9 18z'),
69
+ delete: SVG('M6 7h12M9 7V5h6v2M8 7l1 12h6l1-12'),
70
+ upload: SVG('M12 19V6M6 12l6-6 6 6'),
71
+ complete: SVG('M5 12.5l4.5 4.5L19 7.5'),
72
+ mention: SVG('M16 12a4 4 0 1 1-1.2-2.8M16 9v4a2 2 0 0 0 4 0v-1a8 8 0 1 0-3 6.2'),
73
+ };
74
+
75
+ const nameOf = (actor: ActivityActor | string) => (typeof actor === 'string' ? actor : actor.name);
76
+ const initials = (name: string) =>
77
+ name
78
+ .split(/\s+/)
79
+ .filter(Boolean)
80
+ .slice(0, 2)
81
+ .map((part) => part[0]!.toLocaleUpperCase())
82
+ .join('');
83
+ /** A hue from the name, so a person keeps their colour. */
84
+ const hueOf = (name: string) => [...name].reduce((hash, char) => (hash * 31 + char.charCodeAt(0)) % 360, 7);
85
+
86
+ export function createActivityFeed(root: HTMLElement | null, options: ActivityFeedOptions = {}): ActivityFeedInstance {
87
+ if (!root) return inert({ prepend() {}, append() {}, setItems() {}, items: [] });
88
+ registerMotion();
89
+ const scope = createScope();
90
+ const locale = options.locale ?? root.closest('[lang]')?.getAttribute('lang') ?? 'es';
91
+ // JSON text too (data-mk-items through autoInit).
92
+ let items = [...(fromJson(options.items) ?? [])];
93
+ let waiting: ActivityItem[] = [];
94
+ const effects = new Set<gsap.core.Animation>();
95
+ const keep = (animation: gsap.core.Animation | null) => {
96
+ if (!animation) return;
97
+ effects.add(animation);
98
+ // Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
99
+ for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
100
+ };
101
+
102
+ const pill = document.createElement('button');
103
+ pill.type = 'button';
104
+ pill.dataset.mkAfNew = '';
105
+ pill.hidden = true;
106
+ const pillCount = document.createElement('span');
107
+ pillCount.dataset.mkAfNewCount = '';
108
+ const pillText = document.createElement('span');
109
+ pill.append('↑ ', pillCount, pillText);
110
+ const feed = document.createElement('div');
111
+ feed.dataset.mkAfFeed = '';
112
+ feed.setAttribute('role', 'feed');
113
+ feed.setAttribute('aria-label', options.label ?? 'Actividad');
114
+ const empty = document.createElement('p');
115
+ empty.dataset.mkAfEmpty = '';
116
+ empty.textContent = options.emptyLabel ?? 'Sin actividad todavía';
117
+ const more = document.createElement('button');
118
+ more.type = 'button';
119
+ more.dataset.mkAfMore = '';
120
+ more.textContent = 'Cargar más';
121
+ more.hidden = !options.onLoadMore;
122
+ const status = document.createElement('span');
123
+ status.className = 'mk-sr-only';
124
+ status.setAttribute('role', 'status');
125
+ root.append(pill, feed, empty, more, status);
126
+ scope.add(() => {
127
+ for (const effect of effects) effect.kill();
128
+ effects.clear();
129
+ for (const part of [pill, feed, empty, more, status]) part.remove();
130
+ });
131
+ scope.attr(root, 'data-mk-ready', '');
132
+
133
+ const entry = (item: ActivityItem) => {
134
+ const li = document.createElement('li');
135
+ li.dataset.mkAfItem = '';
136
+ li.dataset.id = item.id;
137
+ li.setAttribute('role', 'article');
138
+ li.tabIndex = -1;
139
+ const name = nameOf(item.actor);
140
+ const avatar = document.createElement('span');
141
+ avatar.dataset.mkAfAvatar = '';
142
+ avatar.setAttribute('aria-hidden', 'true');
143
+ avatar.style.setProperty('--mk-af-hue', String(hueOf(name)));
144
+ const picture = typeof item.actor === 'string' ? undefined : item.actor.avatar;
145
+ if (picture) {
146
+ const img = document.createElement('img');
147
+ img.src = picture;
148
+ img.alt = '';
149
+ img.loading = 'lazy';
150
+ avatar.append(img);
151
+ } else avatar.textContent = initials(name);
152
+ if (item.kind) {
153
+ const badge = document.createElement('span');
154
+ badge.dataset.mkAfBadge = item.kind;
155
+ badge.innerHTML = KIND_ICON[item.kind];
156
+ avatar.append(badge);
157
+ }
158
+ const body = document.createElement('div');
159
+ body.dataset.mkAfBody = '';
160
+ const line = document.createElement('p');
161
+ line.dataset.mkAfLine = '';
162
+ const who = document.createElement('b');
163
+ who.textContent = name;
164
+ line.append(who, ` ${item.action}`);
165
+ if (item.target) {
166
+ line.append(' ');
167
+ const target = document.createElement(item.href ? 'a' : 'span');
168
+ target.dataset.mkAfTarget = '';
169
+ if (target instanceof HTMLAnchorElement) target.href = item.href!;
170
+ target.textContent = item.target;
171
+ line.append(target);
172
+ }
173
+ body.append(line);
174
+ if (item.detail) {
175
+ const quote = document.createElement('blockquote');
176
+ quote.dataset.mkAfDetail = '';
177
+ quote.textContent = item.detail;
178
+ body.append(quote);
179
+ }
180
+ const date = toTime(item.time);
181
+ if (date) {
182
+ const time = document.createElement('time');
183
+ time.dataset.mkAfTime = '';
184
+ time.dateTime = date.toISOString();
185
+ time.title = fullTime(date, locale);
186
+ time.textContent = relativeTime(date, new Date(), locale);
187
+ body.append(time);
188
+ }
189
+ li.append(avatar, body);
190
+ return li;
191
+ };
192
+
193
+ const render = () => {
194
+ const days = new Map<string, { date: Date | null; list: HTMLUListElement }>();
195
+ const sections: HTMLElement[] = [];
196
+ for (const item of items) {
197
+ const date = toTime(item.time);
198
+ const key = date ? dayKey(date) : '';
199
+ let group = days.get(key);
200
+ if (!group) {
201
+ const section = document.createElement('section');
202
+ section.dataset.mkAfDay = key;
203
+ const heading = document.createElement('h3');
204
+ heading.dataset.mkAfHeading = '';
205
+ heading.textContent = date ? dayLabel(date, new Date(), locale) : 'Sin fecha';
206
+ const list = document.createElement('ul');
207
+ section.append(heading, list);
208
+ sections.push(section);
209
+ group = { date, list };
210
+ days.set(key, group);
211
+ }
212
+ group.list.append(entry(item));
213
+ }
214
+ feed.replaceChildren(...sections);
215
+ empty.hidden = items.length > 0;
216
+ };
217
+
218
+ const refreshTimes = () => {
219
+ for (const time of feed.querySelectorAll<HTMLTimeElement>('[data-mk-af-time]')) time.textContent = relativeTime(new Date(time.dateTime), new Date(), locale);
220
+ for (const section of feed.querySelectorAll<HTMLElement>('[data-mk-af-day]')) {
221
+ const key = section.dataset.mkAfDay!;
222
+ if (key) section.querySelector('h3')!.textContent = dayLabel(toTime(key)!, new Date(), locale);
223
+ }
224
+ };
225
+ scope.add(everyMinute(refreshTimes));
226
+
227
+ /** Re-render with `change`; what stays glides (Flip), the given new ids come in from `from`. */
228
+ const reflow = (change: () => void, fresh: Set<string>, from: 'top' | 'bottom') => {
229
+ const all = () => [...feed.querySelectorAll<HTMLElement>('[data-mk-af-item], [data-mk-af-heading]')];
230
+ for (const element of all()) element.dataset.flipId = element.dataset.id ?? `day-${element.closest<HTMLElement>('[data-mk-af-day]')!.dataset.mkAfDay}`;
231
+ const state = reduced() ? null : Flip.getState(all());
232
+ change();
233
+ for (const element of all()) element.dataset.flipId = element.dataset.id ?? `day-${element.closest<HTMLElement>('[data-mk-af-day]')!.dataset.mkAfDay}`;
234
+ const arriving = [...feed.querySelectorAll<HTMLElement>('[data-mk-af-item]')].filter((li) => fresh.has(li.dataset.id!));
235
+ for (const li of arriving) if (from === 'top') li.setAttribute('data-mk-fresh', '');
236
+ if (!state) return;
237
+ keep(
238
+ Flip.from(state, {
239
+ targets: all(),
240
+ duration: TIMING.layoutTravel,
241
+ ease: EASE.layoutReflow,
242
+ onEnter: (entering) => gsap.fromTo(entering, { opacity: 0, y: from === 'top' ? -14 : 14, filter: 'blur(10px)' }, { opacity: 1, y: 0, filter: 'blur(0px)', duration: 0.45, stagger: 0.05, ease: 'power2.out', clearProps: 'opacity,transform,filter' }),
243
+ }),
244
+ );
245
+ };
246
+
247
+ const showPill = () => {
248
+ const count = waiting.length;
249
+ const text = String(count);
250
+ pillText.textContent = count === 1 ? ' novedad' : ' novedades';
251
+ status.textContent = `${count} ${count === 1 ? 'novedad' : 'novedades'}`;
252
+ if (pill.hidden) {
253
+ pill.hidden = false;
254
+ pillCount.textContent = text;
255
+ if (!reduced()) keep(gsap.fromTo(pill, { opacity: 0, y: -10, scale: 0.85, filter: 'blur(6px)' }, { opacity: 1, y: 0, scale: 1, filter: 'blur(0px)', duration: 0.35, ease: EASE.pop, clearProps: 'opacity,transform,filter' }));
256
+ } else keep(rollDigits(pillCount, text, 1, 0.3));
257
+ };
258
+
259
+ const bringIn = (fresh: ActivityItem[]) => {
260
+ const ids = new Set(fresh.map((item) => item.id));
261
+ reflow(() => {
262
+ items = [...fresh, ...items.filter((item) => !ids.has(item.id))];
263
+ render();
264
+ }, ids, 'top');
265
+ };
266
+
267
+ scope.on(pill, 'click', () => {
268
+ const fresh = waiting;
269
+ waiting = [];
270
+ const finish = () => {
271
+ pill.hidden = true;
272
+ bringIn(fresh);
273
+ feed.querySelector<HTMLElement>('[data-mk-af-item]')?.focus({ preventScroll: true });
274
+ };
275
+ if (reduced() || root.scrollTop === 0) {
276
+ root.scrollTop = 0;
277
+ return finish();
278
+ }
279
+ keep(gsap.to(root, { scrollTop: 0, duration: 0.5, ease: EASE.signature, onComplete: finish }));
280
+ });
281
+
282
+ scope.on(more, 'click', async () => {
283
+ if (!options.onLoadMore || more.getAttribute('aria-busy') === 'true') return;
284
+ keep(press(more));
285
+ more.setAttribute('aria-busy', 'true');
286
+ more.textContent = 'Cargando…';
287
+ let older: ActivityItem[] = [];
288
+ try {
289
+ older = await options.onLoadMore();
290
+ } catch {
291
+ more.textContent = 'Reintentar';
292
+ more.removeAttribute('aria-busy');
293
+ return;
294
+ }
295
+ if (scope.disposed) return;
296
+ more.removeAttribute('aria-busy');
297
+ if (!older.length) {
298
+ more.hidden = true;
299
+ status.textContent = 'No hay más actividad';
300
+ const end = document.createElement('p');
301
+ end.dataset.mkAfEnd = '';
302
+ end.textContent = 'No hay más actividad';
303
+ more.after(end);
304
+ scope.add(() => end.remove());
305
+ return;
306
+ }
307
+ more.textContent = 'Cargar más';
308
+ const known = new Set(items.map((item) => item.id));
309
+ const added = older.filter((item) => !known.has(item.id));
310
+ items = [...items, ...added];
311
+ render();
312
+ const arriving = [...feed.querySelectorAll<HTMLElement>('[data-mk-af-item]')].filter((li) => added.some((item) => item.id === li.dataset.id));
313
+ keep(blurIn(arriving, { from: 'rise', stagger: 0.04 }));
314
+ status.textContent = `${added.length} más`;
315
+ });
316
+
317
+ render();
318
+
319
+ return {
320
+ prepend(fresh) {
321
+ if (scope.disposed || !fresh.length) return;
322
+ if (root.scrollTop > 24) {
323
+ const ids = new Set(fresh.map((item) => item.id));
324
+ waiting = [...fresh, ...waiting.filter((item) => !ids.has(item.id))];
325
+ showPill();
326
+ return;
327
+ }
328
+ bringIn(fresh);
329
+ status.textContent = fresh.length === 1 ? `${nameOf(fresh[0]!.actor)} ${fresh[0]!.action}${fresh[0]!.target ? ` ${fresh[0]!.target}` : ''}` : `${fresh.length} novedades`;
330
+ },
331
+ append(older) {
332
+ if (scope.disposed) return;
333
+ const ids = new Set(older.map((item) => item.id));
334
+ reflow(() => {
335
+ items = [...items.filter((item) => !ids.has(item.id)), ...older];
336
+ render();
337
+ }, ids, 'bottom');
338
+ },
339
+ setItems(next) {
340
+ if (scope.disposed) return;
341
+ items = [...next];
342
+ waiting = [];
343
+ pill.hidden = true;
344
+ render();
345
+ },
346
+ get items() {
347
+ return [...items];
348
+ },
349
+ destroy: () => scope.dispose(),
350
+ };
351
+ }