@yewdiel21/motion-kit 0.10.0 → 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 (365) hide show
  1. package/README.md +155 -93
  2. package/dist/auto/index.d.ts +53 -3
  3. package/dist/auto/index.js +101 -3
  4. package/dist/auto/index.js.map +1 -1
  5. package/dist/{chunk-IKQ5FODN.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-XAKC2QY7.js → chunk-EY4BJKC4.js} +3 -28
  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/chart-tooltip.css +168 -0
  17. package/dist/css/components/chart.css +296 -0
  18. package/dist/css/components/code-block.css +264 -0
  19. package/dist/css/components/color-area.css +82 -0
  20. package/dist/css/components/color-field.css +67 -0
  21. package/dist/css/components/color-picker.css +131 -0
  22. package/dist/css/components/color-slider.css +113 -0
  23. package/dist/css/components/command-search.css +252 -0
  24. package/dist/css/components/context-menu.css +23 -0
  25. package/dist/css/components/credit-card.css +288 -0
  26. package/dist/css/components/data-table.css +591 -0
  27. package/dist/css/components/date-range-picker.css +168 -0
  28. package/dist/css/components/date-time-picker.css +178 -0
  29. package/dist/css/components/drawer.css +58 -0
  30. package/dist/css/components/editable-table.css +201 -0
  31. package/dist/css/components/empty-state.css +61 -0
  32. package/dist/css/components/file-explorer.css +633 -0
  33. package/dist/css/components/filter-builder.css +232 -0
  34. package/dist/css/components/hold-confirm.css +87 -0
  35. package/dist/css/components/hover-card.css +13 -7
  36. package/dist/css/components/image-preview.css +195 -0
  37. package/dist/css/components/json-viewer.css +272 -0
  38. package/dist/css/components/like-button.css +78 -0
  39. package/dist/css/components/link-preview.css +275 -0
  40. package/dist/css/components/loading-button.css +77 -0
  41. package/dist/css/components/multi-select.css +267 -0
  42. package/dist/css/components/notification-bell.css +127 -0
  43. package/dist/css/components/password-strength.css +183 -0
  44. package/dist/css/components/popover.css +25 -0
  45. package/dist/css/components/progress.css +140 -0
  46. package/dist/css/components/questionnaire.css +217 -0
  47. package/dist/css/components/range-calendar.css +186 -0
  48. package/dist/css/components/range-slider.css +192 -0
  49. package/dist/css/components/resizable-panel.css +243 -0
  50. package/dist/css/components/rich-text-editor.css +284 -0
  51. package/dist/css/components/scratch-card.css +75 -0
  52. package/dist/css/components/skeleton.css +108 -0
  53. package/dist/css/components/split-view.css +19 -0
  54. package/dist/css/components/star-rating.css +80 -0
  55. package/dist/css/components/stat-card.css +164 -0
  56. package/dist/css/components/swipe-actions.css +105 -0
  57. package/dist/css/components/tag-input.css +244 -0
  58. package/dist/css/components/time-field.css +88 -0
  59. package/dist/css/components/timeline.css +219 -0
  60. package/dist/css/components/tooltip.css +79 -0
  61. package/dist/css/components/tree-view.css +210 -0
  62. package/dist/css/components/virtual-list.css +195 -0
  63. package/dist/css/tokens.css +185 -181
  64. package/dist/elements/index.d.ts +674 -5
  65. package/dist/elements/index.js +1361 -8
  66. package/dist/elements/index.js.map +1 -1
  67. package/dist/{engine-GDGwQnJE.d.ts → engine-Cn6IhE3z.d.ts} +1 -1
  68. package/dist/engine-QECQnGz1.d.ts +33 -0
  69. package/dist/index.css +9104 -189
  70. package/dist/index.d.ts +89 -5
  71. package/dist/index.js +243 -9
  72. package/dist/index.js.map +1 -1
  73. package/dist/react/index.d.ts +579 -23
  74. package/dist/react/index.js +2042 -7
  75. package/dist/react/index.js.map +1 -1
  76. package/dist/{reduced-BmBzKH94.d.ts → reduced-BBUusld6.d.ts} +9 -2
  77. package/dist/reveal-DEvp6U6T.d.ts +2381 -0
  78. package/dist/{store-DYb3tTsZ.d.ts → store-DjkTBX7f.d.ts} +2 -2
  79. package/package.json +1 -1
  80. package/registry.json +406 -1
  81. package/src/auto/auto-init.ts +98 -0
  82. package/src/components/activity-feed/README.md +56 -0
  83. package/src/components/activity-feed/activity-feed.css +239 -0
  84. package/src/components/activity-feed/element.ts +36 -0
  85. package/src/components/activity-feed/engine.ts +351 -0
  86. package/src/components/activity-feed/react.tsx +32 -0
  87. package/src/components/attachment/README.md +27 -0
  88. package/src/components/attachment/attachment.css +265 -0
  89. package/src/components/attachment/element.ts +50 -0
  90. package/src/components/attachment/engine.ts +469 -0
  91. package/src/components/attachment/react.tsx +65 -0
  92. package/src/components/breadcrumb/README.md +38 -0
  93. package/src/components/breadcrumb/breadcrumb.css +109 -0
  94. package/src/components/breadcrumb/element.ts +27 -0
  95. package/src/components/breadcrumb/engine.ts +249 -0
  96. package/src/components/breadcrumb/react.tsx +30 -0
  97. package/src/components/chart/README.md +24 -0
  98. package/src/components/chart/chart.css +295 -0
  99. package/src/components/chart/element.ts +41 -0
  100. package/src/components/chart/engine.ts +811 -0
  101. package/src/components/chart/react.tsx +50 -0
  102. package/src/components/chart-tooltip/README.md +53 -0
  103. package/src/components/chart-tooltip/chart-tooltip.css +167 -0
  104. package/src/components/chart-tooltip/element.ts +37 -0
  105. package/src/components/chart-tooltip/engine.ts +442 -0
  106. package/src/components/chart-tooltip/react.tsx +47 -0
  107. package/src/components/code-block/README.md +19 -0
  108. package/src/components/code-block/code-block.css +263 -0
  109. package/src/components/code-block/element.ts +34 -0
  110. package/src/components/code-block/engine.ts +254 -0
  111. package/src/components/code-block/highlight.ts +244 -0
  112. package/src/components/code-block/react.tsx +94 -0
  113. package/src/components/color-area/README.md +50 -0
  114. package/src/components/color-area/color-area.css +81 -0
  115. package/src/components/color-area/element.ts +26 -0
  116. package/src/components/color-area/engine.ts +246 -0
  117. package/src/components/color-area/react.tsx +42 -0
  118. package/src/components/color-field/README.md +43 -0
  119. package/src/components/color-field/color-field.css +66 -0
  120. package/src/components/color-field/element.ts +26 -0
  121. package/src/components/color-field/engine.ts +174 -0
  122. package/src/components/color-field/react.tsx +42 -0
  123. package/src/components/color-picker/README.md +45 -0
  124. package/src/components/color-picker/color-picker.css +130 -0
  125. package/src/components/color-picker/element.ts +45 -0
  126. package/src/components/color-picker/engine.ts +314 -0
  127. package/src/components/color-picker/react.tsx +47 -0
  128. package/src/components/color-slider/README.md +55 -0
  129. package/src/components/color-slider/color-slider.css +112 -0
  130. package/src/components/color-slider/element.ts +26 -0
  131. package/src/components/color-slider/engine.ts +234 -0
  132. package/src/components/color-slider/react.tsx +45 -0
  133. package/src/components/command-search/README.md +15 -0
  134. package/src/components/command-search/command-search.css +251 -0
  135. package/src/components/command-search/element.ts +36 -0
  136. package/src/components/command-search/engine.ts +356 -0
  137. package/src/components/command-search/match.ts +53 -0
  138. package/src/components/command-search/react.tsx +66 -0
  139. package/src/components/context-menu/README.md +31 -0
  140. package/src/components/context-menu/context-menu.css +22 -0
  141. package/src/components/context-menu/element.ts +40 -0
  142. package/src/components/context-menu/engine.ts +153 -0
  143. package/src/components/context-menu/react.tsx +57 -0
  144. package/src/components/credit-card/README.md +20 -0
  145. package/src/components/credit-card/card.ts +77 -0
  146. package/src/components/credit-card/credit-card.css +287 -0
  147. package/src/components/credit-card/element.ts +33 -0
  148. package/src/components/credit-card/engine.ts +410 -0
  149. package/src/components/credit-card/react.tsx +36 -0
  150. package/src/components/data-table/README.md +83 -0
  151. package/src/components/data-table/data-table.css +590 -0
  152. package/src/components/data-table/element.ts +54 -0
  153. package/src/components/data-table/engine.ts +1059 -0
  154. package/src/components/data-table/react.tsx +94 -0
  155. package/src/components/date-range-picker/README.md +51 -0
  156. package/src/components/date-range-picker/date-range-picker.css +167 -0
  157. package/src/components/date-range-picker/element.ts +52 -0
  158. package/src/components/date-range-picker/engine.ts +352 -0
  159. package/src/components/date-range-picker/react.tsx +57 -0
  160. package/src/components/date-time-picker/README.md +48 -0
  161. package/src/components/date-time-picker/date-time-picker.css +177 -0
  162. package/src/components/date-time-picker/element.ts +47 -0
  163. package/src/components/date-time-picker/engine.ts +350 -0
  164. package/src/components/date-time-picker/react.tsx +54 -0
  165. package/src/components/dialog/README.md +103 -102
  166. package/src/components/dialog/element.ts +123 -118
  167. package/src/components/dialog/engine.ts +449 -432
  168. package/src/components/drawer/README.md +28 -0
  169. package/src/components/drawer/drawer.css +57 -0
  170. package/src/components/drawer/element.ts +21 -0
  171. package/src/components/drawer/engine.ts +56 -0
  172. package/src/components/drawer/react.tsx +85 -0
  173. package/src/components/editable-table/README.md +67 -0
  174. package/src/components/editable-table/editable-table.css +200 -0
  175. package/src/components/editable-table/element.ts +45 -0
  176. package/src/components/editable-table/engine.ts +775 -0
  177. package/src/components/editable-table/react.tsx +79 -0
  178. package/src/components/empty-state/README.md +41 -0
  179. package/src/components/empty-state/element.ts +23 -0
  180. package/src/components/empty-state/empty-state.css +60 -0
  181. package/src/components/empty-state/engine.ts +232 -0
  182. package/src/components/empty-state/react.tsx +34 -0
  183. package/src/components/file-explorer/README.md +67 -0
  184. package/src/components/file-explorer/element.ts +39 -0
  185. package/src/components/file-explorer/engine.ts +1307 -0
  186. package/src/components/file-explorer/file-explorer.css +632 -0
  187. package/src/components/file-explorer/react.tsx +77 -0
  188. package/src/components/filter-builder/README.md +47 -0
  189. package/src/components/filter-builder/element.ts +34 -0
  190. package/src/components/filter-builder/engine.ts +365 -0
  191. package/src/components/filter-builder/filter-builder.css +231 -0
  192. package/src/components/filter-builder/react.tsx +36 -0
  193. package/src/components/hold-confirm/README.md +13 -0
  194. package/src/components/hold-confirm/element.ts +24 -0
  195. package/src/components/hold-confirm/engine.ts +216 -0
  196. package/src/components/hold-confirm/hold-confirm.css +86 -0
  197. package/src/components/hold-confirm/react.tsx +28 -0
  198. package/src/components/hover-card/README.md +6 -2
  199. package/src/components/hover-card/engine.ts +68 -6
  200. package/src/components/hover-card/hover-card.css +13 -7
  201. package/src/components/image-preview/README.md +16 -0
  202. package/src/components/image-preview/element.ts +26 -0
  203. package/src/components/image-preview/engine.ts +325 -0
  204. package/src/components/image-preview/image-preview.css +194 -0
  205. package/src/components/image-preview/react.tsx +43 -0
  206. package/src/components/json-viewer/README.md +56 -0
  207. package/src/components/json-viewer/element.ts +37 -0
  208. package/src/components/json-viewer/engine.ts +440 -0
  209. package/src/components/json-viewer/json-viewer.css +271 -0
  210. package/src/components/json-viewer/react.tsx +56 -0
  211. package/src/components/like-button/README.md +13 -0
  212. package/src/components/like-button/element.ts +47 -0
  213. package/src/components/like-button/engine.ts +145 -0
  214. package/src/components/like-button/like-button.css +77 -0
  215. package/src/components/like-button/react.tsx +39 -0
  216. package/src/components/link-preview/README.md +19 -0
  217. package/src/components/link-preview/element.ts +29 -0
  218. package/src/components/link-preview/engine.ts +192 -0
  219. package/src/components/link-preview/link-preview.css +274 -0
  220. package/src/components/link-preview/react.tsx +101 -0
  221. package/src/components/loading-button/README.md +45 -0
  222. package/src/components/loading-button/element.ts +34 -0
  223. package/src/components/loading-button/engine.ts +201 -0
  224. package/src/components/loading-button/loading-button.css +76 -0
  225. package/src/components/loading-button/react.tsx +43 -0
  226. package/src/components/multi-select/README.md +48 -0
  227. package/src/components/multi-select/element.ts +28 -0
  228. package/src/components/multi-select/engine.ts +294 -0
  229. package/src/components/multi-select/multi-select.css +266 -0
  230. package/src/components/multi-select/react.tsx +39 -0
  231. package/src/components/notification-bell/README.md +14 -0
  232. package/src/components/notification-bell/element.ts +48 -0
  233. package/src/components/notification-bell/engine.ts +202 -0
  234. package/src/components/notification-bell/notification-bell.css +126 -0
  235. package/src/components/notification-bell/react.tsx +57 -0
  236. package/src/components/password-strength/README.md +17 -0
  237. package/src/components/password-strength/element.ts +22 -0
  238. package/src/components/password-strength/engine.ts +212 -0
  239. package/src/components/password-strength/password-strength.css +182 -0
  240. package/src/components/password-strength/react.tsx +36 -0
  241. package/src/components/password-strength/score.ts +62 -0
  242. package/src/components/popover/README.md +44 -0
  243. package/src/components/popover/element.ts +31 -0
  244. package/src/components/popover/engine.ts +114 -0
  245. package/src/components/popover/popover.css +24 -0
  246. package/src/components/popover/react.tsx +50 -0
  247. package/src/components/progress/README.md +17 -0
  248. package/src/components/progress/element.ts +30 -0
  249. package/src/components/progress/engine.ts +173 -0
  250. package/src/components/progress/progress.css +139 -0
  251. package/src/components/progress/react.tsx +35 -0
  252. package/src/components/questionnaire/README.md +32 -0
  253. package/src/components/questionnaire/element.ts +118 -0
  254. package/src/components/questionnaire/engine.ts +255 -0
  255. package/src/components/questionnaire/questionnaire.css +216 -0
  256. package/src/components/questionnaire/react.tsx +140 -0
  257. package/src/components/range-calendar/README.md +58 -0
  258. package/src/components/range-calendar/element.ts +42 -0
  259. package/src/components/range-calendar/engine.ts +390 -0
  260. package/src/components/range-calendar/range-calendar.css +185 -0
  261. package/src/components/range-calendar/react.tsx +56 -0
  262. package/src/components/range-slider/README.md +23 -0
  263. package/src/components/range-slider/element.ts +53 -0
  264. package/src/components/range-slider/engine.ts +316 -0
  265. package/src/components/range-slider/range-slider.css +191 -0
  266. package/src/components/range-slider/react.tsx +66 -0
  267. package/src/components/resizable-panel/README.md +61 -0
  268. package/src/components/resizable-panel/element.ts +36 -0
  269. package/src/components/resizable-panel/engine.ts +551 -0
  270. package/src/components/resizable-panel/react.tsx +74 -0
  271. package/src/components/resizable-panel/resizable-panel.css +242 -0
  272. package/src/components/rich-text-editor/README.md +57 -0
  273. package/src/components/rich-text-editor/element.ts +34 -0
  274. package/src/components/rich-text-editor/engine.ts +673 -0
  275. package/src/components/rich-text-editor/react.tsx +60 -0
  276. package/src/components/rich-text-editor/rich-text-editor.css +283 -0
  277. package/src/components/scratch-card/README.md +15 -0
  278. package/src/components/scratch-card/element.ts +30 -0
  279. package/src/components/scratch-card/engine.ts +257 -0
  280. package/src/components/scratch-card/react.tsx +40 -0
  281. package/src/components/scratch-card/scratch-card.css +74 -0
  282. package/src/components/skeleton/README.md +15 -0
  283. package/src/components/skeleton/element.ts +32 -0
  284. package/src/components/skeleton/engine.ts +104 -0
  285. package/src/components/skeleton/react.tsx +41 -0
  286. package/src/components/skeleton/skeleton.css +107 -0
  287. package/src/components/split-view/README.md +50 -0
  288. package/src/components/split-view/element.ts +34 -0
  289. package/src/components/split-view/engine.ts +114 -0
  290. package/src/components/split-view/react.tsx +63 -0
  291. package/src/components/split-view/split-view.css +18 -0
  292. package/src/components/star-rating/README.md +14 -0
  293. package/src/components/star-rating/element.ts +26 -0
  294. package/src/components/star-rating/engine.ts +217 -0
  295. package/src/components/star-rating/react.tsx +33 -0
  296. package/src/components/star-rating/star-rating.css +79 -0
  297. package/src/components/stat-card/README.md +42 -0
  298. package/src/components/stat-card/element.ts +40 -0
  299. package/src/components/stat-card/engine.ts +283 -0
  300. package/src/components/stat-card/react.tsx +36 -0
  301. package/src/components/stat-card/stat-card.css +163 -0
  302. package/src/components/swipe-actions/README.md +19 -0
  303. package/src/components/swipe-actions/element.ts +26 -0
  304. package/src/components/swipe-actions/engine.ts +260 -0
  305. package/src/components/swipe-actions/react.tsx +80 -0
  306. package/src/components/swipe-actions/swipe-actions.css +104 -0
  307. package/src/components/tag-input/README.md +42 -0
  308. package/src/components/tag-input/element.ts +39 -0
  309. package/src/components/tag-input/engine.ts +313 -0
  310. package/src/components/tag-input/react.tsx +54 -0
  311. package/src/components/tag-input/tag-input.css +243 -0
  312. package/src/components/time-field/README.md +54 -0
  313. package/src/components/time-field/element.ts +38 -0
  314. package/src/components/time-field/engine.ts +110 -0
  315. package/src/components/time-field/react.tsx +45 -0
  316. package/src/components/time-field/time-field.css +87 -0
  317. package/src/components/timeline/README.md +57 -0
  318. package/src/components/timeline/element.ts +31 -0
  319. package/src/components/timeline/engine.ts +300 -0
  320. package/src/components/timeline/react.tsx +59 -0
  321. package/src/components/timeline/timeline.css +218 -0
  322. package/src/components/tooltip/README.md +46 -0
  323. package/src/components/tooltip/element.ts +22 -0
  324. package/src/components/tooltip/engine.ts +288 -0
  325. package/src/components/tooltip/react.tsx +30 -0
  326. package/src/components/tooltip/tooltip.css +78 -0
  327. package/src/components/tree-view/README.md +61 -0
  328. package/src/components/tree-view/element.ts +32 -0
  329. package/src/components/tree-view/engine.ts +569 -0
  330. package/src/components/tree-view/react.tsx +78 -0
  331. package/src/components/tree-view/tree-view.css +209 -0
  332. package/src/components/virtual-list/README.md +81 -0
  333. package/src/components/virtual-list/element.ts +42 -0
  334. package/src/components/virtual-list/engine.ts +678 -0
  335. package/src/components/virtual-list/react.tsx +101 -0
  336. package/src/components/virtual-list/virtual-list.css +194 -0
  337. package/src/core/chips.ts +196 -0
  338. package/src/core/color.ts +304 -0
  339. package/src/core/date-range.ts +67 -0
  340. package/src/core/dates.ts +11 -1
  341. package/src/core/files.ts +107 -0
  342. package/src/core/filter.ts +229 -0
  343. package/src/core/gsap.ts +80 -65
  344. package/src/core/listbox.ts +332 -0
  345. package/src/core/mark.ts +29 -0
  346. package/src/core/position.ts +89 -0
  347. package/src/core/relative-time.ts +77 -0
  348. package/src/core/segment-field.ts +373 -0
  349. package/src/core/segments.ts +153 -0
  350. package/src/core/tree.ts +116 -0
  351. package/src/core/virtual.ts +79 -0
  352. package/src/css/tokens.css +186 -182
  353. package/src/elements/index.ts +147 -0
  354. package/src/index.ts +72 -0
  355. package/src/motion/micro.ts +248 -0
  356. package/src/motion/sheet.ts +88 -65
  357. package/src/motion/surface.ts +9 -0
  358. package/src/react/index.ts +57 -0
  359. package/dist/chunk-IKQ5FODN.js.map +0 -1
  360. package/dist/chunk-J6OCFNGU.js +0 -177
  361. package/dist/chunk-J6OCFNGU.js.map +0 -1
  362. package/dist/chunk-TFNRIZHU.js +0 -3977
  363. package/dist/chunk-TFNRIZHU.js.map +0 -1
  364. package/dist/chunk-XAKC2QY7.js.map +0 -1
  365. package/dist/reveal-jsX-uwUz.d.ts +0 -625
