@moni-labs/moni-ui 0.2.0 → 0.3.1

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 (323) hide show
  1. package/README.md +52 -194
  2. package/custom-elements.json +1709 -338
  3. package/dist/actions/index.d.ts +6 -0
  4. package/dist/actions/index.d.ts.map +1 -1
  5. package/dist/actions/index.js +6 -0
  6. package/dist/components/_base/field-styles.d.ts +51 -16
  7. package/dist/components/_base/field-styles.d.ts.map +1 -1
  8. package/dist/components/_base/field-styles.js +164 -36
  9. package/dist/components/_base/index.d.ts +25 -0
  10. package/dist/components/_base/index.d.ts.map +1 -1
  11. package/dist/components/_base/index.js +25 -0
  12. package/dist/components/_base/interaction-styles.d.ts +39 -12
  13. package/dist/components/_base/interaction-styles.d.ts.map +1 -1
  14. package/dist/components/_base/interaction-styles.js +85 -33
  15. package/dist/components/_base/moni-element.d.ts +43 -8
  16. package/dist/components/_base/moni-element.d.ts.map +1 -1
  17. package/dist/components/_base/moni-element.js +43 -8
  18. package/dist/components/_base/shared-styles.d.ts +41 -17
  19. package/dist/components/_base/shared-styles.d.ts.map +1 -1
  20. package/dist/components/_base/shared-styles.js +113 -21
  21. package/dist/components/index.d.ts +6 -0
  22. package/dist/components/index.d.ts.map +1 -1
  23. package/dist/components/index.js +6 -0
  24. package/dist/components/loading-shapes.d.ts +6 -0
  25. package/dist/components/loading-shapes.d.ts.map +1 -1
  26. package/dist/components/loading-shapes.js +6 -0
  27. package/dist/components/moni-app-bar.d.ts +128 -33
  28. package/dist/components/moni-app-bar.d.ts.map +1 -1
  29. package/dist/components/moni-app-bar.js +121 -26
  30. package/dist/components/moni-badge.d.ts +122 -14
  31. package/dist/components/moni-badge.d.ts.map +1 -1
  32. package/dist/components/moni-badge.js +122 -14
  33. package/dist/components/moni-bottom-sheet.d.ts +120 -15
  34. package/dist/components/moni-bottom-sheet.d.ts.map +1 -1
  35. package/dist/components/moni-bottom-sheet.js +116 -12
  36. package/dist/components/moni-button-group.d.ts +53 -27
  37. package/dist/components/moni-button-group.d.ts.map +1 -1
  38. package/dist/components/moni-button-group.js +49 -23
  39. package/dist/components/moni-button-segment.d.ts +28 -8
  40. package/dist/components/moni-button-segment.d.ts.map +1 -1
  41. package/dist/components/moni-button-segment.js +27 -7
  42. package/dist/components/moni-button.d.ts +51 -32
  43. package/dist/components/moni-button.d.ts.map +1 -1
  44. package/dist/components/moni-button.js +50 -31
  45. package/dist/components/moni-card.d.ts +91 -31
  46. package/dist/components/moni-card.d.ts.map +1 -1
  47. package/dist/components/moni-card.js +86 -26
  48. package/dist/components/moni-carousel.d.ts +80 -17
  49. package/dist/components/moni-carousel.d.ts.map +1 -1
  50. package/dist/components/moni-carousel.js +258 -34
  51. package/dist/components/moni-checkbox.d.ts +122 -17
  52. package/dist/components/moni-checkbox.d.ts.map +1 -1
  53. package/dist/components/moni-checkbox.js +118 -14
  54. package/dist/components/moni-chip.d.ts +56 -30
  55. package/dist/components/moni-chip.d.ts.map +1 -1
  56. package/dist/components/moni-chip.js +51 -25
  57. package/dist/components/moni-color-field.d.ts +44 -6
  58. package/dist/components/moni-color-field.d.ts.map +1 -1
  59. package/dist/components/moni-color-field.js +43 -5
  60. package/dist/components/moni-context-menu.d.ts +44 -22
  61. package/dist/components/moni-context-menu.d.ts.map +1 -1
  62. package/dist/components/moni-context-menu.js +43 -21
  63. package/dist/components/moni-dialog.d.ts +107 -15
  64. package/dist/components/moni-dialog.d.ts.map +1 -1
  65. package/dist/components/moni-dialog.js +105 -14
  66. package/dist/components/moni-divider.d.ts +50 -15
  67. package/dist/components/moni-divider.d.ts.map +1 -1
  68. package/dist/components/moni-divider.js +49 -14
  69. package/dist/components/moni-expansion.d.ts +44 -8
  70. package/dist/components/moni-expansion.d.ts.map +1 -1
  71. package/dist/components/moni-expansion.js +43 -7
  72. package/dist/components/moni-fab-menu.d.ts +39 -20
  73. package/dist/components/moni-fab-menu.d.ts.map +1 -1
  74. package/dist/components/moni-fab-menu.js +38 -19
  75. package/dist/components/moni-fab.d.ts +49 -23
  76. package/dist/components/moni-fab.d.ts.map +1 -1
  77. package/dist/components/moni-fab.js +46 -20
  78. package/dist/components/moni-file-field.d.ts +54 -14
  79. package/dist/components/moni-file-field.d.ts.map +1 -1
  80. package/dist/components/moni-file-field.js +53 -13
  81. package/dist/components/moni-icon.d.ts +78 -11
  82. package/dist/components/moni-icon.d.ts.map +1 -1
  83. package/dist/components/moni-icon.js +77 -10
  84. package/dist/components/moni-list-item.d.ts +61 -30
  85. package/dist/components/moni-list-item.d.ts.map +1 -1
  86. package/dist/components/moni-list-item.js +55 -24
  87. package/dist/components/moni-list.d.ts +37 -13
  88. package/dist/components/moni-list.d.ts.map +1 -1
  89. package/dist/components/moni-list.js +36 -12
  90. package/dist/components/moni-loading-indicator.d.ts +38 -11
  91. package/dist/components/moni-loading-indicator.d.ts.map +1 -1
  92. package/dist/components/moni-loading-indicator.js +37 -10
  93. package/dist/components/moni-menu-item.d.ts +31 -8
  94. package/dist/components/moni-menu-item.d.ts.map +1 -1
  95. package/dist/components/moni-menu-item.js +30 -7
  96. package/dist/components/moni-menu.d.ts +58 -33
  97. package/dist/components/moni-menu.d.ts.map +1 -1
  98. package/dist/components/moni-menu.js +51 -26
  99. package/dist/components/moni-morph-modal.d.ts +7 -1
  100. package/dist/components/moni-morph-modal.d.ts.map +1 -1
  101. package/dist/components/moni-morph-modal.js +46 -24
  102. package/dist/components/moni-nav-item.d.ts +50 -10
  103. package/dist/components/moni-nav-item.d.ts.map +1 -1
  104. package/dist/components/moni-nav-item.js +48 -8
  105. package/dist/components/moni-nav.d.ts +57 -22
  106. package/dist/components/moni-nav.d.ts.map +1 -1
  107. package/dist/components/moni-nav.js +53 -18
  108. package/dist/components/moni-progress.d.ts +108 -20
  109. package/dist/components/moni-progress.d.ts.map +1 -1
  110. package/dist/components/moni-progress.js +104 -16
  111. package/dist/components/moni-radio.d.ts +106 -14
  112. package/dist/components/moni-radio.d.ts.map +1 -1
  113. package/dist/components/moni-radio.js +104 -13
  114. package/dist/components/moni-ripple.d.ts +121 -10
  115. package/dist/components/moni-ripple.d.ts.map +1 -1
  116. package/dist/components/moni-ripple.js +120 -9
  117. package/dist/components/moni-segmented-button.d.ts +31 -11
  118. package/dist/components/moni-segmented-button.d.ts.map +1 -1
  119. package/dist/components/moni-segmented-button.js +30 -10
  120. package/dist/components/moni-select-option.d.ts +43 -9
  121. package/dist/components/moni-select-option.d.ts.map +1 -1
  122. package/dist/components/moni-select-option.js +41 -7
  123. package/dist/components/moni-select.d.ts +59 -2
  124. package/dist/components/moni-select.d.ts.map +1 -1
  125. package/dist/components/moni-select.js +58 -1
  126. package/dist/components/moni-shape.d.ts +1 -1
  127. package/dist/components/moni-side-sheet.d.ts +56 -19
  128. package/dist/components/moni-side-sheet.d.ts.map +1 -1
  129. package/dist/components/moni-side-sheet.js +53 -16
  130. package/dist/components/moni-slider.d.ts +56 -25
  131. package/dist/components/moni-slider.d.ts.map +1 -1
  132. package/dist/components/moni-slider.js +55 -24
  133. package/dist/components/moni-snackbar.d.ts +86 -17
  134. package/dist/components/moni-snackbar.d.ts.map +1 -1
  135. package/dist/components/moni-snackbar.js +85 -16
  136. package/dist/components/moni-split-button.d.ts +38 -9
  137. package/dist/components/moni-split-button.d.ts.map +1 -1
  138. package/dist/components/moni-split-button.js +37 -8
  139. package/dist/components/moni-step.d.ts +42 -9
  140. package/dist/components/moni-step.d.ts.map +1 -1
  141. package/dist/components/moni-step.js +41 -8
  142. package/dist/components/moni-stepper.d.ts +43 -6
  143. package/dist/components/moni-stepper.d.ts.map +1 -1
  144. package/dist/components/moni-stepper.js +42 -5
  145. package/dist/components/moni-switch.d.ts +103 -16
  146. package/dist/components/moni-switch.d.ts.map +1 -1
  147. package/dist/components/moni-switch.js +99 -13
  148. package/dist/components/moni-tab.d.ts +35 -8
  149. package/dist/components/moni-tab.d.ts.map +1 -1
  150. package/dist/components/moni-tab.js +34 -7
  151. package/dist/components/moni-tabs.d.ts +51 -13
  152. package/dist/components/moni-tabs.d.ts.map +1 -1
  153. package/dist/components/moni-tabs.js +48 -10
  154. package/dist/components/moni-text-field.d.ts +55 -10
  155. package/dist/components/moni-text-field.d.ts.map +1 -1
  156. package/dist/components/moni-text-field.js +54 -9
  157. package/dist/components/moni-textarea.d.ts +51 -21
  158. package/dist/components/moni-textarea.d.ts.map +1 -1
  159. package/dist/components/moni-textarea.js +48 -18
  160. package/dist/components/moni-time-picker.d.ts +41 -11
  161. package/dist/components/moni-time-picker.d.ts.map +1 -1
  162. package/dist/components/moni-time-picker.js +40 -10
  163. package/dist/components/moni-toolbar.d.ts +43 -15
  164. package/dist/components/moni-toolbar.d.ts.map +1 -1
  165. package/dist/components/moni-toolbar.js +42 -14
  166. package/dist/components/moni-tooltip.d.ts +55 -25
  167. package/dist/components/moni-tooltip.d.ts.map +1 -1
  168. package/dist/components/moni-tooltip.js +54 -24
  169. package/dist/components/moni-typography.d.ts +43 -18
  170. package/dist/components/moni-typography.d.ts.map +1 -1
  171. package/dist/components/moni-typography.js +42 -17
  172. package/dist/index.d.ts +47 -0
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +59 -2
  175. package/dist/styles/tailwind.css +67 -0
  176. package/dist/styles/tokens.css +111 -99
  177. package/dist/utils/color.d.ts +181 -2
  178. package/dist/utils/color.d.ts.map +1 -1
  179. package/dist/utils/color.js +182 -4
  180. package/dist/utils/theme.svelte.d.ts +305 -2
  181. package/dist/utils/theme.svelte.d.ts.map +1 -1
  182. package/dist/utils/theme.svelte.js +331 -2
  183. package/dist/web-components.d.ts +28 -0
  184. package/dist/web-components.d.ts.map +1 -1
  185. package/dist/web-components.js +29 -2
  186. package/package.json +1 -1
  187. package/src/actions/index.ts +7 -0
  188. package/src/components/_base/field-styles.ts +165 -37
  189. package/src/components/_base/index.ts +27 -0
  190. package/src/components/_base/interaction-styles.ts +86 -33
  191. package/src/components/_base/moni-element.ts +44 -8
  192. package/src/components/_base/shared-styles.ts +114 -21
  193. package/src/components/index.ts +7 -0
  194. package/src/components/loading-shapes.ts +7 -0
  195. package/src/components/moni-app-bar.ts +127 -26
  196. package/src/components/moni-badge.ts +128 -14
  197. package/src/components/moni-bottom-sheet.ts +125 -13
  198. package/src/components/moni-button-group.ts +50 -23
  199. package/src/components/moni-button-segment.ts +28 -7
  200. package/src/components/moni-button.ts +51 -31
  201. package/src/components/moni-card.ts +90 -26
  202. package/src/components/moni-carousel.ts +282 -34
  203. package/src/components/moni-checkbox.ts +125 -14
  204. package/src/components/moni-chip.ts +52 -25
  205. package/src/components/moni-color-field.ts +44 -5
  206. package/src/components/moni-context-menu.ts +44 -21
  207. package/src/components/moni-dialog.ts +111 -14
  208. package/src/components/moni-divider.ts +50 -14
  209. package/src/components/moni-expansion.ts +44 -7
  210. package/src/components/moni-fab-menu.ts +39 -19
  211. package/src/components/moni-fab.ts +47 -20
  212. package/src/components/moni-file-field.ts +54 -13
  213. package/src/components/moni-icon.ts +80 -10
  214. package/src/components/moni-list-item.ts +56 -24
  215. package/src/components/moni-list.ts +37 -12
  216. package/src/components/moni-loading-indicator.ts +38 -10
  217. package/src/components/moni-menu-item.ts +31 -7
  218. package/src/components/moni-menu.ts +52 -26
  219. package/src/components/moni-morph-modal.ts +58 -24
  220. package/src/components/moni-nav-item.ts +49 -8
  221. package/src/components/moni-nav.ts +54 -18
  222. package/src/components/moni-progress.ts +109 -16
  223. package/src/components/moni-radio.ts +111 -13
  224. package/src/components/moni-ripple.ts +126 -9
  225. package/src/components/moni-segmented-button.ts +31 -10
  226. package/src/components/moni-select-option.ts +42 -7
  227. package/src/components/moni-select.ts +79 -1
  228. package/src/components/moni-side-sheet.ts +54 -16
  229. package/src/components/moni-slider.ts +56 -24
  230. package/src/components/moni-snackbar.ts +90 -16
  231. package/src/components/moni-split-button.ts +38 -8
  232. package/src/components/moni-step.ts +42 -8
  233. package/src/components/moni-stepper.ts +43 -5
  234. package/src/components/moni-switch.ts +106 -13
  235. package/src/components/moni-tab.ts +35 -7
  236. package/src/components/moni-tabs.ts +49 -10
  237. package/src/components/moni-text-field.ts +55 -9
  238. package/src/components/moni-textarea.ts +49 -18
  239. package/src/components/moni-time-picker.ts +41 -10
  240. package/src/components/moni-toolbar.ts +43 -14
  241. package/src/components/moni-tooltip.ts +55 -24
  242. package/src/components/moni-typography.ts +43 -17
  243. package/src/index.ts +67 -3
  244. package/src/styles/tailwind.css +67 -0
  245. package/src/styles/tokens.css +111 -99
  246. package/src/types/svelte-runes.d.ts +64 -2
  247. package/src/utils/color.ts +286 -5
  248. package/src/utils/theme.svelte.ts +411 -2
  249. package/src/web-components.ts +31 -2
  250. package/dist/assets/shapes/arch.svg +0 -1
  251. package/dist/assets/shapes/arrow.svg +0 -1
  252. package/dist/assets/shapes/boom.svg +0 -1
  253. package/dist/assets/shapes/burst.svg +0 -1
  254. package/dist/assets/shapes/circle.svg +0 -1
  255. package/dist/assets/shapes/clamshell.svg +0 -1
  256. package/dist/assets/shapes/diamond.svg +0 -1
  257. package/dist/assets/shapes/fan.svg +0 -1
  258. package/dist/assets/shapes/flower.svg +0 -1
  259. package/dist/assets/shapes/gem.svg +0 -1
  260. package/dist/assets/shapes/ghost-ish.svg +0 -1
  261. package/dist/assets/shapes/heart.svg +0 -1
  262. package/dist/assets/shapes/leaf-clover4.svg +0 -1
  263. package/dist/assets/shapes/leaf-clover8.svg +0 -1
  264. package/dist/assets/shapes/loading-indicator.svg +0 -1
  265. package/dist/assets/shapes/oval.svg +0 -1
  266. package/dist/assets/shapes/pentagon.svg +0 -1
  267. package/dist/assets/shapes/pill.svg +0 -1
  268. package/dist/assets/shapes/pixel-circle.svg +0 -1
  269. package/dist/assets/shapes/pixel-triangle.svg +0 -1
  270. package/dist/assets/shapes/puffy-diamond.svg +0 -1
  271. package/dist/assets/shapes/puffy.svg +0 -1
  272. package/dist/assets/shapes/semicircle.svg +0 -1
  273. package/dist/assets/shapes/sided-cookie12.svg +0 -1
  274. package/dist/assets/shapes/sided-cookie4.svg +0 -1
  275. package/dist/assets/shapes/sided-cookie6.svg +0 -1
  276. package/dist/assets/shapes/sided-cookie7.svg +0 -1
  277. package/dist/assets/shapes/sided-cookie9.svg +0 -1
  278. package/dist/assets/shapes/slanted.svg +0 -1
  279. package/dist/assets/shapes/soft-boom.svg +0 -1
  280. package/dist/assets/shapes/soft-burst.svg +0 -1
  281. package/dist/assets/shapes/square.svg +0 -1
  282. package/dist/assets/shapes/sunny.svg +0 -1
  283. package/dist/assets/shapes/triangle.svg +0 -1
  284. package/dist/assets/shapes/very-sunny.svg +0 -1
  285. package/dist/assets/shapes/wavy-circle.svg +0 -1
  286. package/dist/assets/shapes/wavy.svg +0 -1
  287. package/src/assets/shapes/arch.svg +0 -1
  288. package/src/assets/shapes/arrow.svg +0 -1
  289. package/src/assets/shapes/boom.svg +0 -1
  290. package/src/assets/shapes/burst.svg +0 -1
  291. package/src/assets/shapes/circle.svg +0 -1
  292. package/src/assets/shapes/clamshell.svg +0 -1
  293. package/src/assets/shapes/diamond.svg +0 -1
  294. package/src/assets/shapes/fan.svg +0 -1
  295. package/src/assets/shapes/flower.svg +0 -1
  296. package/src/assets/shapes/gem.svg +0 -1
  297. package/src/assets/shapes/ghost-ish.svg +0 -1
  298. package/src/assets/shapes/heart.svg +0 -1
  299. package/src/assets/shapes/leaf-clover4.svg +0 -1
  300. package/src/assets/shapes/leaf-clover8.svg +0 -1
  301. package/src/assets/shapes/loading-indicator.svg +0 -1
  302. package/src/assets/shapes/oval.svg +0 -1
  303. package/src/assets/shapes/pentagon.svg +0 -1
  304. package/src/assets/shapes/pill.svg +0 -1
  305. package/src/assets/shapes/pixel-circle.svg +0 -1
  306. package/src/assets/shapes/pixel-triangle.svg +0 -1
  307. package/src/assets/shapes/puffy-diamond.svg +0 -1
  308. package/src/assets/shapes/puffy.svg +0 -1
  309. package/src/assets/shapes/semicircle.svg +0 -1
  310. package/src/assets/shapes/sided-cookie12.svg +0 -1
  311. package/src/assets/shapes/sided-cookie4.svg +0 -1
  312. package/src/assets/shapes/sided-cookie6.svg +0 -1
  313. package/src/assets/shapes/sided-cookie7.svg +0 -1
  314. package/src/assets/shapes/sided-cookie9.svg +0 -1
  315. package/src/assets/shapes/slanted.svg +0 -1
  316. package/src/assets/shapes/soft-boom.svg +0 -1
  317. package/src/assets/shapes/soft-burst.svg +0 -1
  318. package/src/assets/shapes/square.svg +0 -1
  319. package/src/assets/shapes/sunny.svg +0 -1
  320. package/src/assets/shapes/triangle.svg +0 -1
  321. package/src/assets/shapes/very-sunny.svg +0 -1
  322. package/src/assets/shapes/wavy-circle.svg +0 -1
  323. package/src/assets/shapes/wavy.svg +0 -1
