@moni-labs/moni-ui 0.2.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 (451) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +264 -0
  3. package/custom-elements.json +12510 -0
  4. package/dist/actions/index.d.ts +2 -0
  5. package/dist/actions/index.d.ts.map +1 -0
  6. package/dist/actions/index.js +1 -0
  7. package/dist/assets/arch.svg +1 -0
  8. package/dist/assets/arrow.svg +1 -0
  9. package/dist/assets/boom.svg +1 -0
  10. package/dist/assets/bun.svg +1 -0
  11. package/dist/assets/burst.svg +1 -0
  12. package/dist/assets/circle.svg +1 -0
  13. package/dist/assets/clamshell.svg +1 -0
  14. package/dist/assets/diamond.svg +1 -0
  15. package/dist/assets/fan.svg +1 -0
  16. package/dist/assets/flower.svg +1 -0
  17. package/dist/assets/gem.svg +1 -0
  18. package/dist/assets/ghost-ish.svg +1 -0
  19. package/dist/assets/heart.svg +1 -0
  20. package/dist/assets/leaf-clover4.svg +1 -0
  21. package/dist/assets/leaf-clover8.svg +1 -0
  22. package/dist/assets/loading-indicator.svg +1 -0
  23. package/dist/assets/material-symbols-rounded.woff2 +0 -0
  24. package/dist/assets/material-symbols-subset.woff2 +0 -0
  25. package/dist/assets/oval.svg +1 -0
  26. package/dist/assets/pentagon.svg +1 -0
  27. package/dist/assets/pill.svg +1 -0
  28. package/dist/assets/pixel-circle.svg +1 -0
  29. package/dist/assets/pixel-triangle.svg +1 -0
  30. package/dist/assets/puffy-diamond.svg +1 -0
  31. package/dist/assets/puffy.svg +1 -0
  32. package/dist/assets/semicircle.svg +1 -0
  33. package/dist/assets/shapes/arch.svg +1 -0
  34. package/dist/assets/shapes/arrow.svg +1 -0
  35. package/dist/assets/shapes/boom.svg +1 -0
  36. package/dist/assets/shapes/burst.svg +1 -0
  37. package/dist/assets/shapes/circle.svg +1 -0
  38. package/dist/assets/shapes/clamshell.svg +1 -0
  39. package/dist/assets/shapes/diamond.svg +1 -0
  40. package/dist/assets/shapes/fan.svg +1 -0
  41. package/dist/assets/shapes/flower.svg +1 -0
  42. package/dist/assets/shapes/gem.svg +1 -0
  43. package/dist/assets/shapes/ghost-ish.svg +1 -0
  44. package/dist/assets/shapes/heart.svg +1 -0
  45. package/dist/assets/shapes/leaf-clover4.svg +1 -0
  46. package/dist/assets/shapes/leaf-clover8.svg +1 -0
  47. package/dist/assets/shapes/loading-indicator.svg +1 -0
  48. package/dist/assets/shapes/oval.svg +1 -0
  49. package/dist/assets/shapes/pentagon.svg +1 -0
  50. package/dist/assets/shapes/pill.svg +1 -0
  51. package/dist/assets/shapes/pixel-circle.svg +1 -0
  52. package/dist/assets/shapes/pixel-triangle.svg +1 -0
  53. package/dist/assets/shapes/puffy-diamond.svg +1 -0
  54. package/dist/assets/shapes/puffy.svg +1 -0
  55. package/dist/assets/shapes/semicircle.svg +1 -0
  56. package/dist/assets/shapes/sided-cookie12.svg +1 -0
  57. package/dist/assets/shapes/sided-cookie4.svg +1 -0
  58. package/dist/assets/shapes/sided-cookie6.svg +1 -0
  59. package/dist/assets/shapes/sided-cookie7.svg +1 -0
  60. package/dist/assets/shapes/sided-cookie9.svg +1 -0
  61. package/dist/assets/shapes/slanted.svg +1 -0
  62. package/dist/assets/shapes/soft-boom.svg +1 -0
  63. package/dist/assets/shapes/soft-burst.svg +1 -0
  64. package/dist/assets/shapes/square.svg +1 -0
  65. package/dist/assets/shapes/sunny.svg +1 -0
  66. package/dist/assets/shapes/triangle.svg +1 -0
  67. package/dist/assets/shapes/very-sunny.svg +1 -0
  68. package/dist/assets/shapes/wavy-circle.svg +1 -0
  69. package/dist/assets/shapes/wavy.svg +1 -0
  70. package/dist/assets/sided-cookie12.svg +1 -0
  71. package/dist/assets/sided-cookie4.svg +1 -0
  72. package/dist/assets/sided-cookie6.svg +1 -0
  73. package/dist/assets/sided-cookie7.svg +1 -0
  74. package/dist/assets/sided-cookie9.svg +1 -0
  75. package/dist/assets/slanted.svg +1 -0
  76. package/dist/assets/soft-boom.svg +1 -0
  77. package/dist/assets/soft-burst.svg +1 -0
  78. package/dist/assets/square.svg +1 -0
  79. package/dist/assets/star.svg +1 -0
  80. package/dist/assets/sunny.svg +1 -0
  81. package/dist/assets/triangle.svg +1 -0
  82. package/dist/assets/very-sunny.svg +1 -0
  83. package/dist/assets/wavy-circle.svg +1 -0
  84. package/dist/assets/wavy.svg +1 -0
  85. package/dist/components/_base/field-styles.d.ts +24 -0
  86. package/dist/components/_base/field-styles.d.ts.map +1 -0
  87. package/dist/components/_base/field-styles.js +504 -0
  88. package/dist/components/_base/index.d.ts +4 -0
  89. package/dist/components/_base/index.d.ts.map +1 -0
  90. package/dist/components/_base/index.js +3 -0
  91. package/dist/components/_base/interaction-styles.d.ts +22 -0
  92. package/dist/components/_base/interaction-styles.d.ts.map +1 -0
  93. package/dist/components/_base/interaction-styles.js +123 -0
  94. package/dist/components/_base/moni-element.d.ts +18 -0
  95. package/dist/components/_base/moni-element.d.ts.map +1 -0
  96. package/dist/components/_base/moni-element.js +19 -0
  97. package/dist/components/_base/shared-styles.d.ts +22 -0
  98. package/dist/components/_base/shared-styles.d.ts.map +1 -0
  99. package/dist/components/_base/shared-styles.js +146 -0
  100. package/dist/components/index.d.ts +42 -0
  101. package/dist/components/index.d.ts.map +1 -0
  102. package/dist/components/index.js +43 -0
  103. package/dist/components/loading-shapes.d.ts +10 -0
  104. package/dist/components/loading-shapes.d.ts.map +1 -0
  105. package/dist/components/loading-shapes.js +9 -0
  106. package/dist/components/moni-app-bar.d.ts +52 -0
  107. package/dist/components/moni-app-bar.d.ts.map +1 -0
  108. package/dist/components/moni-app-bar.js +207 -0
  109. package/dist/components/moni-badge.d.ts +37 -0
  110. package/dist/components/moni-badge.d.ts.map +1 -0
  111. package/dist/components/moni-badge.js +153 -0
  112. package/dist/components/moni-bottom-sheet.d.ts +52 -0
  113. package/dist/components/moni-bottom-sheet.d.ts.map +1 -0
  114. package/dist/components/moni-bottom-sheet.js +440 -0
  115. package/dist/components/moni-button-group.d.ts +55 -0
  116. package/dist/components/moni-button-group.d.ts.map +1 -0
  117. package/dist/components/moni-button-group.js +278 -0
  118. package/dist/components/moni-button-segment.d.ts +29 -0
  119. package/dist/components/moni-button-segment.d.ts.map +1 -0
  120. package/dist/components/moni-button-segment.js +300 -0
  121. package/dist/components/moni-button.d.ts +70 -0
  122. package/dist/components/moni-button.d.ts.map +1 -0
  123. package/dist/components/moni-button.js +727 -0
  124. package/dist/components/moni-card.d.ts +48 -0
  125. package/dist/components/moni-card.d.ts.map +1 -0
  126. package/dist/components/moni-card.js +216 -0
  127. package/dist/components/moni-carousel.d.ts +111 -0
  128. package/dist/components/moni-carousel.d.ts.map +1 -0
  129. package/dist/components/moni-carousel.js +1007 -0
  130. package/dist/components/moni-checkbox.d.ts +39 -0
  131. package/dist/components/moni-checkbox.d.ts.map +1 -0
  132. package/dist/components/moni-checkbox.js +212 -0
  133. package/dist/components/moni-chip.d.ts +57 -0
  134. package/dist/components/moni-chip.d.ts.map +1 -0
  135. package/dist/components/moni-chip.js +340 -0
  136. package/dist/components/moni-color-field.d.ts +33 -0
  137. package/dist/components/moni-color-field.d.ts.map +1 -0
  138. package/dist/components/moni-color-field.js +170 -0
  139. package/dist/components/moni-context-menu.d.ts +55 -0
  140. package/dist/components/moni-context-menu.d.ts.map +1 -0
  141. package/dist/components/moni-context-menu.js +184 -0
  142. package/dist/components/moni-dialog.d.ts +37 -0
  143. package/dist/components/moni-dialog.d.ts.map +1 -0
  144. package/dist/components/moni-dialog.js +257 -0
  145. package/dist/components/moni-divider.d.ts +33 -0
  146. package/dist/components/moni-divider.d.ts.map +1 -0
  147. package/dist/components/moni-divider.js +81 -0
  148. package/dist/components/moni-expansion.d.ts +25 -0
  149. package/dist/components/moni-expansion.d.ts.map +1 -0
  150. package/dist/components/moni-expansion.js +94 -0
  151. package/dist/components/moni-fab-menu.d.ts +58 -0
  152. package/dist/components/moni-fab-menu.d.ts.map +1 -0
  153. package/dist/components/moni-fab-menu.js +247 -0
  154. package/dist/components/moni-fab.d.ts +48 -0
  155. package/dist/components/moni-fab.d.ts.map +1 -0
  156. package/dist/components/moni-fab.js +284 -0
  157. package/dist/components/moni-file-field.d.ts +47 -0
  158. package/dist/components/moni-file-field.d.ts.map +1 -0
  159. package/dist/components/moni-file-field.js +189 -0
  160. package/dist/components/moni-icon.d.ts +30 -0
  161. package/dist/components/moni-icon.d.ts.map +1 -0
  162. package/dist/components/moni-icon.js +107 -0
  163. package/dist/components/moni-list-item.d.ts +51 -0
  164. package/dist/components/moni-list-item.d.ts.map +1 -0
  165. package/dist/components/moni-list-item.js +239 -0
  166. package/dist/components/moni-list.d.ts +32 -0
  167. package/dist/components/moni-list.d.ts.map +1 -0
  168. package/dist/components/moni-list.js +67 -0
  169. package/dist/components/moni-loading-indicator.d.ts +32 -0
  170. package/dist/components/moni-loading-indicator.d.ts.map +1 -0
  171. package/dist/components/moni-loading-indicator.js +189 -0
  172. package/dist/components/moni-menu-item.d.ts +27 -0
  173. package/dist/components/moni-menu-item.d.ts.map +1 -0
  174. package/dist/components/moni-menu-item.js +99 -0
  175. package/dist/components/moni-menu.d.ts +55 -0
  176. package/dist/components/moni-menu.d.ts.map +1 -0
  177. package/dist/components/moni-menu.js +295 -0
  178. package/dist/components/moni-morph-modal.d.ts +78 -0
  179. package/dist/components/moni-morph-modal.d.ts.map +1 -0
  180. package/dist/components/moni-morph-modal.js +1223 -0
  181. package/dist/components/moni-nav-item.d.ts +38 -0
  182. package/dist/components/moni-nav-item.d.ts.map +1 -0
  183. package/dist/components/moni-nav-item.js +262 -0
  184. package/dist/components/moni-nav.d.ts +46 -0
  185. package/dist/components/moni-nav.d.ts.map +1 -0
  186. package/dist/components/moni-nav.js +272 -0
  187. package/dist/components/moni-progress.d.ts +45 -0
  188. package/dist/components/moni-progress.d.ts.map +1 -0
  189. package/dist/components/moni-progress.js +333 -0
  190. package/dist/components/moni-radio.d.ts +38 -0
  191. package/dist/components/moni-radio.d.ts.map +1 -0
  192. package/dist/components/moni-radio.js +218 -0
  193. package/dist/components/moni-ripple.d.ts +35 -0
  194. package/dist/components/moni-ripple.d.ts.map +1 -0
  195. package/dist/components/moni-ripple.js +156 -0
  196. package/dist/components/moni-segmented-button.d.ts +52 -0
  197. package/dist/components/moni-segmented-button.d.ts.map +1 -0
  198. package/dist/components/moni-segmented-button.js +208 -0
  199. package/dist/components/moni-select-option.d.ts +27 -0
  200. package/dist/components/moni-select-option.d.ts.map +1 -0
  201. package/dist/components/moni-select-option.js +102 -0
  202. package/dist/components/moni-select.d.ts +76 -0
  203. package/dist/components/moni-select.d.ts.map +1 -0
  204. package/dist/components/moni-select.js +1136 -0
  205. package/dist/components/moni-shape.d.ts +30 -0
  206. package/dist/components/moni-shape.d.ts.map +1 -0
  207. package/dist/components/moni-shape.js +146 -0
  208. package/dist/components/moni-side-sheet.d.ts +62 -0
  209. package/dist/components/moni-side-sheet.d.ts.map +1 -0
  210. package/dist/components/moni-side-sheet.js +576 -0
  211. package/dist/components/moni-slider.d.ts +73 -0
  212. package/dist/components/moni-slider.d.ts.map +1 -0
  213. package/dist/components/moni-slider.js +422 -0
  214. package/dist/components/moni-snackbar.d.ts +40 -0
  215. package/dist/components/moni-snackbar.d.ts.map +1 -0
  216. package/dist/components/moni-snackbar.js +161 -0
  217. package/dist/components/moni-split-button.d.ts +33 -0
  218. package/dist/components/moni-split-button.d.ts.map +1 -0
  219. package/dist/components/moni-split-button.js +122 -0
  220. package/dist/components/moni-step.d.ts +30 -0
  221. package/dist/components/moni-step.d.ts.map +1 -0
  222. package/dist/components/moni-step.js +175 -0
  223. package/dist/components/moni-stepper.d.ts +35 -0
  224. package/dist/components/moni-stepper.d.ts.map +1 -0
  225. package/dist/components/moni-stepper.js +101 -0
  226. package/dist/components/moni-switch.d.ts +39 -0
  227. package/dist/components/moni-switch.d.ts.map +1 -0
  228. package/dist/components/moni-switch.js +258 -0
  229. package/dist/components/moni-tab.d.ts +27 -0
  230. package/dist/components/moni-tab.d.ts.map +1 -0
  231. package/dist/components/moni-tab.js +147 -0
  232. package/dist/components/moni-tabs.d.ts +31 -0
  233. package/dist/components/moni-tabs.d.ts.map +1 -0
  234. package/dist/components/moni-tabs.js +106 -0
  235. package/dist/components/moni-text-field.d.ts +46 -0
  236. package/dist/components/moni-text-field.d.ts.map +1 -0
  237. package/dist/components/moni-text-field.js +190 -0
  238. package/dist/components/moni-textarea.d.ts +57 -0
  239. package/dist/components/moni-textarea.d.ts.map +1 -0
  240. package/dist/components/moni-textarea.js +228 -0
  241. package/dist/components/moni-time-picker.d.ts +51 -0
  242. package/dist/components/moni-time-picker.d.ts.map +1 -0
  243. package/dist/components/moni-time-picker.js +823 -0
  244. package/dist/components/moni-toolbar.d.ts +35 -0
  245. package/dist/components/moni-toolbar.d.ts.map +1 -0
  246. package/dist/components/moni-toolbar.js +128 -0
  247. package/dist/components/moni-tooltip.d.ts +65 -0
  248. package/dist/components/moni-tooltip.d.ts.map +1 -0
  249. package/dist/components/moni-tooltip.js +320 -0
  250. package/dist/components/moni-typography.d.ts +42 -0
  251. package/dist/components/moni-typography.d.ts.map +1 -0
  252. package/dist/components/moni-typography.js +205 -0
  253. package/dist/index.d.ts +7 -0
  254. package/dist/index.d.ts.map +1 -0
  255. package/dist/index.js +7 -0
  256. package/dist/styles/animations.css +46 -0
  257. package/dist/styles/base.css +248 -0
  258. package/dist/styles/index.css +3 -0
  259. package/dist/styles/tokens.css +190 -0
  260. package/dist/utils/color.d.ts +51 -0
  261. package/dist/utils/color.d.ts.map +1 -0
  262. package/dist/utils/color.js +107 -0
  263. package/dist/utils/theme.svelte.d.ts +45 -0
  264. package/dist/utils/theme.svelte.d.ts.map +1 -0
  265. package/dist/utils/theme.svelte.js +168 -0
  266. package/dist/web-components.d.ts +3 -0
  267. package/dist/web-components.d.ts.map +1 -0
  268. package/dist/web-components.js +4 -0
  269. package/package.json +76 -0
  270. package/src/actions/index.ts +2 -0
  271. package/src/assets/arch.svg +1 -0
  272. package/src/assets/arrow.svg +1 -0
  273. package/src/assets/boom.svg +1 -0
  274. package/src/assets/bun.svg +1 -0
  275. package/src/assets/burst.svg +1 -0
  276. package/src/assets/circle.svg +1 -0
  277. package/src/assets/clamshell.svg +1 -0
  278. package/src/assets/diamond.svg +1 -0
  279. package/src/assets/fan.svg +1 -0
  280. package/src/assets/flower.svg +1 -0
  281. package/src/assets/gem.svg +1 -0
  282. package/src/assets/ghost-ish.svg +1 -0
  283. package/src/assets/heart.svg +1 -0
  284. package/src/assets/leaf-clover4.svg +1 -0
  285. package/src/assets/leaf-clover8.svg +1 -0
  286. package/src/assets/loading-indicator.svg +1 -0
  287. package/src/assets/material-symbols-rounded.woff2 +0 -0
  288. package/src/assets/material-symbols-subset.woff2 +0 -0
  289. package/src/assets/oval.svg +1 -0
  290. package/src/assets/pentagon.svg +1 -0
  291. package/src/assets/pill.svg +1 -0
  292. package/src/assets/pixel-circle.svg +1 -0
  293. package/src/assets/pixel-triangle.svg +1 -0
  294. package/src/assets/puffy-diamond.svg +1 -0
  295. package/src/assets/puffy.svg +1 -0
  296. package/src/assets/semicircle.svg +1 -0
  297. package/src/assets/shapes/arch.svg +1 -0
  298. package/src/assets/shapes/arrow.svg +1 -0
  299. package/src/assets/shapes/boom.svg +1 -0
  300. package/src/assets/shapes/burst.svg +1 -0
  301. package/src/assets/shapes/circle.svg +1 -0
  302. package/src/assets/shapes/clamshell.svg +1 -0
  303. package/src/assets/shapes/diamond.svg +1 -0
  304. package/src/assets/shapes/fan.svg +1 -0
  305. package/src/assets/shapes/flower.svg +1 -0
  306. package/src/assets/shapes/gem.svg +1 -0
  307. package/src/assets/shapes/ghost-ish.svg +1 -0
  308. package/src/assets/shapes/heart.svg +1 -0
  309. package/src/assets/shapes/leaf-clover4.svg +1 -0
  310. package/src/assets/shapes/leaf-clover8.svg +1 -0
  311. package/src/assets/shapes/loading-indicator.svg +1 -0
  312. package/src/assets/shapes/oval.svg +1 -0
  313. package/src/assets/shapes/pentagon.svg +1 -0
  314. package/src/assets/shapes/pill.svg +1 -0
  315. package/src/assets/shapes/pixel-circle.svg +1 -0
  316. package/src/assets/shapes/pixel-triangle.svg +1 -0
  317. package/src/assets/shapes/puffy-diamond.svg +1 -0
  318. package/src/assets/shapes/puffy.svg +1 -0
  319. package/src/assets/shapes/semicircle.svg +1 -0
  320. package/src/assets/shapes/sided-cookie12.svg +1 -0
  321. package/src/assets/shapes/sided-cookie4.svg +1 -0
  322. package/src/assets/shapes/sided-cookie6.svg +1 -0
  323. package/src/assets/shapes/sided-cookie7.svg +1 -0
  324. package/src/assets/shapes/sided-cookie9.svg +1 -0
  325. package/src/assets/shapes/slanted.svg +1 -0
  326. package/src/assets/shapes/soft-boom.svg +1 -0
  327. package/src/assets/shapes/soft-burst.svg +1 -0
  328. package/src/assets/shapes/square.svg +1 -0
  329. package/src/assets/shapes/sunny.svg +1 -0
  330. package/src/assets/shapes/triangle.svg +1 -0
  331. package/src/assets/shapes/very-sunny.svg +1 -0
  332. package/src/assets/shapes/wavy-circle.svg +1 -0
  333. package/src/assets/shapes/wavy.svg +1 -0
  334. package/src/assets/sided-cookie12.svg +1 -0
  335. package/src/assets/sided-cookie4.svg +1 -0
  336. package/src/assets/sided-cookie6.svg +1 -0
  337. package/src/assets/sided-cookie7.svg +1 -0
  338. package/src/assets/sided-cookie9.svg +1 -0
  339. package/src/assets/slanted.svg +1 -0
  340. package/src/assets/soft-boom.svg +1 -0
  341. package/src/assets/soft-burst.svg +1 -0
  342. package/src/assets/square.svg +1 -0
  343. package/src/assets/star.svg +1 -0
  344. package/src/assets/sunny.svg +1 -0
  345. package/src/assets/triangle.svg +1 -0
  346. package/src/assets/very-sunny.svg +1 -0
  347. package/src/assets/wavy-circle.svg +1 -0
  348. package/src/assets/wavy.svg +1 -0
  349. package/src/assets.d.ts +12 -0
  350. package/src/components/_base/field-styles.ts +507 -0
  351. package/src/components/_base/index.ts +3 -0
  352. package/src/components/_base/interaction-styles.ts +125 -0
  353. package/src/components/_base/moni-element.ts +21 -0
  354. package/src/components/_base/shared-styles.ts +148 -0
  355. package/src/components/index.ts +45 -0
  356. package/src/components/loading-shapes.ts +9 -0
  357. package/src/components/moni-app-bar.test.ts +86 -0
  358. package/src/components/moni-app-bar.ts +190 -0
  359. package/src/components/moni-badge.ts +138 -0
  360. package/src/components/moni-bottom-sheet.test.ts +420 -0
  361. package/src/components/moni-bottom-sheet.ts +425 -0
  362. package/src/components/moni-button-group.test.ts +148 -0
  363. package/src/components/moni-button-group.ts +277 -0
  364. package/src/components/moni-button-segment.ts +291 -0
  365. package/src/components/moni-button.test.ts +166 -0
  366. package/src/components/moni-button.ts +709 -0
  367. package/src/components/moni-card.test.ts +83 -0
  368. package/src/components/moni-card.ts +203 -0
  369. package/src/components/moni-carousel.test.ts +238 -0
  370. package/src/components/moni-carousel.ts +1027 -0
  371. package/src/components/moni-checkbox.test.ts +78 -0
  372. package/src/components/moni-checkbox.ts +192 -0
  373. package/src/components/moni-chip.test.ts +168 -0
  374. package/src/components/moni-chip.ts +335 -0
  375. package/src/components/moni-color-field.test.ts +56 -0
  376. package/src/components/moni-color-field.ts +135 -0
  377. package/src/components/moni-context-menu.test.ts +99 -0
  378. package/src/components/moni-context-menu.ts +166 -0
  379. package/src/components/moni-dialog.ts +240 -0
  380. package/src/components/moni-divider.test.ts +42 -0
  381. package/src/components/moni-divider.ts +77 -0
  382. package/src/components/moni-expansion.ts +86 -0
  383. package/src/components/moni-fab-menu.test.ts +118 -0
  384. package/src/components/moni-fab-menu.ts +237 -0
  385. package/src/components/moni-fab.test.ts +128 -0
  386. package/src/components/moni-fab.ts +262 -0
  387. package/src/components/moni-file-field.test.ts +81 -0
  388. package/src/components/moni-file-field.ts +149 -0
  389. package/src/components/moni-icon.test.ts +70 -0
  390. package/src/components/moni-icon.ts +97 -0
  391. package/src/components/moni-list-item.test.ts +114 -0
  392. package/src/components/moni-list-item.ts +222 -0
  393. package/src/components/moni-list.ts +59 -0
  394. package/src/components/moni-loading-indicator.test.ts +41 -0
  395. package/src/components/moni-loading-indicator.ts +188 -0
  396. package/src/components/moni-menu-item.ts +85 -0
  397. package/src/components/moni-menu.test.ts +87 -0
  398. package/src/components/moni-menu.ts +287 -0
  399. package/src/components/moni-morph-modal.test.ts +286 -0
  400. package/src/components/moni-morph-modal.ts +1312 -0
  401. package/src/components/moni-nav-item.ts +243 -0
  402. package/src/components/moni-nav.test.ts +139 -0
  403. package/src/components/moni-nav.ts +266 -0
  404. package/src/components/moni-progress.test.ts +90 -0
  405. package/src/components/moni-progress.ts +322 -0
  406. package/src/components/moni-radio.test.ts +86 -0
  407. package/src/components/moni-radio.ts +196 -0
  408. package/src/components/moni-ripple.ts +146 -0
  409. package/src/components/moni-segmented-button.test.ts +99 -0
  410. package/src/components/moni-segmented-button.ts +220 -0
  411. package/src/components/moni-select-option.ts +85 -0
  412. package/src/components/moni-select.test.ts +210 -0
  413. package/src/components/moni-select.ts +1107 -0
  414. package/src/components/moni-shape.ts +128 -0
  415. package/src/components/moni-side-sheet.test.ts +128 -0
  416. package/src/components/moni-side-sheet.ts +544 -0
  417. package/src/components/moni-slider.test.ts +82 -0
  418. package/src/components/moni-slider.ts +387 -0
  419. package/src/components/moni-snackbar.test.ts +82 -0
  420. package/src/components/moni-snackbar.ts +146 -0
  421. package/src/components/moni-split-button.ts +121 -0
  422. package/src/components/moni-step.test.ts +57 -0
  423. package/src/components/moni-step.ts +155 -0
  424. package/src/components/moni-stepper.test.ts +116 -0
  425. package/src/components/moni-stepper.ts +100 -0
  426. package/src/components/moni-switch.test.ts +117 -0
  427. package/src/components/moni-switch.ts +237 -0
  428. package/src/components/moni-tab.test.ts +54 -0
  429. package/src/components/moni-tab.ts +133 -0
  430. package/src/components/moni-tabs.ts +92 -0
  431. package/src/components/moni-text-field.test.ts +115 -0
  432. package/src/components/moni-text-field.ts +149 -0
  433. package/src/components/moni-textarea.test.ts +147 -0
  434. package/src/components/moni-textarea.ts +176 -0
  435. package/src/components/moni-time-picker.test.ts +61 -0
  436. package/src/components/moni-time-picker.ts +800 -0
  437. package/src/components/moni-toolbar.test.ts +93 -0
  438. package/src/components/moni-toolbar.ts +119 -0
  439. package/src/components/moni-tooltip.test.ts +122 -0
  440. package/src/components/moni-tooltip.ts +324 -0
  441. package/src/components/moni-typography.test.ts +119 -0
  442. package/src/components/moni-typography.ts +195 -0
  443. package/src/index.ts +65 -0
  444. package/src/styles/animations.css +46 -0
  445. package/src/styles/base.css +248 -0
  446. package/src/styles/index.css +3 -0
  447. package/src/styles/tokens.css +190 -0
  448. package/src/types/svelte-runes.d.ts +7 -0
  449. package/src/utils/color.ts +170 -0
  450. package/src/utils/theme.svelte.ts +206 -0
  451. package/src/web-components.ts +5 -0