@@ -0,0 +1,26 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createStarRating, type StarRatingInstance } from './engine';
3
+
4
+ /**
5
+ * <mk-star-rating class="mk-star-rating" name="valoracion" value="4" allow-clear></mk-star-rating>
6
+ * readonly with value="4.5" draws a fixed rating. Emits mk:change ({value}).
7
+ */
8
+ export class MkStarRatingElement extends MkElement<StarRatingInstance> {
9
+ static options: Record<string, OptionType> = { max: 'number', value: 'number', name: 'string', label: 'string', labels: 'list', readonly: 'boolean', allowClear: 'boolean', disabled: 'boolean' };
10
+
11
+ protected create(options: Record<string, unknown>) {
12
+ return createStarRating(this, options);
13
+ }
14
+
15
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
16
+ if (previous === next || !this.instance) return;
17
+ if (name === 'value') this.instance.setValue(Number(next) || 0);
18
+ else super.attributeChangedCallback(name, previous, next);
19
+ }
20
+
21
+ get value() {
22
+ return this.instance?.value ?? 0;
23
+ }
24
+ }
25
+
26
+ export const defineStarRating = () => defineElement('mk-star-rating', MkStarRatingElement);
@@ -0,0 +1,217 @@
1
+ import { gsap, ELASTIC, registerMotion } from '../../core/gsap';
2
+ import { reduced } from '../../core/reduced';
3
+ import { numberFormat } from '../../core/dates';
4
+ import { createScope } from '../../core/scope';
5
+ import { inert, type Instance } from '../../core/types';
6
+ import { burst } from '../../motion/micro';
7
+
8
+ /*
9
+ * Star rating on native radios (arrow keys and forms come from the platform).
10
+ * Pointing previews the stars in a 20 ms cascade; choosing one springs it from
11
+ * 1.4 (elastic), throws a small burst and the label ("Excelente") changes with
12
+ * a blur. allowClear lets a click on the chosen star clear it. readonly draws a
13
+ * value (fractions too) as an image with its value as the name.
14
+ */
15
+
16
+ export interface StarRatingOptions {
17
+ max?: number;
18
+ value?: number;
19
+ /** Form name (default 'rating'). */
20
+ name?: string;
21
+ /** Accessible name of the group (default 'Valoración'). */
22
+ label?: string;
23
+ /** One text per star, shown and read for the chosen value. */
24
+ labels?: string[];
25
+ readonly?: boolean;
26
+ allowClear?: boolean;
27
+ disabled?: boolean;
28
+ onChange?: (value: number) => void;
29
+ }
30
+
31
+ export interface StarRatingInstance extends Instance {
32
+ setValue(value: number, notify?: boolean): void;
33
+ readonly value: number;
34
+ }
35
+
36
+ const STAR = 'M12 3.2l2.6 5.4 5.9.8-4.3 4.1 1 5.9L12 16.6l-5.2 2.8 1-5.9-4.3-4.1 5.9-.8z';
37
+ const LABELS = ['Mala', 'Regular', 'Buena', 'Muy buena', 'Excelente'];
38
+ let sequence = 0;
39
+
40
+ const star = () => {
41
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
42
+ svg.setAttribute('viewBox', '0 0 24 24');
43
+ svg.setAttribute('aria-hidden', 'true');
44
+ const back = document.createElementNS('http://www.w3.org/2000/svg', 'path');
45
+ back.setAttribute('d', STAR);
46
+ back.dataset.mkPart = 'empty';
47
+ const front = document.createElementNS('http://www.w3.org/2000/svg', 'path');
48
+ front.setAttribute('d', STAR);
49
+ front.dataset.mkPart = 'full';
50
+ svg.append(back, front);
51
+ return svg;
52
+ };
53
+
54
+ export function createStarRating(root: HTMLElement | null, options: StarRatingOptions = {}): StarRatingInstance {
55
+ if (!root) return inert({ setValue() {}, value: 0 });
56
+ registerMotion();
57
+ const scope = createScope();
58
+ const max = Math.max(1, Math.min(10, Math.round(Number.isFinite(options.max) ? options.max! : 5)));
59
+ const texts = options.labels ?? (max === 5 ? LABELS : []);
60
+ const clamp = (n: number) => Math.max(0, Math.min(max, n));
61
+ let value = clamp(Number.isFinite(options.value) ? options.value! : 0);
62
+ const name = options.name ?? `rating-${++sequence}`;
63
+
64
+ const group = document.createElement('div');
65
+ group.dataset.mkStars = '';
66
+ const caption = document.createElement('span');
67
+ caption.dataset.mkStarLabel = '';
68
+ root.append(group, caption);
69
+ scope.add(() => {
70
+ group.remove();
71
+ caption.remove();
72
+ });
73
+ scope.attr(root, 'data-mk-ready', '');
74
+ scope.attr(root, 'style', root.getAttribute('style'));
75
+
76
+ const stars: HTMLElement[] = [];
77
+ const inputs: HTMLInputElement[] = [];
78
+ if (options.readonly) {
79
+ group.setAttribute('role', 'img');
80
+ for (let i = 1; i <= max; i++) {
81
+ const item = document.createElement('span');
82
+ item.dataset.mkStar = String(i);
83
+ item.append(star());
84
+ group.append(item);
85
+ stars.push(item);
86
+ }
87
+ } else {
88
+ group.setAttribute('role', 'radiogroup');
89
+ group.setAttribute('aria-label', options.label ?? 'Valoración');
90
+ for (let i = 1; i <= max; i++) {
91
+ const item = document.createElement('label');
92
+ item.dataset.mkStar = String(i);
93
+ const input = document.createElement('input');
94
+ input.type = 'radio';
95
+ input.name = name;
96
+ input.value = String(i);
97
+ input.className = 'mk-sr-only';
98
+ input.disabled = Boolean(options.disabled);
99
+ input.setAttribute('aria-label', texts[i - 1] ? `${i} de ${max}: ${texts[i - 1]}` : `${i} de ${max}`);
100
+ item.append(input, star());
101
+ group.append(item);
102
+ stars.push(item);
103
+ inputs.push(input);
104
+ }
105
+ }
106
+
107
+ const effects = new Set<gsap.core.Animation>();
108
+ const keep = (animation: gsap.core.Animation | null) => {
109
+ if (!animation) return;
110
+ effects.add(animation);
111
+ // Finished ones leave as new ones arrive: the animation's own onComplete stays untouched.
112
+ for (const effect of effects) if (effect.totalProgress() === 1) effects.delete(effect);
113
+ };
114
+ scope.add(() => {
115
+ for (const effect of effects) effect.kill();
116
+ effects.clear();
117
+ });
118
+
119
+ const svgs = () => stars.map((item) => item.querySelector('svg')!);
120
+ /** Light the stars up to `upto` (fractions fill the last one partly). */
121
+ const light = (upto: number) => {
122
+ stars.forEach((item, i) => {
123
+ const fill = Math.max(0, Math.min(1, upto - i));
124
+ item.style.setProperty('--mk-star-fill', `${(fill * 100).toFixed(1)}%`);
125
+ item.toggleAttribute('data-mk-on', fill >= 1);
126
+ });
127
+ };
128
+ const describe = () => {
129
+ const text = value ? texts[Math.ceil(value) - 1] ?? '' : '';
130
+ if (options.readonly) {
131
+ const shown = numberFormat(root, { maximumFractionDigits: 1 }).format(value);
132
+ group.setAttribute('aria-label', `${options.label ?? 'Valoración'}: ${shown} de ${max}`);
133
+ caption.textContent = shown;
134
+ return;
135
+ }
136
+ if (caption.textContent === text) return;
137
+ if (reduced() || !caption.textContent) {
138
+ caption.textContent = text;
139
+ return;
140
+ }
141
+ gsap.killTweensOf(caption);
142
+ keep(
143
+ gsap
144
+ .timeline()
145
+ .to(caption, { opacity: 0, filter: 'blur(4px)', y: -3, duration: 0.12 })
146
+ .call(() => {
147
+ caption.textContent = text;
148
+ })
149
+ .fromTo(caption, { opacity: 0, filter: 'blur(4px)', y: 3 }, { opacity: 1, filter: 'blur(0px)', y: 0, duration: 0.25, clearProps: 'all' }),
150
+ );
151
+ };
152
+ const paint = () => {
153
+ inputs.forEach((input, i) => (input.checked = i + 1 === value));
154
+ light(value);
155
+ describe();
156
+ };
157
+
158
+ const choose = (next: number, notify: boolean, animate: boolean) => {
159
+ if (scope.disposed) return;
160
+ const target = clamp(Math.round(next));
161
+ if (target === value) return;
162
+ value = target;
163
+ paint();
164
+ if (animate && target && !reduced()) {
165
+ const item = stars[target - 1]!;
166
+ keep(gsap.fromTo(item.querySelector('svg'), { scale: 1.45, rotation: -12 }, { scale: 1, rotation: 0, duration: 0.8, ease: ELASTIC, clearProps: 'all' }));
167
+ keep(gsap.fromTo(svgs().slice(0, target - 1), { scale: 0.8 }, { scale: 1, duration: 0.4, ease: 'back.out(3)', stagger: 0.03, clearProps: 'all' }));
168
+ keep(burst(item, { count: 6, distance: 22, size: 5, colors: ['var(--mk-star-color)'] }));
169
+ }
170
+ if (!notify) return;
171
+ options.onChange?.(value);
172
+ root.dispatchEvent(new CustomEvent('mk:change', { bubbles: true, detail: { value } }));
173
+ };
174
+
175
+ if (!options.readonly) {
176
+ let preview = 0;
177
+ scope.on(group, 'pointerover', (event) => {
178
+ const item = (event.target as Element | null)?.closest<HTMLElement>('[data-mk-star]');
179
+ if (!item || options.disabled) return;
180
+ const upto = Number(item.dataset.mkStar);
181
+ if (upto === preview) return;
182
+ const from = preview;
183
+ preview = upto;
184
+ scope.attr(root, 'data-mk-preview', String(upto));
185
+ light(upto);
186
+ if (!reduced() && upto > from) keep(gsap.fromTo(svgs().slice(from, upto), { scale: 0.85 }, { scale: 1, duration: 0.3, ease: 'back.out(3)', stagger: 0.02, clearProps: 'all' }));
187
+ });
188
+ scope.on(group, 'pointerleave', () => {
189
+ preview = 0;
190
+ scope.attr(root, 'data-mk-preview', null);
191
+ light(value);
192
+ });
193
+ // A click on the chosen star (allowClear) clears it: radios cannot uncheck themselves.
194
+ let before = 0;
195
+ scope.on(group, 'pointerdown', () => {
196
+ before = value;
197
+ });
198
+ scope.on(group, 'click', (event) => {
199
+ const input = (event.target as Element | null)?.closest('input');
200
+ if (!input || !options.allowClear) return;
201
+ if (Number(input.value) === before && before === value) {
202
+ before = -1;
203
+ choose(0, true, false);
204
+ }
205
+ });
206
+ for (const input of inputs) scope.on(input, 'change', () => choose(Number(input.value), true, true));
207
+ }
208
+ paint();
209
+
210
+ return {
211
+ setValue: (next, notify = false) => choose(next, notify, !notify),
212
+ get value() {
213
+ return value;
214
+ },
215
+ destroy: () => scope.dispose(),
216
+ };
217
+ }
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+ import { useRef, type HTMLAttributes } from 'react';
3
+ import { useIsomorphicLayoutEffect, useLatest } from '../../react/use-recipe';
4
+ import { createStarRating, type StarRatingInstance, type StarRatingOptions } from './engine';
5
+
6
+ export interface StarRatingProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'>, Omit<StarRatingOptions, 'value'> {
7
+ /** Controlled value; without it the stars keep their own (defaultValue). */
8
+ value?: number;
9
+ defaultValue?: number;
10
+ }
11
+
12
+ /** <StarRating value={stars} onChange={setStars} allowClear /> or <StarRating readonly value={4.5} />. */
13
+ export function StarRating({ value, defaultValue, max, name, label, labels, readonly, allowClear, disabled, onChange, className, ...props }: StarRatingProps) {
14
+ const ref = useRef<HTMLDivElement>(null);
15
+ const instance = useRef<StarRatingInstance | null>(null);
16
+ const change = useLatest(onChange);
17
+ const start = useRef(value ?? defaultValue ?? 0);
18
+ const key = JSON.stringify(labels ?? null);
19
+ useIsomorphicLayoutEffect(() => {
20
+ const created = createStarRating(ref.current, { value: value ?? start.current, max, name, label, labels, readonly, allowClear, disabled, onChange: (next) => change.current?.(next) });
21
+ instance.current = created;
22
+ return () => {
23
+ start.current = created.value;
24
+ created.destroy();
25
+ instance.current = null;
26
+ };
27
+ // eslint-disable-next-line react-hooks/exhaustive-deps
28
+ }, [max, name, label, key, readonly, allowClear, disabled]);
29
+ useIsomorphicLayoutEffect(() => {
30
+ if (value !== undefined && instance.current && instance.current.value !== value) instance.current.setValue(value);
31
+ }, [value]);
32
+ return <div {...props} ref={ref} className={['mk-star-rating', className].filter(Boolean).join(' ')} />;
33
+ }
@@ -0,0 +1,79 @@
1
+ /* star-rating — stars outlined in the surface tone, filled with the star colour; a label beside them.
2
+ Hover previews; the focus ring sits on the star of the focused radio. */
3
+ .mk-star-rating,
4
+ mk-star-rating {
5
+ --mk-star-size: 2rem;
6
+ --mk-star-color: #f5b301;
7
+ --mk-star-empty: var(--mk-surface-4);
8
+ display: inline-flex;
9
+ align-items: center;
10
+ gap: 0.75rem;
11
+ color: var(--mk-text);
12
+ font-family: var(--mk-font);
13
+ }
14
+
15
+ :is(.mk-star-rating, mk-star-rating) [data-mk-stars] {
16
+ display: inline-flex;
17
+ gap: 0.15rem;
18
+ }
19
+
20
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star] {
21
+ position: relative;
22
+ display: grid;
23
+ place-items: center;
24
+ width: calc(var(--mk-star-size) + 0.5rem);
25
+ height: calc(var(--mk-star-size) + 0.5rem);
26
+ border-radius: 50%;
27
+ cursor: pointer;
28
+ }
29
+
30
+ :is(.mk-star-rating, mk-star-rating) [role='img'] [data-mk-star] {
31
+ width: var(--mk-star-size);
32
+ height: var(--mk-star-size);
33
+ cursor: default;
34
+ }
35
+
36
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star] svg {
37
+ width: var(--mk-star-size);
38
+ height: var(--mk-star-size);
39
+ overflow: visible;
40
+ }
41
+
42
+ :is(.mk-star-rating, mk-star-rating) [data-mk-part='empty'] {
43
+ fill: var(--mk-star-empty);
44
+ }
45
+
46
+ :is(.mk-star-rating, mk-star-rating) [data-mk-part='full'] {
47
+ fill: var(--mk-star-color);
48
+ clip-path: inset(0 calc(100% - var(--mk-star-fill, 0%)) 0 0);
49
+ transition: clip-path 0.15s ease;
50
+ }
51
+
52
+ /* While previewing, lit stars are a step lighter. */
53
+ :is(.mk-star-rating, mk-star-rating)[data-mk-preview] [data-mk-part='full'] {
54
+ fill: color-mix(in srgb, var(--mk-star-color) 75%, white);
55
+ }
56
+
57
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star]:has(input:focus-visible) {
58
+ outline: 2px solid var(--mk-accent);
59
+ outline-offset: 1px;
60
+ }
61
+
62
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star]:has(input:disabled) {
63
+ cursor: not-allowed;
64
+ opacity: 0.5;
65
+ }
66
+
67
+ :is(.mk-star-rating, mk-star-rating) [data-mk-star-label] {
68
+ min-width: 5.5rem;
69
+ color: var(--mk-muted);
70
+ font-size: 0.9375rem;
71
+ font-weight: 500;
72
+ font-variant-numeric: tabular-nums;
73
+ }
74
+
75
+ @media (prefers-reduced-motion: reduce) {
76
+ :is(.mk-star-rating, mk-star-rating) [data-mk-part='full'] {
77
+ transition: none;
78
+ }
79
+ }
@@ -0,0 +1,42 @@
1
+ # stat-card
2
+
3
+ Tarjeta de KPI con su tendencia. Cuando entra en pantalla, la cifra cuenta hasta su valor (.9 s) con el formato del idioma de la página (moneda, porcentaje, compacto). La variación respecto a `previous` va en una insignia (▲ o ▼ y el porcentaje): verde cuando va en la buena dirección (`positive` dice cuál), roja cuando no, neutra si no cambia. Aparece con un pop mientras la flecha da un empujón. Una minigráfica de `series` se dibuja sola (.9 s) con un área suave y un punto final que rebota; `target` llena una barra de meta.
4
+
5
+ `update()` cuenta desde la cifra mostrada hasta la nueva, hace rodar los dígitos de la insignia, gira la flecha si cambia la tendencia y transforma la minigráfica hacia los puntos nuevos.
6
+
7
+ ## HTML / Astro
8
+
9
+ ```html
10
+ <mk-stat-card class="mk-stat-card" label="Ingresos" value="128400" previous="118900"
11
+ format='{"style":"currency","currency":"MXN","maximumFractionDigits":0}'
12
+ series="90,98,104,101,118,128" target="150000"></mk-stat-card>
13
+ ```
14
+
15
+ ## React
16
+
17
+ ```tsx
18
+ <StatCard label="Cancelaciones" value={42} previous={51} positive="down" comparison="vs. mes pasado" series={trend} />
19
+ ```
20
+
21
+ ## Opciones
22
+
23
+ | Opción | Atributo | Por defecto | Qué hace |
24
+ |---|---|---|---|
25
+ | `label` | `label` | — | Nombre del indicador. |
26
+ | `value` | `value` | — | Cifra actual. |
27
+ | `previous` | `previous` | — | Cifra con la que se compara. |
28
+ | `format` | `format` (JSON) | 1 decimal | Opciones de `Intl.NumberFormat` o una función. |
29
+ | `locale` | `locale` | `lang` de la página | Idioma de los números. |
30
+ | `positive` | `positive` | `'up'` | Qué dirección es buena (`down` para costos, bajas, latencia). |
31
+ | `comparison` | `comparison` | `'vs. periodo anterior'` | Con qué se compara. |
32
+ | `series` | `series` (lista con comas) | — | Valores de la minigráfica (dos o más). |
33
+ | `target` | `target` | — | Meta: una barra muestra el avance. |
34
+ | `countOnView` | `count-on-view` | `true` | Contar al entrar en pantalla; `false` la muestra al momento. |
35
+
36
+ ## API
37
+
38
+ `update({ value, previous, series, target, … })`, `destroy()`.
39
+
40
+ ## Accesibilidad y estilo
41
+
42
+ La cifra visible es `aria-hidden`; una frase con los valores finales habla por ella («Ingresos: $128,400. Sube 8,0 % vs. periodo anterior.»). Tarjeta de 20 px sobre `--mk-surface-1`. Variables: `--mk-stat-good`, `--mk-stat-bad`, `--mk-stat-line`. Con movimiento reducido todo aparece en su valor final.
@@ -0,0 +1,40 @@
1
+ import { MkElement, defineElement, type OptionType } from '../../elements/base';
2
+ import { createStatCard, type StatCardInstance } from './engine';
3
+
4
+ /**
5
+ * <mk-stat-card class="mk-stat-card" label="Ingresos" value="128400" previous="118900"
6
+ * format='{"style":"currency","currency":"MXN","maximumFractionDigits":0}' series="90,98,104,101,118,128"></mk-stat-card>
7
+ * Changing value, previous, series or target updates it in place (the figure counts).
8
+ */
9
+ export class MkStatCardElement extends MkElement<StatCardInstance> {
10
+ static options: Record<string, OptionType> = {
11
+ label: 'string',
12
+ value: 'number',
13
+ previous: 'number',
14
+ format: 'json',
15
+ locale: 'string',
16
+ positive: 'string',
17
+ comparison: 'string',
18
+ series: 'list',
19
+ target: 'number',
20
+ countOnView: 'boolean',
21
+ };
22
+
23
+ protected create(options: Record<string, unknown>) {
24
+ return createStatCard(this, normalise(options));
25
+ }
26
+
27
+ attributeChangedCallback(name: string, previous: string | null, next: string | null) {
28
+ if (previous === next || !this.instance) return;
29
+ if (name === 'locale' || name === 'count-on-view') return super.attributeChangedCallback(name, previous, next);
30
+ this.instance.update(normalise(this.options));
31
+ }
32
+ }
33
+
34
+ /** The series attribute is a comma list: numbers. */
35
+ const normalise = (options: Record<string, unknown>) => ({
36
+ ...options,
37
+ series: Array.isArray(options.series) ? (options.series as string[]).map(Number).filter(Number.isFinite) : undefined,
38
+ });
39
+
40
+ export const defineStatCard = () => defineElement('mk-stat-card', MkStatCardElement);