@@ -1,34 +1,85 @@
1
+ /**
2
+ * @file components/moni-carousel.ts
3
+ * @package @moni-labs/moni-ui
4
+ * @license MIT
5
+ * @contributors Moni Labs & Contributors
6
+ */
7
+
1
8
  import { html, css } from 'lit';
2
9
  import { customElement, property, query, state, queryAll } from 'lit/decorators.js';
3
10
  import { MoniElement, sharedStyles } from './_base/index.js';
4
11
  import { gsap } from 'gsap';
5
12
 
13
+ /**
14
+ * Data model for a single item in a `<moni-carousel>`.
15
+ */
6
16
  export interface CarouselItem {
17
+ /** Display title overlaid on the item. */
7
18
  title: string;
19
+ /** URL or path to the background image. */
8
20
  img: string;
21
+ /** Optional link target. If provided, the item renders as an `<a>` element. */
9
22
  href?: string;
23
+ /** Link target attribute (e.g. `'_blank'`). Only applies if `href` is set. */
10
24
  target?: string;
11
25
  }
12
26
 
13
27
  /**
14
28
  * 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
29
  *
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.
30
+ * Carousels display a collection of related items in a scrollable, horizontal list.
31
+ * They allow users to quickly browse through items like images, cards, or products.
32
+ *
33
+ * **M3 spec reference:** `m3-docs/components/carousel/specs.md`
34
+ *
35
+ * **Layout variants:**
36
+ * - `multi-browse` (default) Shows a mix of large, medium, and small (peeking)
37
+ * items. Best for exploring a large number of items.
38
+ * - `hero` Focuses on one large primary item while showing a sliver of the
39
+ * next item. Best for featuring important content.
40
+ * - `uncontained` Standard layout where all items have the same width and
41
+ * bleed off the edges of the container.
42
+ *
43
+ * **Animation & Gestures:**
44
+ * This component uses GSAP for smooth drag, flick, and snap animations,
45
+ * mirroring the high-fidelity M3 Expressive motion specs. It handles touch
46
+ * gestures for mobile and mouse-drag for desktop.
47
+ *
48
+ * **Auto-sizing (`auto` mode):**
49
+ * When `auto=true` (default), the carousel measures its own width and
50
+ * dynamically calculates the optimal sizes for large, medium, and small items
51
+ * based on the active `layout` to ensure they fit perfectly without awkward gaps
52
+ * or clipping at the edges.
53
+ *
54
+ * @example
55
+ * ```html
56
+ * <!-- Declarative usage via DOM properties (recommended) -->
57
+ * <moni-carousel layout="hero"></moni-carousel>
58
+ * <script>
59
+ * const carousel = document.querySelector('moni-carousel');
60
+ * carousel.items = [
61
+ * { title: 'Item 1', img: '/img1.jpg', href: '/link1' },
62
+ * { title: 'Item 2', img: '/img2.jpg' }
63
+ * ];
64
+ * </script>
65
+ *
66
+ * <!-- Slot-based usage (for SSR or simple static content) -->
67
+ * <moni-carousel layout="uncontained">
68
+ * <div slot="item">
69
+ * <img src="/img1.jpg" />
70
+ * <h3>Static Item</h3>
71
+ * </div>
72
+ * </moni-carousel>
73
+ * ```
74
+ *
75
+ * @slot item - Alternative to the `items` property. Slot individual HTML elements
76
+ * instead of passing data objects.
77
+ *
78
+ * @csspart carousel - The outer wrapper.
79
+ * @csspart track - The scrolling track element.
80
+ * @csspart item - Individual carousel item containers.
81
+ * @csspart img - The image elements inside the items.
82
+ * @csspart title - The title text elements inside the items.
32
83
  */