@@ -0,0 +1,1027 @@
1
+ import { html, css } from 'lit';
2
+ import { customElement, property, query, state, queryAll } from 'lit/decorators.js';
3
+ import { MoniElement, sharedStyles } from './_base/index.js';
4
+ import { gsap } from 'gsap';
5
+
6
+ export interface CarouselItem {
7
+ title: string;
8
+ img: string;
9
+ href?: string;
10
+ target?: string;
11
+ }
12
+
13
+ /**
14
+ * Material Design 3 Expressive Carousel component.
15
+ * Supports three layouts:
16
+ * - `multi-browse`: Shows a collection of items (large, medium, and small items).
17
+ * - `hero`: Focuses on one large item with a smaller item peeking.
18
+ * - `uncontained`: Standard same-sized layout bleeding off the edges.
19
+ *
20
+ * Attributes:
21
+ * - layout: 'multi-browse' | 'hero' | 'uncontained'
22
+ * - auto: if true, dynamically calculates items sizes to fit the container without gaps.
23
+ * - large-width: width of large items (used in non-auto mode or as a guideline).
24
+ * - medium-width: width of medium items (multi-browse layout).
25
+ * - small-width: width of small/peeking items.
26
+ * - gap: spacing between elements (default 8px).
27
+ * - padding: leading/trailing padding (default 16px).
28
+ * - border-radius: radius of cards (default 28px).
29
+ * - header-text: header title of the carousel.
30
+ * - show-all: renders a "Show all" action button/link.
31
+ * - show-all-text: customize the text for the "Show all" action.
32
+ */
33
+ @customElement('moni-carousel')
34
+ export class MoniCarousel extends MoniElement {
35
+ @property({ type: Array }) items: CarouselItem[] = [];
36
+ @property({ reflect: true }) layout: 'multi-browse' | 'hero' | 'uncontained' = 'multi-browse';
37
+ @property({ type: Boolean, reflect: true }) auto = true;
38
+ @property({ type: Number, attribute: 'large-width' }) largeWidth = 220;
39
+ @property({ type: Number, attribute: 'medium-width' }) mediumWidth = 96;
40
+ @property({ type: Number, attribute: 'small-width' }) smallWidth = 48;
41
+ @property({ type: Number }) gap = 8;
42
+ @property({ type: Number }) padding = 16;
43
+ @property({ type: Number, attribute: 'border-radius' }) borderRadius = 28;
44
+ @property({ type: Boolean, attribute: 'show-all' }) showAll = false;
45
+ @property({ attribute: 'show-all-text' }) showAllText = 'Show all';
46
+ @property({ attribute: 'header-text' }) headerText = '';
47
+ @property({ type: Boolean, attribute: 'hide-nav' }) hideNav = false;
48
+
49
+ @state() private _containerWidth = 0;
50
+ @state() private _slottedItems: CarouselItem[] = [];
51
+ @state() private _hasSlottedShowAll = false;
52
+
53
+ get effectiveItems(): CarouselItem[] {
54
+ return this.items && this.items.length > 0 ? this.items : this._slottedItems;
55
+ }
56
+
57
+ @query('.scroll-container') private _scrollContainer!: HTMLDivElement;
58
+ @queryAll('.card') private _cards!: NodeListOf<HTMLDivElement>;
59
+
60
+ private _resizeObserver: ResizeObserver | null = null;
61
+ private _isDown = false;
62
+ private _startX = 0;
63
+ private _scrollLeftStart = 0;
64
+ private _velocity = 0;
65
+ private _lastTime = 0;
66
+ private _lastX = 0;
67
+ private _draggedDistance = 0;
68
+ private _rafId: number | null = null;
69
+ private _isMobile = false;
70
+
71
+ // Scroll interpolation state
72
+ private _tickerTarget = 0;
73
+ private _tickerCurrent = 0;
74
+ private _isTicking = false;
75
+
76
+ // quickSetter caches — avoid gsap.set() overhead per frame
77
+ private _cardSetX: ((value: number) => void)[] = [];
78
+ private _cardSetWidth: ((value: number) => void)[] = [];
79
+ private _imgSetX: ((value: number) => void)[] = [];
80
+ private _titleSetOpacity: ((value: number) => void)[] = [];
81
+
82
+ static override styles = [
83
+ sharedStyles,
84
+ css`
85
+ :host {
86
+ display: block;
87
+ width: 100%;
88
+ user-select: none;
89
+ -webkit-user-select: none;
90
+ }
91
+
92
+ .carousel-container {
93
+ position: relative;
94
+ width: 100%;
95
+ display: flex;
96
+ flex-direction: column;
97
+ gap: 12px;
98
+ }
99
+
100
+ .carousel-header {
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: flex-end;
104
+ padding: 0 var(--carousel-padding, 16px);
105
+ }
106
+
107
+ .carousel-header h3 {
108
+ margin: 0;
109
+ font-family: var(--font-title, serif);
110
+ font-size: 1.5rem;
111
+ font-weight: 500;
112
+ color: var(--on-surface);
113
+ }
114
+
115
+ .show-all-link {
116
+ color: var(--primary);
117
+ font-weight: 500;
118
+ font-size: 0.875rem;
119
+ text-decoration: none;
120
+ cursor: pointer;
121
+ transition: opacity var(--speed2);
122
+ }
123
+
124
+ .show-all-link:hover {
125
+ opacity: 0.8;
126
+ text-decoration: underline;
127
+ }
128
+
129
+ .carousel-viewport {
130
+ position: relative;
131
+ width: 100%;
132
+ overflow: hidden;
133
+ }
134
+
135
+ .scroll-container {
136
+ width: 100%;
137
+ height: 296px;
138
+ overflow-x: auto;
139
+ overflow-y: hidden;
140
+ scroll-snap-type: x mandatory;
141
+ scrollbar-width: none;
142
+ -webkit-overflow-scrolling: touch;
143
+ cursor: grab;
144
+ }
145
+
146
+ .scroll-container:active {
147
+ cursor: grabbing;
148
+ }
149
+
150
+ .scroll-container::-webkit-scrollbar {
151
+ display: none;
152
+ }
153
+
154
+ .snap-track {
155
+ display: flex;
156
+ width: calc(var(--carousel-track-width, 0px) + var(--carousel-right-padding, 128px));
157
+ gap: var(--carousel-gap, 8px);
158
+ padding: 8px 0 8px var(--carousel-padding, 16px);
159
+ height: 100%;
160
+ pointer-events: none;
161
+ }
162
+
163
+ .snap-item {
164
+ flex: 0 0 var(--carousel-snap-width, 220px);
165
+ height: 100%;
166
+ scroll-snap-align: start;
167
+ pointer-events: none;
168
+ }
169
+
170
+ .visual-track {
171
+ position: absolute;
172
+ left: 0;
173
+ top: 8px;
174
+ width: 0;
175
+ height: 280px;
176
+ overflow: visible;
177
+ pointer-events: none;
178
+ }
179
+
180
+ .card {
181
+ position: absolute;
182
+ top: 0;
183
+ height: 280px;
184
+ border-radius: var(--carousel-border-radius, 28px);
185
+ overflow: hidden;
186
+ background-color: var(--surface-container-high);
187
+ pointer-events: auto;
188
+ box-shadow: var(--elevate1);
189
+ will-change: transform;
190
+ cursor: pointer;
191
+ transition: box-shadow var(--speed2);
192
+ }
193
+
194
+ .card:hover {
195
+ box-shadow: var(--elevate2);
196
+ }
197
+
198
+ .card::after {
199
+ content: '';
200
+ position: absolute;
201
+ top: 0;
202
+ left: 0;
203
+ right: 0;
204
+ bottom: 0;
205
+ background: linear-gradient(to bottom, transparent 40%, rgba(0, 0, 0, 0.7) 100%);
206
+ pointer-events: none;
207
+ z-index: 1;
208
+ }
209
+
210
+ .img-parallax-container {
211
+ width: 100%;
212
+ height: 100%;
213
+ position: relative;
214
+ overflow: hidden;
215
+ }
216
+
217
+ .card img {
218
+ width: calc(100% + 50px);
219
+ height: 100%;
220
+ object-fit: cover;
221
+ position: absolute;
222
+ left: -25px;
223
+ pointer-events: none;
224
+ user-select: none;
225
+ -webkit-user-drag: none;
226
+ will-change: transform;
227
+ }
228
+
229
+ .card-title {
230
+ position: absolute;
231
+ bottom: 20px;
232
+ left: 20px;
233
+ right: 20px;
234
+ color: #ffffff;
235
+ margin: 0;
236
+ font-size: 1.1rem;
237
+ font-weight: 500;
238
+ pointer-events: none;
239
+ white-space: nowrap;
240
+ overflow: hidden;
241
+ text-overflow: ellipsis;
242
+ z-index: 2;
243
+ }
244
+
245
+ /* Arrow navigation overlays */
246
+ .nav-button {
247
+ position: absolute;
248
+ top: 50%;
249
+ transform: translateY(-50%);
250
+ z-index: 10;
251
+ width: 48px;
252
+ height: 48px;
253
+ border-radius: 50%;
254
+ background-color: var(--surface-container-highest);
255
+ color: var(--on-surface);
256
+ border: none;
257
+ box-shadow: var(--elevate2);
258
+ display: flex;
259
+ justify-content: center;
260
+ align-items: center;
261
+ cursor: pointer;
262
+ opacity: 0;
263
+ pointer-events: none;
264
+ transition: opacity var(--speed2), background-color var(--speed2);
265
+ }
266
+
267
+ .nav-button:hover {
268
+ background-color: var(--primary-container);
269
+ color: var(--on-primary-container);
270
+ }
271
+
272
+ .nav-button.prev {
273
+ left: 24px;
274
+ }
275
+
276
+ .nav-button.next {
277
+ right: 24px;
278
+ }
279
+
280
+ .carousel-viewport:hover .nav-button.visible {
281
+ opacity: 1;
282
+ pointer-events: auto;
283
+ }
284
+
285
+ @media (max-width: 600px) {
286
+ .nav-button {
287
+ display: none !important;
288
+ }
289
+ }
290
+
291
+ /* Utility icon style inside shadow dom */
292
+ .material-symbols {
293
+ font-family: var(--font-icon);
294
+ font-weight: normal;
295
+ font-style: normal;
296
+ font-size: 24px;
297
+ line-height: 1;
298
+ letter-spacing: normal;
299
+ text-transform: none;
300
+ display: inline-block;
301
+ white-space: nowrap;
302
+ word-wrap: normal;
303
+ direction: ltr;
304
+ -webkit-font-smoothing: antialiased;
305
+ }
306
+ `
307
+ ];
308
+
309
+ override connectedCallback() {
310
+ super.connectedCallback();
311
+ if (typeof ResizeObserver !== 'undefined') {
312
+ this._resizeObserver = new ResizeObserver((entries) => {
313
+ for (const entry of entries) {
314
+ this._containerWidth = entry.contentRect.width;
315
+ this.updateLayout(true); // Synchronous update for instant resizing!
316
+ this.requestUpdate();
317
+ }
318
+ });
319
+ this._resizeObserver.observe(this);
320
+ }
321
+
322
+ // Detect mobile for parallax toggle
323
+ if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {
324
+ const mql = window.matchMedia('(max-width: 600px)');
325
+ this._isMobile = mql.matches;
326
+ const handler = (e: MediaQueryListEvent) => { this._isMobile = e.matches; };
327
+ mql.addEventListener('change', handler);
328
+ // Store for cleanup
329
+ (this as any).__mqlCleanup = () => mql.removeEventListener('change', handler);
330
+ }
331
+ }
332
+
333
+ override disconnectedCallback() {
334
+ if (this._resizeObserver) {
335
+ this._resizeObserver.disconnect();
336
+ }
337
+ // Cancel any pending RAF or ticker
338
+ if (this._rafId !== null) {
339
+ cancelAnimationFrame(this._rafId);
340
+ this._rafId = null;
341
+ }
342
+ gsap.ticker.remove(this._tick);
343
+ this._isTicking = false;
344
+
345
+ // Clean up matchMedia listener
346
+ if ((this as any).__mqlCleanup) {
347
+ (this as any).__mqlCleanup();
348
+ delete (this as any).__mqlCleanup;
349
+ }
350
+ super.disconnectedCallback();
351
+ }
352
+
353
+ override firstUpdated() {
354
+ const defaultSlot = this.shadowRoot?.querySelector('slot:not([name])');
355
+ if (defaultSlot) {
356
+ this._handleSlotChange({ target: defaultSlot } as any);
357
+ }
358
+ const showAllSlot = this.shadowRoot?.querySelector('slot[name="show-all"]');
359
+ if (showAllSlot) {
360
+ this._handleShowAllSlotChange({ target: showAllSlot } as any);
361
+ }
362
+ this._buildQuickSetters();
363
+ this.updateLayout(true);
364
+ }
365
+
366
+ override updated(changedProperties: Map<string, any>) {
367
+ super.updated(changedProperties);
368
+ if (
369
+ changedProperties.has('layout') ||
370
+ changedProperties.has('largeWidth') ||
371
+ changedProperties.has('mediumWidth') ||
372
+ changedProperties.has('smallWidth') ||
373
+ changedProperties.has('gap') ||
374
+ changedProperties.has('padding') ||
375
+ changedProperties.has('items') ||
376
+ changedProperties.has('hideNav') ||
377
+ changedProperties.has('_containerWidth') ||
378
+ changedProperties.has('_slottedItems') ||
379
+ changedProperties.has('_hasSlottedShowAll')
380
+ ) {
381
+ if (changedProperties.has('layout') && this._scrollContainer) {
382
+ this._scrollContainer.scrollLeft = 0;
383
+ this._scrollContainer.style.scrollSnapType =
384
+ this.layout === 'uncontained' ? 'none' : 'x mandatory';
385
+ }
386
+ this.updateLayout(true);
387
+ }
388
+ }
389
+
390
+ get computedLayout() {
391
+ const W_c = this._containerWidth || this.getBoundingClientRect().width || 360;
392
+ const gap = this.gap;
393
+ const padding = this.padding;
394
+ const W_a = W_c - 2 * padding;
395
+
396
+ let L = this.largeWidth;
397
+ let M = this.mediumWidth;
398
+ let S = this.smallWidth;
399
+ let N = 1;
400
+
401
+ if (this.auto) {
402
+ if (this.layout === 'uncontained') {
403
+ // Fits as many L items as possible, letting them bleed off the right edge.
404
+ // We target large width around 260px.
405
+ L = Math.min(W_a, 260);
406
+ } else if (this.layout === 'hero') {
407
+ // Hero has N large items and 1 small peeking item.
408
+ // W_a = N * L + S + N * gap
409
+ S = 48;
410
+ N = Math.max(1, Math.floor((W_a - S) / (250 + gap)));
411
+ L = (W_a - S - N * gap) / N;
412
+
413
+ if (L < 150) {
414
+ N = 1;
415
+ L = W_a - S - gap;
416
+ }
417
+ } else {
418
+ // multi-browse: N large items, 1 medium, 1 small
419
+ // W_a = N * L + M + S + (N + 1) * gap
420
+ S = 48;
421
+ let bestN = 1;
422
+ let bestL = 220;
423
+ let bestDiff = Infinity;
424
+
425
+ for (let n = 1; n <= 10; n++) {
426
+ const l = (W_a - S - (n + 1) * gap) / (n + 0.45);
427
+ if (l >= 140 && l <= 340) {
428
+ const diff = Math.abs(l - 220);
429
+ if (diff < bestDiff) {
430
+ bestDiff = diff;
431
+ bestN = n;
432
+ bestL = l;
433
+ }
434
+ }
435
+ }
436
+
437
+ if (bestDiff === Infinity) {
438
+ N = 1;
439
+ L = (W_a - S - 2 * gap) / 1.45;
440
+ } else {
441
+ N = bestN;
442
+ L = bestL;
443
+ }
444
+ M = 0.45 * L;
445
+ }
446
+ } else {
447
+ // Non-auto/manual sizing mode
448
+ L = this.largeWidth;
449
+ M = this.mediumWidth;
450
+ S = this.smallWidth;
451
+
452
+ if (this.layout === 'hero') {
453
+ N = 1;
454
+ } else if (this.layout === 'uncontained') {
455
+ N = Math.max(1, Math.floor(W_a / (L + gap)));
456
+ } else {
457
+ N = 1;
458
+ }
459
+ }
460
+
461
+ return { L, M, S, N };
462
+ }
463
+
464
+ get itemSize() {
465
+ return this.computedLayout.L + this.gap;
466
+ }
467
+
468
+ private _getCardLayout(p: number) {
469
+ const { L, M, S, N } = this.computedLayout;
470
+ const gap = this.gap;
471
+ const padding = this.padding;
472
+
473
+ let width = L;
474
+ let x = padding;
475
+ let opacity = 1;
476
+
477
+ if (this.layout === 'uncontained') {
478
+ width = L;
479
+ x = padding + p * (L + gap);
480
+ opacity = 1;
481
+ } else if (this.layout === 'hero') {
482
+ const X_N = padding + N * (L + gap);
483
+ const X_M1 = padding - gap - S;
484
+
485
+ if (p <= -1) {
486
+ width = S;
487
+ x = X_M1 + (p - -1) * (S + gap);
488
+ opacity = 0;
489
+ } else if (p <= 0) {
490
+ width = gsap.utils.interpolate(S, L, p + 1);
491
+ x = gsap.utils.interpolate(X_M1, padding, p + 1);
492
+ opacity = gsap.utils.interpolate(0, 1, p + 1);
493
+ } else if (p <= N - 1) {
494
+ width = L;
495
+ x = padding + p * (L + gap);
496
+ opacity = 1;
497
+ } else if (p <= N) {
498
+ const u = p - (N - 1);
499
+ const X_last = padding + (N - 1) * (L + gap);
500
+ width = gsap.utils.interpolate(L, S, u);
501
+ x = gsap.utils.interpolate(X_last, X_N, u);
502
+ opacity = gsap.utils.interpolate(1, 0, u);
503
+ } else {
504
+ width = S;
505
+ x = X_N + (p - N) * (S + gap);
506
+ opacity = 0;
507
+ }
508
+ } else {
509
+ // multi-browse
510
+ const X_N = padding + N * (L + gap);
511
+ const X_N1 = X_N + M + gap;
512
+ const X_M1 = padding - gap - S;
513
+
514
+ if (p <= -1) {
515
+ width = S;
516
+ x = X_M1 + (p - -1) * (S + gap);
517
+ opacity = 0;
518
+ } else if (p <= 0) {
519
+ width = gsap.utils.interpolate(S, L, p + 1);
520
+ x = gsap.utils.interpolate(X_M1, padding, p + 1);
521
+ opacity = gsap.utils.interpolate(0, 1, p + 1);
522
+ } else if (p <= N - 1) {
523
+ width = L;
524
+ x = padding + p * (L + gap);
525
+ opacity = 1;
526
+ } else if (p <= N) {
527
+ const u = p - (N - 1);
528
+ const X_last = padding + (N - 1) * (L + gap);
529
+ width = gsap.utils.interpolate(L, M, u);
530
+ x = gsap.utils.interpolate(X_last, X_N, u);
531
+ opacity = gsap.utils.interpolate(1, 0, u);
532
+ } else if (p <= N + 1) {
533
+ const u = p - N;
534
+ width = gsap.utils.interpolate(M, S, u);
535
+ x = gsap.utils.interpolate(X_N, X_N1, u);
536
+ opacity = 0;
537
+ } else {
538
+ width = S;
539
+ x = X_N1 + (p - (N + 1)) * (S + gap);
540
+ opacity = 0;
541
+ }
542
+ }
543
+
544
+ return { width, x, opacity };
545
+ }
546
+
547
+ /**
548
+ * Rebuild quickSetter caches after cards change.
549
+ * quickSetter avoids gsap.set() overhead for properties updated every frame.
550
+ */
551
+ private _buildQuickSetters() {
552
+ if (!this._cards || this._cards.length === 0) return;
553
+
554
+ this._cardSetX = [];
555
+ this._cardSetWidth = [];
556
+ this._imgSetX = [];
557
+ this._titleSetOpacity = [];
558
+
559
+ this._cards.forEach((card) => {
560
+ this._cardSetX.push(gsap.quickSetter(card, 'x', 'px') as (v: number) => void);
561
+ this._cardSetWidth.push(gsap.quickSetter(card, 'width', 'px') as (v: number) => void);
562
+
563
+ const img = card.querySelector('img');
564
+ this._imgSetX.push(
565
+ img ? (gsap.quickSetter(img, 'x', 'px') as (v: number) => void) : (() => {})
566
+ );
567
+
568
+ const title = card.querySelector('.card-title');
569
+ this._titleSetOpacity.push(
570
+ title ? (gsap.quickSetter(title, 'opacity') as (v: number) => void) : (() => {})
571
+ );
572
+ });
573
+ }
574
+
575
+ /**
576
+ * RAF-gated version of updateLayout for scroll events.
577
+ * Prevents multiple layout recalculations per frame.
578
+ */
579
+ private _tick = () => {
580
+ const dt = gsap.ticker.deltaRatio();
581
+ // Smoother lerp on mobile to avoid abrupt changes ("brusco")
582
+ const ease = this._isMobile ? 0.08 : 0.15;
583
+ this._tickerCurrent += (this._tickerTarget - this._tickerCurrent) * ease * dt;
584
+
585
+ if (Math.abs(this._tickerTarget - this._tickerCurrent) < 0.0005) {
586
+ this._tickerCurrent = this._tickerTarget;
587
+ gsap.ticker.remove(this._tick);
588
+ this._isTicking = false;
589
+ }
590
+
591
+ this._applyLayout(this._tickerCurrent);
592
+ };
593
+
594
+ private _scheduleLayout = () => {
595
+ if (!this._scrollContainer) return;
596
+ this._tickerTarget = this._scrollContainer.scrollLeft / this.itemSize;
597
+ if (!this._isTicking) {
598
+ this._isTicking = true;
599
+ gsap.ticker.add(this._tick);
600
+ }
601
+ };
602
+
603
+ updateLayout = (instant = false) => {
604
+ if (!this._scrollContainer || !this._cards || this._cards.length === 0) return;
605
+
606
+ // Rebuild quickSetters if card count changed (e.g. after items update)
607
+ if (this._cardSetX.length !== this._cards.length) {
608
+ this._buildQuickSetters();
609
+ }
610
+
611
+ this._tickerTarget = this._scrollContainer.scrollLeft / this.itemSize;
612
+
613
+ if (instant || (!this._isTicking && this._tickerCurrent === 0)) {
614
+ this._tickerCurrent = this._tickerTarget;
615
+ this._applyLayout(this._tickerCurrent);
616
+ } else {
617
+ if (!this._isTicking) {
618
+ this._isTicking = true;
619
+ gsap.ticker.add(this._tick);
620
+ }
621
+ }
622
+ };
623
+
624
+ private _applyLayout(t: number) {
625
+ const isMobile = this._isMobile;
626
+ const cardCount = this._cards.length;
627
+ for (let i = 0; i < cardCount; i++) {
628
+ const p = i - t;
629
+ const layout = this._getCardLayout(p);
630
+
631
+ // Use quickSetters — ~3x faster than gsap.set() per call
632
+ this._cardSetX[i](layout.x);
633
+ this._cardSetWidth[i](layout.width);
634
+
635
+ // Skip parallax on mobile to save per-frame work
636
+ if (!isMobile) {
637
+ const clampedP = Math.max(-1, Math.min(1, p));
638
+ this._imgSetX[i](clampedP * -25);
639
+ }
640
+
641
+ this._titleSetOpacity[i](layout.opacity);
642
+ }
643
+ }
644
+
645
+ private _handleMouseDown(e: MouseEvent) {
646
+ gsap.killTweensOf(this._scrollContainer);
647
+ this._isDown = true;
648
+ this._draggedDistance = 0;
649
+ this._scrollContainer.style.scrollBehavior = 'auto';
650
+ this._scrollContainer.style.scrollSnapType = 'none';
651
+
652
+ this._startX = e.pageX - this._scrollContainer.offsetLeft;
653
+ this._scrollLeftStart = this._scrollContainer.scrollLeft;
654
+ this._velocity = 0;
655
+ this._lastX = e.pageX;
656
+ this._lastTime = performance.now();
657
+ }
658
+
659
+ private _handleMouseMove(e: MouseEvent) {
660
+ if (!this._isDown) return;
661
+ e.preventDefault();
662
+
663
+ const currentX = e.pageX;
664
+ const x = currentX - this._scrollContainer.offsetLeft;
665
+ this._scrollContainer.scrollLeft = this._scrollLeftStart + (this._startX - x) * 1.25;
666
+
667
+ const currentTime = performance.now();
668
+ const dt = currentTime - this._lastTime;
669
+ if (dt > 0) {
670
+ this._velocity = (currentX - this._lastX) / dt;
671
+ }
672
+ this._draggedDistance += Math.abs(currentX - this._lastX);
673
+ this._lastX = currentX;
674
+ this._lastTime = currentTime;
675
+ }
676
+
677
+ private _handleMouseUp() {
678
+ if (!this._isDown) return;
679
+ this._isDown = false;
680
+
681
+ const currentTime = performance.now();
682
+ if (currentTime - this._lastTime > 100) {
683
+ this._velocity = 0;
684
+ }
685
+
686
+ // Dynamic momentum: stronger swipes have a much higher multiplier
687
+ const momentumMultiplier = 350 + Math.abs(this._velocity) * 300;
688
+ const projectedScrollLeft = this._scrollContainer.scrollLeft - this._velocity * momentumMultiplier;
689
+
690
+ if (this.layout !== 'uncontained') {
691
+ this._scrollContainer.style.scrollSnapType = 'none';
692
+
693
+ let targetIndex = Math.round(projectedScrollLeft / this.itemSize);
694
+ targetIndex = Math.max(0, Math.min(this.effectiveItems.length - 1, targetIndex));
695
+ const snapPoint = targetIndex * this.itemSize;
696
+
697
+ // Dynamic duration: longer distance = more time, max 0.85s for faster feeling
698
+ const currentIndex = Math.round(this._scrollContainer.scrollLeft / this.itemSize);
699
+ const cardsSkipped = Math.abs(targetIndex - currentIndex);
700
+ const duration = Math.min(0.85, 0.25 + cardsSkipped * 0.04);
701
+
702
+ this._scrollContainer.style.scrollBehavior = 'auto';
703
+
704
+ gsap.to(this._scrollContainer, {
705
+ scrollLeft: snapPoint,
706
+ duration: duration,
707
+ ease: 'power3.out',
708
+ onComplete: () => {
709
+ if (!this._isDown && this._scrollContainer) {
710
+ this._scrollContainer.style.scrollSnapType = 'x mandatory';
711
+ }
712
+ }
713
+ });
714
+ }
715
+ }
716
+
717
+ private _handleTouchStart(e: TouchEvent) {
718
+ gsap.killTweensOf(this._scrollContainer);
719
+ this._isDown = true;
720
+ this._draggedDistance = 0;
721
+ this._scrollContainer.style.scrollBehavior = 'auto';
722
+ this._scrollContainer.style.scrollSnapType = 'none';
723
+
724
+ const touch = e.touches[0];
725
+ this._startX = touch.pageX - this._scrollContainer.offsetLeft;
726
+ this._scrollLeftStart = this._scrollContainer.scrollLeft;
727
+ this._velocity = 0;
728
+ this._lastX = touch.pageX;
729
+ this._lastTime = performance.now();
730
+ }
731
+
732
+ private _handleTouchMove(e: TouchEvent) {
733
+ if (!this._isDown) return;
734
+
735
+ const touch = e.touches[0];
736
+ const currentX = touch.pageX;
737
+ const x = currentX - this._scrollContainer.offsetLeft;
738
+ this._scrollContainer.scrollLeft = this._scrollLeftStart + (this._startX - x);
739
+
740
+ const currentTime = performance.now();
741
+ const dt = currentTime - this._lastTime;
742
+ if (dt > 0) {
743
+ this._velocity = (currentX - this._lastX) / dt;
744
+ }
745
+ this._draggedDistance += Math.abs(currentX - this._lastX);
746
+ this._lastX = currentX;
747
+ this._lastTime = currentTime;
748
+ }
749
+
750
+ private _handleTouchEnd() {
751
+ this._handleMouseUp();
752
+ }
753
+
754
+ private _handleWheel(e: WheelEvent) {
755
+ // Solo interceptar scroll vertical puro (rueda de mouse típica)
756
+ if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
757
+ const maxScroll = this._scrollContainer.scrollWidth - this._scrollContainer.clientWidth;
758
+ const currentScroll = this._scrollContainer.scrollLeft;
759
+
760
+ // Comprobar si estamos en los bordes para permitir el scroll natural de la página
761
+ const isAtStart = currentScroll <= 0;
762
+ const isAtEnd = currentScroll >= maxScroll - 1; // Margen de 1px por redondeos decimales
763
+
764
+ if ((e.deltaY < 0 && isAtStart) || (e.deltaY > 0 && isAtEnd)) {
765
+ return; // Dejar que la página haga scroll vertical
766
+ }
767
+
768
+ e.preventDefault(); // Prevenir el scroll de la página
769
+
770
+ // Desactivar el snap momentáneamente para que el movimiento de rueda sea fluido
771
+ this._scrollContainer.style.scrollSnapType = 'none';
772
+
773
+ // Multiplicador si el deltaMode no es pixel (0)
774
+ let delta = e.deltaY;
775
+ if (e.deltaMode === 1) delta *= 40; // Líneas
776
+ else if (e.deltaMode === 2) delta *= 800; // Páginas
777
+
778
+ this._scrollContainer.scrollLeft += delta;
779
+
780
+ // Restablecer el snap después de un pequeño retraso
781
+ if ((this as any)._wheelTimeout) clearTimeout((this as any)._wheelTimeout);
782
+ (this as any)._wheelTimeout = setTimeout(() => {
783
+ if (this.layout !== 'uncontained') {
784
+ this._scrollContainer.style.scrollSnapType = 'x mandatory';
785
+ }
786
+ }, 150);
787
+ }
788
+ }
789
+
790
+ private _handleCardClick(e: Event, item: CarouselItem, index: number) {
791
+ if (this._draggedDistance > 8) {
792
+ e.preventDefault();
793
+ e.stopPropagation();
794
+ return;
795
+ }
796
+
797
+ this.dispatchEvent(
798
+ new CustomEvent('item-click', {
799
+ detail: { item, index },
800
+ bubbles: true,
801
+ composed: true
802
+ })
803
+ );
804
+ }
805
+
806
+ private _scrollPrevious() {
807
+ const target = Math.max(0, this._scrollContainer.scrollLeft - this.itemSize);
808
+ this._scrollContainer.style.scrollBehavior = 'smooth';
809
+ this._scrollContainer.scrollTo({ left: target, behavior: 'smooth' });
810
+ }
811
+
812
+ private _scrollNext() {
813
+ const maxScroll = this._scrollContainer.scrollWidth - this._scrollContainer.clientWidth;
814
+ const target = Math.min(maxScroll, this._scrollContainer.scrollLeft + this.itemSize);
815
+ this._scrollContainer.style.scrollBehavior = 'smooth';
816
+ this._scrollContainer.scrollTo({ left: target, behavior: 'smooth' });
817
+ }
818
+
819
+ private _handleShowAllClick(e: Event) {
820
+ const target = e.target as HTMLElement;
821
+ if (target.tagName.toLowerCase() === 'a' && target.getAttribute('href') === '#') {
822
+ e.preventDefault();
823
+ }
824
+ this.dispatchEvent(
825
+ new CustomEvent('show-all-click', {
826
+ bubbles: true,
827
+ composed: true
828
+ })
829
+ );
830
+ }
831
+
832
+ private _handleShowAllSlotChange(e: Event) {
833
+ const slot = e.target as HTMLSlotElement;
834
+ let nodes = typeof slot.assignedElements === 'function' ? slot.assignedElements({ flatten: true }) : [];
835
+ if (nodes.length === 0) {
836
+ nodes = slot.assignedNodes().filter(n => n.nodeType === 1) as Element[];
837
+ }
838
+ if (nodes.length === 0) {
839
+ nodes = Array.from(this.querySelectorAll('[slot="show-all"]')); // Fallback for JSDOM
840
+ }
841
+ this._hasSlottedShowAll = nodes.length > 0;
842
+ }
843
+
844
+ private _handleSlotChange(e: Event) {
845
+ const slot = e.target as HTMLSlotElement;
846
+ let nodes = typeof slot.assignedElements === 'function' ? slot.assignedElements({ flatten: true }) : [];
847
+ if (nodes.length === 0) {
848
+ nodes = slot.assignedNodes().filter(n => n.nodeType === 1) as Element[];
849
+ }
850
+ if (nodes.length === 0) {
851
+ nodes = Array.from(this.children).filter(child => child.getAttribute('slot') !== 'show-all'); // Fallback for JSDOM, ignore show-all slot
852
+ }
853
+ const items: CarouselItem[] = [];
854
+
855
+ for (const node of nodes) {
856
+ if (node.tagName.toLowerCase() === 'img') {
857
+ const img = node as HTMLImageElement;
858
+ items.push({
859
+ title: img.getAttribute('title') || img.getAttribute('alt') || '',
860
+ img: img.src
861
+ });
862
+ } else if (node.tagName.toLowerCase() === 'a') {
863
+ const anchor = node as HTMLAnchorElement;
864
+ const img = anchor.querySelector('img');
865
+ if (img) {
866
+ items.push({
867
+ title: img.getAttribute('title') || img.getAttribute('alt') || anchor.getAttribute('title') || '',
868
+ img: img.src,
869
+ href: anchor.href,
870
+ target: anchor.target
871
+ });
872
+ }
873
+ }
874
+ }
875
+ this._slottedItems = items;
876
+
877
+ // Check if slotted show-all button presence changed
878
+ const hasSlottedShowAll = !!this.querySelector('[slot="show-all"]');
879
+ if (hasSlottedShowAll !== this._hasSlottedShowAll) {
880
+ this._hasSlottedShowAll = hasSlottedShowAll;
881
+ }
882
+
883
+ void this.updateComplete.then(() => {
884
+ this.updateLayout(true);
885
+ });
886
+ }
887
+
888
+ override render() {
889
+ const { L, M, S } = this.computedLayout;
890
+ const hasItems = this.effectiveItems && this.effectiveItems.length > 0;
891
+ const hasSlottedShowAll = this._hasSlottedShowAll || !!this.querySelector('[slot="show-all"]');
892
+ const showHeader = this.headerText || this.showAll || hasSlottedShowAll;
893
+
894
+ let rightPadding = 128;
895
+ if (this.layout === 'hero') {
896
+ rightPadding = S + this.gap;
897
+ } else if (this.layout === 'uncontained') {
898
+ rightPadding = 16;
899
+ } else {
900
+ rightPadding = M + S + 2 * this.gap;
901
+ }
902
+
903
+ // Ensure the scroll track is long enough to allow the last item to scroll to the start (p = 0 focus).
904
+ // Safety margin of 400px avoids fractional rounding/zoom-scale snapping blockages in Firefox.
905
+ const W_c = this._containerWidth || this.getBoundingClientRect().width || 360;
906
+ const minRightPaddingNeeded = W_c - L - this.padding + 400;
907
+ if (rightPadding < minRightPaddingNeeded) {
908
+ rightPadding = minRightPaddingNeeded;
909
+ }
910
+
911
+ const trackWidth = hasItems
912
+ ? this.effectiveItems.length * L + (this.effectiveItems.length - 1) * this.gap
913
+ : 0;
914
+
915
+ const isScrollable = hasItems && this._scrollContainer &&
916
+ (this._scrollContainer.scrollWidth > this._scrollContainer.clientWidth);
917
+ const showPrevArrow = isScrollable && this._scrollContainer.scrollLeft > 5;
918
+ const showNextArrow = isScrollable &&
919
+ (this._scrollContainer.scrollWidth - this._scrollContainer.scrollLeft - this._scrollContainer.clientWidth > 5);
920
+
921
+ return html`
922
+ <div class="carousel-container" style="
923
+ --carousel-snap-width: ${L}px;
924
+ --carousel-gap: ${this.gap}px;
925
+ --carousel-padding: ${this.padding}px;
926
+ --carousel-right-padding: ${rightPadding}px;
927
+ --carousel-border-radius: ${this.borderRadius}px;
928
+ --carousel-track-width: ${trackWidth}px;
929
+ ">
930
+ <slot @slotchange=${this._handleSlotChange} style="display: none;"></slot>
931
+ ${showHeader
932
+ ? html`
933
+ <div class="carousel-header">
934
+ ${this.headerText ? html`<h3>${this.headerText}</h3>` : ''}
935
+ <slot name="show-all" @slotchange=${this._handleShowAllSlotChange} @click=${this._handleShowAllClick}>
936
+ ${this.showAll
937
+ ? html`
938
+ <a href="#" class="show-all-link">
939
+ ${this.showAllText}
940
+ </a>
941
+ `
942
+ : ''}
943
+ </slot>
944
+ </div>
945
+ `
946
+ : ''}
947
+
948
+ <div class="carousel-viewport">
949
+ ${!this.hideNav
950
+ ? html`
951
+ <button
952
+ class="nav-button prev ${showPrevArrow ? 'visible' : ''}"
953
+ @click=${this._scrollPrevious}
954
+ aria-label="Previous items"
955
+ >
956
+ <span class="material-symbols">chevron_left</span>
957
+ </button>
958
+ `
959
+ : ''}
960
+
961
+ <div
962
+ class="scroll-container"
963
+ @scroll=${this._scheduleLayout}
964
+ @mousedown=${this._handleMouseDown}
965
+ @mousemove=${this._handleMouseMove}
966
+ @mouseup=${this._handleMouseUp}
967
+ @mouseleave=${this._handleMouseUp}
968
+ @touchstart=${this._handleTouchStart}
969
+ @touchmove=${this._handleTouchMove}
970
+ @touchend=${this._handleTouchEnd}
971
+ @wheel=${this._handleWheel}
972
+ >
973
+ <div class="snap-track">
974
+ ${hasItems ? this.effectiveItems.map(() => html`<div class="snap-item"></div>`) : ''}
975
+ </div>
976
+ <div class="visual-track">
977
+ ${hasItems
978
+ ? this.effectiveItems.map(
979
+ (item, idx) => html`
980
+ <div
981
+ class="card"
982
+ @click=${(e: Event) => this._handleCardClick(e, item, idx)}
983
+ >
984
+ ${item.href
985
+ ? html`
986
+ <a href="${item.href}" target="${item.target || '_self'}" style="text-decoration:none; color:inherit;">
987
+ <div class="img-parallax-container">
988
+ <img src="${item.img}" alt="${item.title}" draggable="false" />
989
+ </div>
990
+ <h2 class="card-title">${item.title}</h2>
991
+ </a>
992
+ `
993
+ : html`
994
+ <div class="img-parallax-container">
995
+ <img src="${item.img}" alt="${item.title}" draggable="false" />
996
+ </div>
997
+ <h2 class="card-title">${item.title}</h2>
998
+ `}
999
+ </div>
1000
+ `
1001
+ )
1002
+ : ''}
1003
+ </div>
1004
+ </div>
1005
+
1006
+ ${!this.hideNav
1007
+ ? html`
1008
+ <button
1009
+ class="nav-button next ${showNextArrow ? 'visible' : ''}"
1010
+ @click=${this._scrollNext}
1011
+ aria-label="Next items"
1012
+ >
1013
+ <span class="material-symbols">chevron_right</span>
1014
+ </button>
1015
+ `
1016
+ : ''}
1017
+ </div>
1018
+ </div>
1019
+ `;
1020
+ }
1021
+ }
1022
+
1023
+ declare global {
1024
+ interface HTMLElementTagNameMap {
1025
+ 'moni-carousel': MoniCarousel;
1026
+ }
1027
+ }