33
84
  @customElement('moni-carousel')
34
85
  export class MoniCarousel extends MoniElement {
@@ -45,6 +96,9 @@ export class MoniCarousel extends MoniElement {
45
96
  @property({ attribute: 'show-all-text' }) showAllText = 'Show all';
46
97
  @property({ attribute: 'header-text' }) headerText = '';
47
98
  @property({ type: Boolean, attribute: 'hide-nav' }) hideNav = false;
99
+ @property({ type: Boolean, reflect: true }) infinite = false;
100
+ @property({ type: Boolean, reflect: true }) autoplay = false;
101
+ @property({ type: Number, attribute: 'autoplay-interval' }) autoplayInterval = 3000;
48
102
 
49
103
  @state() private _containerWidth = 0;
50
104
  @state() private _slottedItems: CarouselItem[] = [];
@@ -73,6 +127,10 @@ export class MoniCarousel extends MoniElement {
73
127
  private _tickerCurrent = 0;
74
128
  private _isTicking = false;
75
129
 
130
+ // Infinite & Autoplay state
131
+ private _infiniteInitialized = false;
132
+ private _autoplayTimer: any = null;
133
+
76
134
  // quickSetter caches — avoid gsap.set() overhead per frame
77
135
  private _cardSetX: ((value: number) => void)[] = [];
78
136
  private _cardSetWidth: ((value: number) => void)[] = [];
@@ -361,6 +419,10 @@ export class MoniCarousel extends MoniElement {
361
419
  }
362
420
  this._buildQuickSetters();
363
421
  this.updateLayout(true);
422
+
423
+ if (this.autoplay) {
424
+ this._startAutoplay();
425
+ }
364
426
  }
365
427
 
366
428
  override updated(changedProperties: Map<string, any>) {
@@ -374,16 +436,32 @@ export class MoniCarousel extends MoniElement {
374
436
  changedProperties.has('padding') ||
375
437
  changedProperties.has('items') ||
376
438
  changedProperties.has('hideNav') ||
439
+ changedProperties.has('infinite') ||
440
+ changedProperties.has('autoplay') ||
441
+ changedProperties.has('autoplayInterval') ||
377
442
  changedProperties.has('_containerWidth') ||
378
443
  changedProperties.has('_slottedItems') ||
379
444
  changedProperties.has('_hasSlottedShowAll')
380
445
  ) {
446
+ if (changedProperties.has('infinite') || changedProperties.has('items') || changedProperties.has('_slottedItems')) {
447
+ this._infiniteInitialized = false;
448
+ }
449
+
381
450
  if (changedProperties.has('layout') && this._scrollContainer) {
382
451
  this._scrollContainer.scrollLeft = 0;
452
+ this._infiniteInitialized = false;
383
453
  this._scrollContainer.style.scrollSnapType =
384
454
  this.layout === 'uncontained' ? 'none' : 'x mandatory';
385
455
  }
386
456
  this.updateLayout(true);
457
+
458
+ if (changedProperties.has('autoplay') || changedProperties.has('autoplayInterval') || changedProperties.has('items') || changedProperties.has('_slottedItems')) {
459
+ if (this.autoplay) {
460
+ this._startAutoplay();
461
+ } else {
462
+ this._stopAutoplay();
463
+ }
464
+ }
387
465
  }
388
466
  }
389
467
 
@@ -586,6 +664,7 @@ export class MoniCarousel extends MoniElement {
586
664
  this._tickerCurrent = this._tickerTarget;
587
665
  gsap.ticker.remove(this._tick);
588
666
  this._isTicking = false;
667
+ this._checkInfiniteWrap();
589
668
  }
590
669
 
591
670
  this._applyLayout(this._tickerCurrent);
@@ -608,6 +687,34 @@ export class MoniCarousel extends MoniElement {
608
687
  this._buildQuickSetters();
609
688
  }
610
689
 
690
+ if (this.infinite && !this._infiniteInitialized && this.effectiveItems.length > 0) {
691
+ const C = this.effectiveItems.length;
692
+ const V = C * this._visualCardsMultiplier;
693
+ const K = this._snapSetsCount;
694
+ const centerSetIndex = Math.floor(K / 2);
695
+
696
+ // Wait for the browser to render the new items so scrollWidth is updated
697
+ // A double-rAF or setTimeout ensures the layout engine has processed the new snap-items
698
+ requestAnimationFrame(() => {
699
+ setTimeout(() => {
700
+ if (this._scrollContainer) {
701
+ // Force layout calculation
702
+ this._scrollContainer.scrollWidth;
703
+ this._scrollContainer.scrollLeft = centerSetIndex * V * this.itemSize;
704
+
705
+ // If the browser still clamped it, it means it's not ready.
706
+ // But with setTimeout it should be fully ready.
707
+
708
+ // Force the visual ticker to match instantly to avoid an animation zoom from 0
709
+ this._tickerTarget = this._scrollContainer.scrollLeft / this.itemSize;
710
+ this._tickerCurrent = this._tickerTarget;
711
+ this._applyLayout(this._tickerCurrent);
712
+ }
713
+ }, 20);
714
+ });
715
+ this._infiniteInitialized = true;
716
+ }
717
+
611
718
  this._tickerTarget = this._scrollContainer.scrollLeft / this.itemSize;
612
719
 
613
720
  if (instant || (!this._isTicking && this._tickerCurrent === 0)) {
@@ -625,7 +732,21 @@ export class MoniCarousel extends MoniElement {
625
732
  const isMobile = this._isMobile;
626
733
  const cardCount = this._cards.length;
627
734
  for (let i = 0; i < cardCount; i++) {
628
- const p = i - t;
735
+ let p = i - t;
736
+ if (this.infinite) {
737
+ // Modulo wrap so the cards perfectly loop around the center
738
+ p = i - (t % cardCount);
739
+ if (p > cardCount / 2) p -= cardCount;
740
+ if (p < -cardCount / 2) p += cardCount;
741
+ }
742
+
743
+ // Optimization: if the card is far off-screen, hide it to save rendering work
744
+ if (p < -15 || p > 15) {
745
+ this._cardSetX[i](-9999);
746
+ this._titleSetOpacity[i](0);
747
+ continue;
748
+ }
749
+
629
750
  const layout = this._getCardLayout(p);
630
751
 
631
752
  // Use quickSetters — ~3x faster than gsap.set() per call
@@ -642,8 +763,112 @@ export class MoniCarousel extends MoniElement {
642
763
  }
643
764
  }
644
765
 
766
+ private get _visualCardsMultiplier() {
767
+ if (!this.infinite || this.effectiveItems.length === 0) return 1;
768
+ const C = this.effectiveItems.length;
769
+ // We need at least ~15 cards to fill the screen seamlessly for small datasets
770
+ return Math.max(1, Math.ceil(15 / C));
771
+ }
772
+
773
+ private get _snapSetsCount() {
774
+ if (!this.infinite || this.effectiveItems.length === 0) return 1;
775
+ const C = this.effectiveItems.length;
776
+ const V = C * this._visualCardsMultiplier;
777
+ // We want enough snap items so the user NEVER reaches the end in a single fast swipe.
778
+ // A total of ~1000 items is extremely cheap as empty divs, and provides a massive buffer.
779
+ let K = Math.ceil(1000 / V);
780
+ if (K % 2 === 0) K += 1; // Keep it odd for a perfect center
781
+ return Math.max(3, K);
782
+ }
783
+
784
+ private get _snapItemsCount() {
785
+ if (this.infinite) {
786
+ const C = this.effectiveItems.length;
787
+ const V_mult = this._visualCardsMultiplier;
788
+ const K = this._snapSetsCount;
789
+ return K * (C * V_mult);
790
+ }
791
+ return this.effectiveItems.length;
792
+ }
793
+
794
+ private _checkInfiniteWrap() {
795
+ if (!this.infinite || this.effectiveItems.length === 0 || !this._scrollContainer) return;
796
+
797
+ const C = this.effectiveItems.length;
798
+ const V = C * this._visualCardsMultiplier;
799
+ const K = this._snapSetsCount;
800
+ const centerSetIndex = Math.floor(K / 2);
801
+
802
+ // The scroll track is divided into K sets, each containing V items.
803
+ const setWidth = V * this.itemSize;
804
+
805
+ const currentScroll = this._scrollContainer.scrollLeft;
806
+ const currentSet = Math.floor(currentScroll / setWidth);
807
+
808
+ if (currentSet !== centerSetIndex) {
809
+ const delta = (centerSetIndex - currentSet) * setWidth;
810
+
811
+ const oldSnap = this._scrollContainer.style.scrollSnapType;
812
+ const oldBehavior = this._scrollContainer.style.scrollBehavior;
813
+
814
+ // Temporarily disable snap and smooth behavior so the wrap is instantaneous
815
+ this._scrollContainer.style.scrollSnapType = 'none';
816
+ this._scrollContainer.style.scrollBehavior = 'auto';
817
+
818
+ this._scrollContainer.scrollLeft += delta;
819
+
820
+ // INSTANTLY update the GSAP visual ticker so it doesn't animate the jump
821
+ this._tickerTarget = this._scrollContainer.scrollLeft / this.itemSize;
822
+ this._tickerCurrent = this._tickerTarget;
823
+ this._applyLayout(this._tickerCurrent);
824
+
825
+ // Restore original scroll behavior
826
+ this._scrollContainer.style.scrollBehavior = oldBehavior;
827
+
828
+ if (this._isDown) {
829
+ this._scrollLeftStart += delta;
830
+ }
831
+
832
+ if (oldSnap && oldSnap !== 'none') {
833
+ // Re-enable snap on the next frame to avoid jumping
834
+ requestAnimationFrame(() => {
835
+ if (this._scrollContainer && !this._isDown) {
836
+ this._scrollContainer.style.scrollSnapType = oldSnap;
837
+ }
838
+ });
839
+ }
840
+ }
841
+ }
842
+
843
+ private _startAutoplay() {
844
+ this._stopAutoplay();
845
+ if (this.autoplay && this.effectiveItems.length > 0) {
846
+ this._autoplayTimer = setInterval(() => {
847
+ // We removed _isInteracting to make autoplay more robust. It only pauses on active drag.
848
+ if (!this._isDown) {
849
+ this._scrollNext();
850
+ }
851
+ }, this.autoplayInterval) as unknown as number;
852
+ }
853
+ }
854
+
855
+ private _stopAutoplay() {
856
+ if (this._autoplayTimer) {
857
+ clearInterval(this._autoplayTimer);
858
+ this._autoplayTimer = null;
859
+ }
860
+ }
861
+
862
+ private _handleMouseEnter() {
863
+ }
864
+
865
+ private _handleMouseLeave() {
866
+ this._handleMouseUp();
867
+ }
868
+
645
869
  private _handleMouseDown(e: MouseEvent) {
646
870
  gsap.killTweensOf(this._scrollContainer);
871
+ this._checkInfiniteWrap();
647
872
  this._isDown = true;
648
873
  this._draggedDistance = 0;
649
874
  this._scrollContainer.style.scrollBehavior = 'auto';
@@ -685,13 +910,22 @@ export class MoniCarousel extends MoniElement {
685
910
 
686
911
  // Dynamic momentum: stronger swipes have a much higher multiplier
687
912
  const momentumMultiplier = 350 + Math.abs(this._velocity) * 300;
688
- const projectedScrollLeft = this._scrollContainer.scrollLeft - this._velocity * momentumMultiplier;
913
+ let projectedScrollLeft = this._scrollContainer.scrollLeft - this._velocity * momentumMultiplier;
914
+
915
+ // Cap momentum throw to prevent hitting infinite wrap edges on fast swipes
916
+ const maxThrowDist = 15 * this.itemSize;
917
+ if (projectedScrollLeft < this._scrollContainer.scrollLeft - maxThrowDist) {
918
+ projectedScrollLeft = this._scrollContainer.scrollLeft - maxThrowDist;
919
+ } else if (projectedScrollLeft > this._scrollContainer.scrollLeft + maxThrowDist) {
920
+ projectedScrollLeft = this._scrollContainer.scrollLeft + maxThrowDist;
921
+ }
689
922
 
690
923
  if (this.layout !== 'uncontained') {
691
924
  this._scrollContainer.style.scrollSnapType = 'none';
692
925
 
693
926
  let targetIndex = Math.round(projectedScrollLeft / this.itemSize);
694
- targetIndex = Math.max(0, Math.min(this.effectiveItems.length - 1, targetIndex));
927
+ const maxIndex = this.effectiveItems.length > 0 ? this._snapItemsCount - 1 : 0;
928
+ targetIndex = Math.max(0, Math.min(maxIndex, targetIndex));
695
929
  const snapPoint = targetIndex * this.itemSize;
696
930
 
697
931
  // Dynamic duration: longer distance = more time, max 0.85s for faster feeling
@@ -716,6 +950,7 @@ export class MoniCarousel extends MoniElement {
716
950
 
717
951
  private _handleTouchStart(e: TouchEvent) {
718
952
  gsap.killTweensOf(this._scrollContainer);
953
+ this._checkInfiniteWrap();
719
954
  this._isDown = true;
720
955
  this._draggedDistance = 0;
721
956
  this._scrollContainer.style.scrollBehavior = 'auto';
@@ -780,7 +1015,7 @@ export class MoniCarousel extends MoniElement {
780
1015
  // Restablecer el snap después de un pequeño retraso
781
1016
  if ((this as any)._wheelTimeout) clearTimeout((this as any)._wheelTimeout);
782
1017
  (this as any)._wheelTimeout = setTimeout(() => {
783
- if (this.layout !== 'uncontained') {
1018
+ if (this.layout !== 'uncontained' && !this._isDown) {
784
1019
  this._scrollContainer.style.scrollSnapType = 'x mandatory';
785
1020
  }
786
1021
  }, 150);
@@ -794,9 +1029,10 @@ export class MoniCarousel extends MoniElement {
794
1029
  return;
795
1030
  }
796
1031
 
1032
+ const originalIndex = index % this.effectiveItems.length;
797
1033
  this.dispatchEvent(
798
1034
  new CustomEvent('item-click', {
799
- detail: { item, index },
1035
+ detail: { item, index: originalIndex },
800
1036
  bubbles: true,
801
1037
  composed: true
802
1038
  })
@@ -804,14 +1040,13 @@ export class MoniCarousel extends MoniElement {
804
1040
  }
805
1041
 
806
1042
  private _scrollPrevious() {
807
- const target = Math.max(0, this._scrollContainer.scrollLeft - this.itemSize);
1043
+ const target = this._scrollContainer.scrollLeft - this.itemSize;
808
1044
  this._scrollContainer.style.scrollBehavior = 'smooth';
809
1045
  this._scrollContainer.scrollTo({ left: target, behavior: 'smooth' });
810
1046
  }
811
1047
 
812
1048
  private _scrollNext() {
813
- const maxScroll = this._scrollContainer.scrollWidth - this._scrollContainer.clientWidth;
814
- const target = Math.min(maxScroll, this._scrollContainer.scrollLeft + this.itemSize);
1049
+ const target = this._scrollContainer.scrollLeft + this.itemSize;
815
1050
  this._scrollContainer.style.scrollBehavior = 'smooth';
816
1051
  this._scrollContainer.scrollTo({ left: target, behavior: 'smooth' });
817
1052
  }
@@ -908,15 +1143,27 @@ export class MoniCarousel extends MoniElement {
908
1143
  rightPadding = minRightPaddingNeeded;
909
1144
  }
910
1145
 
1146
+ let snapItemsCount = hasItems ? this._snapItemsCount : 0;
1147
+ let visualItems = this.effectiveItems;
1148
+
1149
+ if (this.infinite && hasItems) {
1150
+ const V_mult = this._visualCardsMultiplier;
1151
+
1152
+ visualItems = [];
1153
+ for (let i = 0; i < V_mult; i++) {
1154
+ visualItems = visualItems.concat(this.effectiveItems);
1155
+ }
1156
+ }
1157
+
911
1158
  const trackWidth = hasItems
912
- ? this.effectiveItems.length * L + (this.effectiveItems.length - 1) * this.gap
1159
+ ? snapItemsCount * L + (snapItemsCount - 1) * this.gap
913
1160
  : 0;
914
1161
 
915
1162
  const isScrollable = hasItems && this._scrollContainer &&
916
1163
  (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);
1164
+ const showPrevArrow = this.infinite || (isScrollable && this._scrollContainer.scrollLeft > 5);
1165
+ const showNextArrow = this.infinite || (isScrollable &&
1166
+ (this._scrollContainer.scrollWidth - this._scrollContainer.scrollLeft - this._scrollContainer.clientWidth > 5));
920
1167
 
921
1168
  return html`
922
1169
  <div class="carousel-container" style="
@@ -964,18 +1211,19 @@ export class MoniCarousel extends MoniElement {
964
1211
  @mousedown=${this._handleMouseDown}
965
1212
  @mousemove=${this._handleMouseMove}
966
1213
  @mouseup=${this._handleMouseUp}
967
- @mouseleave=${this._handleMouseUp}
1214
+ @mouseenter=${this._handleMouseEnter}
1215
+ @mouseleave=${this._handleMouseLeave}
968
1216
  @touchstart=${this._handleTouchStart}
969
1217
  @touchmove=${this._handleTouchMove}
970
1218
  @touchend=${this._handleTouchEnd}
971
1219
  @wheel=${this._handleWheel}
972
1220
  >
973
1221
  <div class="snap-track">
974
- ${hasItems ? this.effectiveItems.map(() => html`<div class="snap-item"></div>`) : ''}
1222
+ ${hasItems ? Array.from({ length: snapItemsCount }).map(() => html`<div class="snap-item"></div>`) : ''}
975
1223
  </div>
976
1224
  <div class="visual-track">
977
1225
  ${hasItems
978
- ? this.effectiveItems.map(
1226
+ ? visualItems.map(
979
1227
  (item, idx) => html`
980
1228
  <div
981
1229
  class="card"
@@ -983,16 +1231,16 @@ export class MoniCarousel extends MoniElement {
983
1231
  >
984
1232
  ${item.href
985
1233
  ? html`
986
- <a href="${item.href}" target="${item.target || '_self'}" style="text-decoration:none; color:inherit;">
1234
+ <a href="${item.href}" target="${item.target || '_self'}" style="text-decoration:none; color:inherit;" draggable="false">
987
1235
  <div class="img-parallax-container">
988
- <img src="${item.img}" alt="${item.title}" draggable="false" />
1236
+ <img src="${item.img}" alt="${item.title}" draggable="false" loading="lazy" />
989
1237
  </div>
990
1238
  <h2 class="card-title">${item.title}</h2>
991
1239
  </a>
992
1240
  `
993
1241
  : html`
994
1242
  <div class="img-parallax-container">
995
- <img src="${item.img}" alt="${item.title}" draggable="false" />
1243
+ <img src="${item.img}" alt="${item.title}" draggable="false" loading="lazy" />
996
1244
  </div>
997
1245
  <h2 class="card-title">${item.title}</h2>
998
1246
  `}
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @file components/moni-checkbox.ts
3
+ * @package @moni-labs/moni-ui
4
+ * @license MIT
5
+ * @contributors Moni Labs & Contributors
6
+ */
7
+
1
8
  import { html, css } from 'lit';
2
9
  import { customElement, property, query } from 'lit/decorators.js';
3
10
  import { ifDefined } from 'lit/directives/if-defined.js';
@@ -5,35 +12,122 @@ import { live } from 'lit/directives/live.js';
5
12
  import { MoniElement, sharedStyles } from './_base/index.js';
6
13
 
7
14
  /**
8
- * Checkbox that faithfully ports BeerCSS's `.checkbox` styles.
15
+ * Material Design 3 Checkbox component.
16
+ *
17
+ * Checkboxes allow users to select one or more items from a set, or toggle
18
+ * a single binary option. They are visual-only shells — the consumer is
19
+ * responsible for wiring up form submission and validation logic.
20
+ *
21
+ * **Visual architecture (BeerCSS pattern):**
22
+ * The native `<input type="checkbox">` occupies real layout space (16×16 minimum)
23
+ * but is visually hidden via `opacity: 0`. A `<span>` sibling rendered after
24
+ * the input holds two pseudo-elements:
25
+ * - `::before` — the visible checkbox icon (Material Symbols ligature).
26
+ * - `::after` — the hover/focus state layer ripple ring.
27
+ *
28
+ * The `::before` content switches between:
29
+ * - `'check_box_outline_blank'` (unchecked)
30
+ * - `'check_box'` (checked)
31
+ * - `'indeterminate_check_box'` (native indeterminate state)
32
+ *
33
+ * **Form integration:**
34
+ * Setting `name` and `value` passes them to the native `<input>` element,
35
+ * enabling participation in HTML form submissions.
9
36
  *
10
- * BeerCSS architecture:
11
- * - `.checkbox` is inline-flex, aligns items center
12
- * - `input` has width/height = --_size, opacity: 0 (occupies real space, no absolute!)
13
- * - `span::before` is the visible checkbox box (position: absolute, inset from the input)
14
- * - `span::after` is the ripple hover effect
15
- * - Content is set via CSS: "check_box_outline_blank" / "check_box" (Material Symbols font)
37
+ * @fires change - Bubbles and is composed. Fired when the checkbox is toggled.
38
+ * The consumer can read `element.checked` for the new state.
16
39
  *
17
- * Attributes:
18
- * - label: text label
19
- * - checked: present input is checked
20
- * - disabled: present → input is disabled
21
- * - size: small | medium (default) | large | extra
22
- * - name: forwarded to input.name
23
- * - value: forwarded to input.value
40
+ * @example
41
+ * ```html
42
+ * <moni-checkbox label="Accept terms" name="terms" value="yes"></moni-checkbox>
43
+ *
44
+ * <script>
45
+ * document.querySelector('moni-checkbox').addEventListener('change', (e) => {
46
+ * console.log('checked:', e.target.checked);
47
+ * });
48
+ * </script>
49
+ * ```
50
+ *
51
+ * @csspart checkbox - The outer `<label>` element.
24
52
  */
25
53
  @customElement('moni-checkbox')
26
54
  export class MoniCheckbox extends MoniElement {
55
+ /**
56
+ * Text label displayed to the right of the checkbox icon.
57
+ *
58
+ * When non-empty, the label is rendered as a text node inside the `<span>`.
59
+ * When empty, the default slot is rendered instead, allowing slotted HTML.
60
+ *
61
+ * @default ''
62
+ */
27
63
  @property({ reflect: true }) label = '';
64
+
65
+ /**
66
+ * Whether the checkbox is currently checked.
67
+ *
68
+ * Reflected as an attribute so CSS attribute selectors and external state
69
+ * readers can observe the checked state without accessing the JS property.
70
+ * Synced to the native input via `updated()`.
71
+ *
72
+ * @default false
73
+ */
28
74
  @property({ type: Boolean, reflect: true }) checked = false;
75
+
76
+ /**
77
+ * When `true`, the native input is disabled: the checkbox is not interactive
78
+ * and renders at 50% opacity.
79
+ *
80
+ * @default false
81
+ */
29
82
  @property({ type: Boolean, reflect: true }) disabled = false;
83
+
84
+ /**
85
+ * Visual size of the checkbox icon.
86
+ *
87
+ * Maps to the `--_size` custom property which controls both the invisible
88
+ * input's hit area and the visible `::before` icon size.
89
+ *
90
+ * | Value | `--_size` |
91
+ * |------------|-----------|
92
+ * | `'small'` | 1rem |
93
+ * | `'medium'` | 1.5rem |
94
+ * | `'large'` | 2rem |
95
+ * | `'extra'` | 2.5rem |
96
+ *
97
+ * @default 'medium'
98
+ */
30
99
  @property({ reflect: true })
31
100
  size: 'small' | 'medium' | 'large' | 'extra' = 'medium';
101
+
102
+ /**
103
+ * Forwarded to the native `<input name>` attribute.
104
+ * Required for grouping checkboxes within a form.
105
+ *
106
+ * @default ''
107
+ */
32
108
  @property({ reflect: true }) name = '';
109
+
110
+ /**
111
+ * Forwarded to the native `<input value>` attribute.
112
+ * The value submitted in a form when this checkbox is checked.
113
+ *
114
+ * @default ''
115
+ */
33
116
  @property({ reflect: true }) value = '';
34
117
 
118
+ /** Direct reference to the native input element for programmatic access. */
35
119
  @query('input') private _input!: HTMLInputElement;
36
120
 
121
+ /**
122
+ * Syncs `checked` and `disabled` back to the native input element after
123
+ * Lit's render cycle, ensuring the DOM stays in sync with component state.
124
+ *
125
+ * This is necessary because Lit's `.property=${value}` binding updates the
126
+ * DOM property, but the `live()` directive and direct property assignment
127
+ * are more reliable for boolean inputs across browser implementations.
128
+ *
129
+ * @param changed - Map of changed property names to their previous values.
130
+ */
37
131
  override updated(changed: Map<string, unknown>) {
38
132
  if (this._input) {
39
133
  if (changed.has('checked')) this._input.checked = this.checked;
@@ -159,11 +253,28 @@ export class MoniCheckbox extends MoniElement {
159
253
  `
160
254
  ];
161
255
 
256
+ /**
257
+ * Handles the native input's `change` event.
258
+ *
259
+ * Reads the new checked state from the input and re-dispatches a composed
260
+ * `'change'` event so the signal crosses shadow DOM boundaries and can be
261
+ * heard by parent elements in the light DOM.
262
+ *
263
+ * @param e - The native `change` event from the hidden `<input>`.
264
+ */
162
265
  private _onChange(e: Event) {
163
266
  this.checked = (e.target as HTMLInputElement).checked;
164
267
  this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
165
268
  }
166
269
 
270
+ /**
271
+ * Renders the accessible checkbox label structure.
272
+ *
273
+ * The `<label>` wraps the hidden `<input>` and a `<span>` so that clicking
274
+ * anywhere on the label area (including the text) toggles the checkbox.
275
+ * The `has-label` class on the span adds left padding to separate the icon
276
+ * from the label text.
277
+ */
167
278
  override render() {
168
279
  return html`<label part="checkbox">
169
280
  <input