microui-wc 0.1.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 (609) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +33 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
  4. package/.github/workflows/ci.yml +42 -0
  5. package/.github/workflows/deploy-pages.yml +112 -0
  6. package/AGENTS.md +2366 -0
  7. package/CHANGELOG.md +47 -0
  8. package/CODE_OF_CONDUCT.md +59 -0
  9. package/CONTRIBUTING.md +156 -0
  10. package/LICENSE +190 -0
  11. package/README.md +254 -0
  12. package/SECURITY.md +58 -0
  13. package/app/.generated/routes/alerts.js +8 -0
  14. package/app/.generated/routes/avatars.js +8 -0
  15. package/app/.generated/routes/badges.js +8 -0
  16. package/app/.generated/routes/buttons.js +10 -0
  17. package/app/.generated/routes/cards.js +10 -0
  18. package/app/.generated/routes/checkboxes.js +9 -0
  19. package/app/.generated/routes/chips.js +8 -0
  20. package/app/.generated/routes/dropdowns.js +9 -0
  21. package/app/.generated/routes/home.js +7 -0
  22. package/app/.generated/routes/icons.js +9 -0
  23. package/app/.generated/routes/inputs.js +10 -0
  24. package/app/.generated/routes/installation.js +7 -0
  25. package/app/.generated/routes/layout.js +9 -0
  26. package/app/.generated/routes/modals.js +9 -0
  27. package/app/.generated/routes/navbar.js +7 -0
  28. package/app/.generated/routes/progress.js +9 -0
  29. package/app/.generated/routes/radios.js +9 -0
  30. package/app/.generated/routes/switches.js +9 -0
  31. package/app/.generated/routes/tabs.js +8 -0
  32. package/app/.generated/routes/toasts.js +9 -0
  33. package/app/index.html +67 -0
  34. package/app/pages/alerts.html +23 -0
  35. package/app/pages/avatars.html +22 -0
  36. package/app/pages/badges.html +22 -0
  37. package/app/pages/buttons.html +71 -0
  38. package/app/pages/cards.html +54 -0
  39. package/app/pages/checkboxes.html +39 -0
  40. package/app/pages/chips.html +23 -0
  41. package/app/pages/dropdowns.html +41 -0
  42. package/app/pages/home.html +59 -0
  43. package/app/pages/icons.html +29 -0
  44. package/app/pages/inputs.html +66 -0
  45. package/app/pages/installation.html +34 -0
  46. package/app/pages/layout.html +30 -0
  47. package/app/pages/modals.html +21 -0
  48. package/app/pages/navbar.html +22 -0
  49. package/app/pages/progress.html +35 -0
  50. package/app/pages/radios.html +40 -0
  51. package/app/pages/switches.html +39 -0
  52. package/app/pages/tabs.html +30 -0
  53. package/app/pages/toasts.html +22 -0
  54. package/app-dist/index.html +67 -0
  55. package/app-dist/pages/alerts.html +23 -0
  56. package/app-dist/pages/avatars.html +22 -0
  57. package/app-dist/pages/badges.html +22 -0
  58. package/app-dist/pages/buttons.html +71 -0
  59. package/app-dist/pages/cards.html +54 -0
  60. package/app-dist/pages/checkboxes.html +39 -0
  61. package/app-dist/pages/chips.html +23 -0
  62. package/app-dist/pages/dropdowns.html +41 -0
  63. package/app-dist/pages/home.html +59 -0
  64. package/app-dist/pages/icons.html +29 -0
  65. package/app-dist/pages/inputs.html +66 -0
  66. package/app-dist/pages/installation.html +34 -0
  67. package/app-dist/pages/layout.html +30 -0
  68. package/app-dist/pages/modals.html +21 -0
  69. package/app-dist/pages/navbar.html +22 -0
  70. package/app-dist/pages/progress.html +35 -0
  71. package/app-dist/pages/radios.html +40 -0
  72. package/app-dist/pages/switches.html +39 -0
  73. package/app-dist/pages/tabs.html +30 -0
  74. package/app-dist/pages/toasts.html +22 -0
  75. package/app-dist/pages.json +217 -0
  76. package/app-dist/routes/alerts.js +5 -0
  77. package/app-dist/routes/avatars.js +1 -0
  78. package/app-dist/routes/badges.js +1 -0
  79. package/app-dist/routes/buttons.js +1 -0
  80. package/app-dist/routes/cards.js +1 -0
  81. package/app-dist/routes/checkboxes.js +9 -0
  82. package/app-dist/routes/chips.js +4 -0
  83. package/app-dist/routes/chunk-019e5e2f.js +5 -0
  84. package/app-dist/routes/chunk-0m4j19yd.js +2 -0
  85. package/app-dist/routes/chunk-0tmmp5q0.js +1 -0
  86. package/app-dist/routes/chunk-10xn709r.js +1 -0
  87. package/app-dist/routes/chunk-15m2qcda.js +2 -0
  88. package/app-dist/routes/chunk-1bh8g23n.js +1 -0
  89. package/app-dist/routes/chunk-1vg0v937.js +1 -0
  90. package/app-dist/routes/chunk-1zvcgy3j.js +1 -0
  91. package/app-dist/routes/chunk-2afb0861.js +1 -0
  92. package/app-dist/routes/chunk-2c6ttpzt.js +5 -0
  93. package/app-dist/routes/chunk-3dy30fhs.js +1 -0
  94. package/app-dist/routes/chunk-426dnces.js +13 -0
  95. package/app-dist/routes/chunk-44kgxery.js +1 -0
  96. package/app-dist/routes/chunk-47fdnejd.js +33 -0
  97. package/app-dist/routes/chunk-49a6t2vq.js +1 -0
  98. package/app-dist/routes/chunk-4fe1rm5b.js +1 -0
  99. package/app-dist/routes/chunk-4ggmvkta.js +33 -0
  100. package/app-dist/routes/chunk-4vkz81q7.js +33 -0
  101. package/app-dist/routes/chunk-4w4tmj8f.js +31 -0
  102. package/app-dist/routes/chunk-532s62kr.js +31 -0
  103. package/app-dist/routes/chunk-5hm3bssy.js +33 -0
  104. package/app-dist/routes/chunk-5vrh24hc.js +1 -0
  105. package/app-dist/routes/chunk-61pcg25a.js +1 -0
  106. package/app-dist/routes/chunk-6nfhygvf.js +1 -0
  107. package/app-dist/routes/chunk-700e7je6.js +33 -0
  108. package/app-dist/routes/chunk-7fsn17kg.js +1 -0
  109. package/app-dist/routes/chunk-7k789b32.js +1 -0
  110. package/app-dist/routes/chunk-7r46q0ys.js +36 -0
  111. package/app-dist/routes/chunk-86fmc1fr.js +5 -0
  112. package/app-dist/routes/chunk-8qth37vw.js +1 -0
  113. package/app-dist/routes/chunk-924wv8n0.js +1 -0
  114. package/app-dist/routes/chunk-9mbhgxk9.js +1 -0
  115. package/app-dist/routes/chunk-a216hyd9.js +1 -0
  116. package/app-dist/routes/chunk-akzxykh9.js +33 -0
  117. package/app-dist/routes/chunk-b3dcvy8c.js +1 -0
  118. package/app-dist/routes/chunk-b74zahz5.js +31 -0
  119. package/app-dist/routes/chunk-bftj53p2.js +5 -0
  120. package/app-dist/routes/chunk-c01hnz3e.js +1 -0
  121. package/app-dist/routes/chunk-d8pvv5km.js +1 -0
  122. package/app-dist/routes/chunk-dev0aezr.js +2 -0
  123. package/app-dist/routes/chunk-dh6vnv0e.js +1 -0
  124. package/app-dist/routes/chunk-dn2cbpva.js +36 -0
  125. package/app-dist/routes/chunk-dvn0my90.js +1 -0
  126. package/app-dist/routes/chunk-dvq8mnve.js +36 -0
  127. package/app-dist/routes/chunk-e8c2gc4d.js +5 -0
  128. package/app-dist/routes/chunk-ejf9ak2x.js +1 -0
  129. package/app-dist/routes/chunk-f083m55s.js +1 -0
  130. package/app-dist/routes/chunk-fnrj28s1.js +31 -0
  131. package/app-dist/routes/chunk-fvg3yjdp.js +31 -0
  132. package/app-dist/routes/chunk-g7k381n1.js +1 -0
  133. package/app-dist/routes/chunk-h01kq2ae.js +13 -0
  134. package/app-dist/routes/chunk-h4dk761v.js +5 -0
  135. package/app-dist/routes/chunk-hmx91z2x.js +5 -0
  136. package/app-dist/routes/chunk-hxbg4m42.js +36 -0
  137. package/app-dist/routes/chunk-jbjnfp2b.js +2 -0
  138. package/app-dist/routes/chunk-jxtz5vv6.js +36 -0
  139. package/app-dist/routes/chunk-jxzcs0ey.js +36 -0
  140. package/app-dist/routes/chunk-kt7wwhcx.js +1 -0
  141. package/app-dist/routes/chunk-kzptszyc.js +33 -0
  142. package/app-dist/routes/chunk-mhgca4w4.js +2 -0
  143. package/app-dist/routes/chunk-mhswxa20.js +1 -0
  144. package/app-dist/routes/chunk-n8zfeex6.js +1 -0
  145. package/app-dist/routes/chunk-pee47b2r.js +1 -0
  146. package/app-dist/routes/chunk-pesmw829.js +1 -0
  147. package/app-dist/routes/chunk-pgc4c6f3.js +36 -0
  148. package/app-dist/routes/chunk-q8egegm1.js +1 -0
  149. package/app-dist/routes/chunk-q9mn2qyq.js +36 -0
  150. package/app-dist/routes/chunk-qh0rtaf3.js +5 -0
  151. package/app-dist/routes/chunk-qqhmk6ye.js +2 -0
  152. package/app-dist/routes/chunk-qrxygmf7.js +33 -0
  153. package/app-dist/routes/chunk-r46yzksx.js +36 -0
  154. package/app-dist/routes/chunk-rgpbw2w0.js +5 -0
  155. package/app-dist/routes/chunk-rnpzv3d8.js +2 -0
  156. package/app-dist/routes/chunk-s5v8cv05.js +2 -0
  157. package/app-dist/routes/chunk-sbwn5bpc.js +1 -0
  158. package/app-dist/routes/chunk-sqbg8jbt.js +33 -0
  159. package/app-dist/routes/chunk-sv8dqnf7.js +1 -0
  160. package/app-dist/routes/chunk-t67sw3za.js +1 -0
  161. package/app-dist/routes/chunk-tjdpqwdf.js +31 -0
  162. package/app-dist/routes/chunk-tq2mfghg.js +1 -0
  163. package/app-dist/routes/chunk-ttn10vt6.js +1 -0
  164. package/app-dist/routes/chunk-v2hzpjxr.js +1 -0
  165. package/app-dist/routes/chunk-wfjjkw9y.js +1 -0
  166. package/app-dist/routes/chunk-wt8cxzmf.js +31 -0
  167. package/app-dist/routes/chunk-x45d372k.js +5 -0
  168. package/app-dist/routes/chunk-y3wsazkt.js +1 -0
  169. package/app-dist/routes/chunk-y7pmgc7t.js +33 -0
  170. package/app-dist/routes/chunk-zefdt2q3.js +31 -0
  171. package/app-dist/routes/dropdowns.js +6 -0
  172. package/app-dist/routes/home.js +1 -0
  173. package/app-dist/routes/icons.js +1 -0
  174. package/app-dist/routes/inputs.js +12 -0
  175. package/app-dist/routes/installation.js +1 -0
  176. package/app-dist/routes/layout.js +1 -0
  177. package/app-dist/routes/modals.js +7 -0
  178. package/app-dist/routes/navbar.js +1 -0
  179. package/app-dist/routes/progress.js +1 -0
  180. package/app-dist/routes/radios.js +6 -0
  181. package/app-dist/routes/switches.js +6 -0
  182. package/app-dist/routes/tabs.js +1 -0
  183. package/app-dist/routes/toasts.js +16 -0
  184. package/assets/fonts/material-symbols-mini.woff2 +0 -0
  185. package/assets/fonts/material-symbols.woff2 +0 -0
  186. package/assets/fonts/roboto-400.woff2 +0 -0
  187. package/assets/fonts/roboto-500.woff2 +0 -0
  188. package/assets/fonts/roboto-700.woff2 +0 -0
  189. package/assets/logo-banner-400.jpg +0 -0
  190. package/assets/logo-banner-400.webp +0 -0
  191. package/assets/logo-banner-800.webp +0 -0
  192. package/assets/logo-banner.jpg +0 -0
  193. package/assets/logo-icon-64.jpg +0 -0
  194. package/assets/logo-icon-64.webp +0 -0
  195. package/assets/logo-icon.jpg +0 -0
  196. package/assets/logo-square.jpg +0 -0
  197. package/bun.lock +312 -0
  198. package/bunfig.toml +4 -0
  199. package/custom-elements.json +1916 -0
  200. package/demo/api/sample-data.json +38 -0
  201. package/demo/content/alerts.html +115 -0
  202. package/demo/content/avatars.html +70 -0
  203. package/demo/content/badges.html +65 -0
  204. package/demo/content/buttons.html +188 -0
  205. package/demo/content/callouts.html +91 -0
  206. package/demo/content/cards.html +121 -0
  207. package/demo/content/checkboxes.html +178 -0
  208. package/demo/content/chips.html +67 -0
  209. package/demo/content/codeblocks.html +101 -0
  210. package/demo/content/confirms.html +115 -0
  211. package/demo/content/datatables.html +149 -0
  212. package/demo/content/dividers.html +119 -0
  213. package/demo/content/dropdowns.html +89 -0
  214. package/demo/content/enterprise.html +252 -0
  215. package/demo/content/home.html +149 -0
  216. package/demo/content/icons.html +89 -0
  217. package/demo/content/inputs.html +135 -0
  218. package/demo/content/installation.html +16 -0
  219. package/demo/content/layout.html +136 -0
  220. package/demo/content/modals.html +141 -0
  221. package/demo/content/navbar.html +70 -0
  222. package/demo/content/progress.html +119 -0
  223. package/demo/content/radios.html +88 -0
  224. package/demo/content/skeletons.html +109 -0
  225. package/demo/content/spinners.html +96 -0
  226. package/demo/content/switches.html +84 -0
  227. package/demo/content/tables.html +124 -0
  228. package/demo/content/tabs.html +85 -0
  229. package/demo/content/toasts.html +116 -0
  230. package/demo/content/tooltips.html +107 -0
  231. package/demo/content/virtual-lists.html +233 -0
  232. package/demo/favicon.ico +0 -0
  233. package/demo/favicon.png +0 -0
  234. package/demo/full.html +52 -0
  235. package/demo/iife.html +46 -0
  236. package/demo/manifest.json +34 -0
  237. package/demo/pages/datatable-demo.html +237 -0
  238. package/demo/pages/prompt-ui-demo.html +218 -0
  239. package/demo/pages/responsive-demo.html +122 -0
  240. package/demo/pages/schema-form-demo.html +270 -0
  241. package/demo/robots.txt +6 -0
  242. package/demo/shell.html +712 -0
  243. package/demo/sw.js +387 -0
  244. package/dist/AGENTS.md +2366 -0
  245. package/dist/README.md +254 -0
  246. package/dist/chunks/advanced.js +174 -0
  247. package/dist/chunks/chunk-1nhr1wrq.js +14 -0
  248. package/dist/chunks/chunk-hssyjbr0.js +2 -0
  249. package/dist/chunks/chunk-k8etzx0z.js +2 -0
  250. package/dist/chunks/chunk-rr1et8fg.js +2 -0
  251. package/dist/chunks/chunk-sjcx4fd5.js +6 -0
  252. package/dist/chunks/chunk-v1c777xh.js +5 -0
  253. package/dist/chunks/chunk-w5k5vwjd.js +13 -0
  254. package/dist/chunks/core.js +10 -0
  255. package/dist/chunks/display.js +17 -0
  256. package/dist/chunks/feedback.js +15 -0
  257. package/dist/chunks/forms.js +48 -0
  258. package/dist/chunks/layout.js +9 -0
  259. package/dist/components/chunk-4tezav8r.js +2 -0
  260. package/dist/components/chunk-fqyb2pms.js +2 -0
  261. package/dist/components/chunk-h7cdbhxw.js +13 -0
  262. package/dist/components/chunk-mzd8jwrs.js +2 -0
  263. package/dist/components/chunk-qwmxyn8e.js +2 -0
  264. package/dist/components/chunk-redtk47a.js +14 -0
  265. package/dist/components/mu-alert.js +5 -0
  266. package/dist/components/mu-api-table.js +33 -0
  267. package/dist/components/mu-avatar.js +1 -0
  268. package/dist/components/mu-badge.js +1 -0
  269. package/dist/components/mu-bottom-nav.js +1 -0
  270. package/dist/components/mu-button.js +1 -0
  271. package/dist/components/mu-callout.js +1 -0
  272. package/dist/components/mu-card.js +1 -0
  273. package/dist/components/mu-checkbox.js +9 -0
  274. package/dist/components/mu-chip.js +4 -0
  275. package/dist/components/mu-code.js +48 -0
  276. package/dist/components/mu-confirm.js +10 -0
  277. package/dist/components/mu-container.js +1 -0
  278. package/dist/components/mu-datatable.js +96 -0
  279. package/dist/components/mu-divider.js +1 -0
  280. package/dist/components/mu-doc-page.js +26 -0
  281. package/dist/components/mu-drawer-item.js +9 -0
  282. package/dist/components/mu-drawer.js +1 -0
  283. package/dist/components/mu-dropdown.js +6 -0
  284. package/dist/components/mu-error-boundary.js +10 -0
  285. package/dist/components/mu-example.js +38 -0
  286. package/dist/components/mu-fetch.js +1 -0
  287. package/dist/components/mu-form.js +1 -0
  288. package/dist/components/mu-grid.js +1 -0
  289. package/dist/components/mu-icon.js +5 -0
  290. package/dist/components/mu-input.js +12 -0
  291. package/dist/components/mu-layout.js +1 -0
  292. package/dist/components/mu-lazy.js +1 -0
  293. package/dist/components/mu-modal.js +7 -0
  294. package/dist/components/mu-navbar.js +1 -0
  295. package/dist/components/mu-page.js +1 -0
  296. package/dist/components/mu-progress.js +1 -0
  297. package/dist/components/mu-prompt-ui.js +20 -0
  298. package/dist/components/mu-radio.js +6 -0
  299. package/dist/components/mu-repeat.js +1 -0
  300. package/dist/components/mu-router.js +6 -0
  301. package/dist/components/mu-schema-form.js +76 -0
  302. package/dist/components/mu-sidebar.js +1 -0
  303. package/dist/components/mu-skeleton.js +13 -0
  304. package/dist/components/mu-spinner.js +1 -0
  305. package/dist/components/mu-stack.js +1 -0
  306. package/dist/components/mu-switch.js +6 -0
  307. package/dist/components/mu-table.js +1 -0
  308. package/dist/components/mu-tabs.js +1 -0
  309. package/dist/components/mu-textarea.js +11 -0
  310. package/dist/components/mu-theme-toggle.js +5 -0
  311. package/dist/components/mu-toast.js +4 -0
  312. package/dist/components/mu-tooltip.js +10 -0
  313. package/dist/components/mu-virtual-list.js +33 -0
  314. package/dist/components.css +1 -0
  315. package/dist/microui.css +1 -0
  316. package/dist/microui.d.ts +234 -0
  317. package/dist/microui.esm.js +549 -0
  318. package/dist/microui.esm.js.map +79 -0
  319. package/dist/microui.min.js +549 -0
  320. package/dist/microui.min.js.map +79 -0
  321. package/dist/routes/alerts.js +1 -0
  322. package/dist/routes/avatars.js +1 -0
  323. package/dist/routes/badges.js +1 -0
  324. package/dist/routes/buttons.js +1 -0
  325. package/dist/routes/callouts.js +1 -0
  326. package/dist/routes/cards.js +1 -0
  327. package/dist/routes/checkboxes.js +9 -0
  328. package/dist/routes/chips.js +4 -0
  329. package/dist/routes/chunk-19wgcncm.js +2 -0
  330. package/dist/routes/chunk-1khyr3v1.js +33 -0
  331. package/dist/routes/chunk-4rhxe97g.js +1 -0
  332. package/dist/routes/chunk-5qah04bh.js +2 -0
  333. package/dist/routes/chunk-7gfxy70n.js +5 -0
  334. package/dist/routes/chunk-e86zbeta.js +1 -0
  335. package/dist/routes/chunk-fagt36h6.js +2 -0
  336. package/dist/routes/chunk-fed7zr7m.js +1 -0
  337. package/dist/routes/chunk-hwj7pfwp.js +1 -0
  338. package/dist/routes/chunk-mhvcs2f8.js +5 -0
  339. package/dist/routes/chunk-nv3bddmj.js +13 -0
  340. package/dist/routes/chunk-q3f2aeqe.js +7 -0
  341. package/dist/routes/chunk-qxxa8trk.js +1 -0
  342. package/dist/routes/chunk-rw15y9zh.js +1 -0
  343. package/dist/routes/chunk-sfb7x11v.js +5 -0
  344. package/dist/routes/chunk-swyhghrm.js +48 -0
  345. package/dist/routes/chunk-sxddjs2d.js +2 -0
  346. package/dist/routes/chunk-vby0zg5w.js +17 -0
  347. package/dist/routes/chunk-w6zqjqqs.js +9 -0
  348. package/dist/routes/chunk-z960rexd.js +38 -0
  349. package/dist/routes/codeblocks.js +1 -0
  350. package/dist/routes/confirms.js +10 -0
  351. package/dist/routes/datatables.js +96 -0
  352. package/dist/routes/dividers.js +1 -0
  353. package/dist/routes/dropdowns.js +6 -0
  354. package/dist/routes/enterprise.js +15 -0
  355. package/dist/routes/home.js +1 -0
  356. package/dist/routes/icons.js +1 -0
  357. package/dist/routes/inputs.js +22 -0
  358. package/dist/routes/installation.js +1 -0
  359. package/dist/routes/layout.js +1 -0
  360. package/dist/routes/modals.js +1 -0
  361. package/dist/routes/navbar.js +1 -0
  362. package/dist/routes/page-components.json +316 -0
  363. package/dist/routes/progress.js +1 -0
  364. package/dist/routes/radios.js +6 -0
  365. package/dist/routes/route-deps.json +156 -0
  366. package/dist/routes/shell-critical.js +1 -0
  367. package/dist/routes/shell-deferred.js +1 -0
  368. package/dist/routes/shell.js +20 -0
  369. package/dist/routes/skeletons.js +13 -0
  370. package/dist/routes/spinners.js +1 -0
  371. package/dist/routes/src/chunks/core.js +36 -0
  372. package/dist/routes/switches.js +6 -0
  373. package/dist/routes/tables.js +1 -0
  374. package/dist/routes/tabs.js +1 -0
  375. package/dist/routes/toasts.js +1 -0
  376. package/dist/routes/tooltips.js +10 -0
  377. package/dist/routes/virtual-lists.js +33 -0
  378. package/dist/styles/common.css +1 -0
  379. package/dist/styles/components/animations.css +1 -0
  380. package/dist/styles/components/avatar.css +1 -0
  381. package/dist/styles/components/badge.css +1 -0
  382. package/dist/styles/components/bottom-nav.css +1 -0
  383. package/dist/styles/components/button.css +1 -0
  384. package/dist/styles/components/card.css +1 -0
  385. package/dist/styles/components/checkbox.css +1 -0
  386. package/dist/styles/components/chip.css +1 -0
  387. package/dist/styles/components/datatable.css +1 -0
  388. package/dist/styles/components/divider.css +1 -0
  389. package/dist/styles/components/drawer-item.css +1 -0
  390. package/dist/styles/components/drawer.css +1 -0
  391. package/dist/styles/components/grid.css +1 -0
  392. package/dist/styles/components/icon.css +1 -0
  393. package/dist/styles/components/input.css +1 -0
  394. package/dist/styles/components/layout.css +1 -0
  395. package/dist/styles/components/navbar.css +1 -0
  396. package/dist/styles/components/overlays.css +1 -0
  397. package/dist/styles/components/progress.css +1 -0
  398. package/dist/styles/components/prompt-ui.css +1 -0
  399. package/dist/styles/components/radio.css +1 -0
  400. package/dist/styles/components/schema-form.css +1 -0
  401. package/dist/styles/components/switch.css +1 -0
  402. package/dist/styles/components/tabs.css +1 -0
  403. package/dist/styles/components/tooltip.css +1 -0
  404. package/dist/styles/components/virtual-list.css +1 -0
  405. package/dist/tokens.css +1 -0
  406. package/docs/api-reference.md +175 -0
  407. package/docs/component-schema.md +231 -0
  408. package/docs/components.md +269 -0
  409. package/docs/design-system.md +183 -0
  410. package/docs/getting-started.md +198 -0
  411. package/docs/message-protocol.md +262 -0
  412. package/docs/utility-classes.md +205 -0
  413. package/lighthouse-audit.mjs +113 -0
  414. package/package.json +45 -0
  415. package/scripts/analyze-components.js +105 -0
  416. package/scripts/build-app.js +193 -0
  417. package/scripts/build-framework.js +444 -0
  418. package/scripts/build-utils.js +101 -0
  419. package/scripts/test-isolated.js +151 -0
  420. package/server.js +256 -0
  421. package/src/chunks/advanced.js +27 -0
  422. package/src/chunks/core.js +61 -0
  423. package/src/chunks/display.js +25 -0
  424. package/src/chunks/feedback.js +15 -0
  425. package/src/chunks/forms.js +25 -0
  426. package/src/chunks/layout.js +27 -0
  427. package/src/components/mu-alert.js +96 -0
  428. package/src/components/mu-api-table.js +167 -0
  429. package/src/components/mu-avatar.js +94 -0
  430. package/src/components/mu-badge.js +32 -0
  431. package/src/components/mu-bottom-nav.js +115 -0
  432. package/src/components/mu-button.js +61 -0
  433. package/src/components/mu-callout.js +71 -0
  434. package/src/components/mu-card.js +36 -0
  435. package/src/components/mu-checkbox.js +186 -0
  436. package/src/components/mu-chip.js +125 -0
  437. package/src/components/mu-code.js +534 -0
  438. package/src/components/mu-confirm.js +268 -0
  439. package/src/components/mu-container.js +53 -0
  440. package/src/components/mu-datatable.js +517 -0
  441. package/src/components/mu-divider.js +40 -0
  442. package/src/components/mu-doc-page.js +100 -0
  443. package/src/components/mu-drawer-item.js +158 -0
  444. package/src/components/mu-drawer.js +305 -0
  445. package/src/components/mu-dropdown.js +239 -0
  446. package/src/components/mu-error-boundary.js +191 -0
  447. package/src/components/mu-example.js +335 -0
  448. package/src/components/mu-fetch.js +256 -0
  449. package/src/components/mu-form.js +133 -0
  450. package/src/components/mu-grid.js +63 -0
  451. package/src/components/mu-icon.js +211 -0
  452. package/src/components/mu-input.js +142 -0
  453. package/src/components/mu-layout.js +129 -0
  454. package/src/components/mu-lazy.js +94 -0
  455. package/src/components/mu-modal.js +160 -0
  456. package/src/components/mu-navbar.js +71 -0
  457. package/src/components/mu-page.js +77 -0
  458. package/src/components/mu-progress.js +54 -0
  459. package/src/components/mu-prompt-ui.js +382 -0
  460. package/src/components/mu-radio.js +200 -0
  461. package/src/components/mu-repeat.js +135 -0
  462. package/src/components/mu-router.js +169 -0
  463. package/src/components/mu-schema-form.js +441 -0
  464. package/src/components/mu-sidebar.js +81 -0
  465. package/src/components/mu-skeleton.js +69 -0
  466. package/src/components/mu-spinner.js +30 -0
  467. package/src/components/mu-stack.js +59 -0
  468. package/src/components/mu-switch.js +150 -0
  469. package/src/components/mu-table.js +80 -0
  470. package/src/components/mu-tabs.js +112 -0
  471. package/src/components/mu-textarea.js +96 -0
  472. package/src/components/mu-theme-toggle.js +52 -0
  473. package/src/components/mu-toast.js +151 -0
  474. package/src/components/mu-tooltip.js +182 -0
  475. package/src/components/mu-virtual-list.js +184 -0
  476. package/src/core/MuElement.js +562 -0
  477. package/src/core/agent-api.js +771 -0
  478. package/src/core/breakpoints.js +195 -0
  479. package/src/core/bus.js +378 -0
  480. package/src/core/component-schema.js +287 -0
  481. package/src/core/feature-registry.js +241 -0
  482. package/src/core/form-state.js +252 -0
  483. package/src/core/http.js +104 -0
  484. package/src/core/keyboard.js +105 -0
  485. package/src/core/layers.js +71 -0
  486. package/src/core/render.js +201 -0
  487. package/src/core/ripple.js +158 -0
  488. package/src/core/router.js +100 -0
  489. package/src/core/scheduler.js +109 -0
  490. package/src/core/signals.js +164 -0
  491. package/src/core/store.js +268 -0
  492. package/src/core/theme.js +68 -0
  493. package/src/core/transitions.js +72 -0
  494. package/src/core/utils.js +30 -0
  495. package/src/index.d.ts +234 -0
  496. package/src/index.js +308 -0
  497. package/src/styles/animations.css +252 -0
  498. package/src/styles/common.css +82 -0
  499. package/src/styles/components/animations.css +129 -0
  500. package/src/styles/components/avatar.css +83 -0
  501. package/src/styles/components/badge.css +80 -0
  502. package/src/styles/components/bottom-nav.css +37 -0
  503. package/src/styles/components/button.css +348 -0
  504. package/src/styles/components/card.css +138 -0
  505. package/src/styles/components/checkbox.css +201 -0
  506. package/src/styles/components/chip.css +93 -0
  507. package/src/styles/components/datatable.css +180 -0
  508. package/src/styles/components/divider.css +49 -0
  509. package/src/styles/components/drawer-item.css +123 -0
  510. package/src/styles/components/drawer.css +273 -0
  511. package/src/styles/components/grid.css +189 -0
  512. package/src/styles/components/icon.css +40 -0
  513. package/src/styles/components/input.css +203 -0
  514. package/src/styles/components/layout.css +121 -0
  515. package/src/styles/components/navbar.css +91 -0
  516. package/src/styles/components/overlays.css +329 -0
  517. package/src/styles/components/progress.css +79 -0
  518. package/src/styles/components/prompt-ui.css +286 -0
  519. package/src/styles/components/radio.css +17 -0
  520. package/src/styles/components/schema-form.css +85 -0
  521. package/src/styles/components/switch.css +69 -0
  522. package/src/styles/components/tabs.css +145 -0
  523. package/src/styles/components/tooltip.css +93 -0
  524. package/src/styles/components/virtual-list.css +36 -0
  525. package/src/styles/components.css +3677 -0
  526. package/src/styles/routes/home.css +97 -0
  527. package/src/styles/tokens.css +675 -0
  528. package/tests/agents/agent-integration.test.js +76 -0
  529. package/tests/benchmark.html +296 -0
  530. package/tests/build/scan-components.test.js +173 -0
  531. package/tests/components/all-components.test.js +245 -0
  532. package/tests/components/all-missing-components.test.js +574 -0
  533. package/tests/components/mu-alert.test.js +113 -0
  534. package/tests/components/mu-avatar.test.js +148 -0
  535. package/tests/components/mu-badge.test.js +92 -0
  536. package/tests/components/mu-button.test.js +112 -0
  537. package/tests/components/mu-card.test.js +89 -0
  538. package/tests/components/mu-checkbox.test.js +158 -0
  539. package/tests/components/mu-chip.test.js +118 -0
  540. package/tests/components/mu-container.test.js +120 -0
  541. package/tests/components/mu-divider.test.js +98 -0
  542. package/tests/components/mu-drawer-item.test.js +199 -0
  543. package/tests/components/mu-drawer.test.js +96 -0
  544. package/tests/components/mu-dropdown.test.js +125 -0
  545. package/tests/components/mu-form.test.js +138 -0
  546. package/tests/components/mu-grid.test.js +135 -0
  547. package/tests/components/mu-icon.test.js +110 -0
  548. package/tests/components/mu-input.test.js +131 -0
  549. package/tests/components/mu-lazy.test.js +103 -0
  550. package/tests/components/mu-modal.test.js +275 -0
  551. package/tests/components/mu-navbar.test.js +101 -0
  552. package/tests/components/mu-progress.test.js +115 -0
  553. package/tests/components/mu-radio.test.js +114 -0
  554. package/tests/components/mu-repeat.test.js +106 -0
  555. package/tests/components/mu-sidebar.test.js +126 -0
  556. package/tests/components/mu-skeleton.test.js +162 -0
  557. package/tests/components/mu-stack.test.js +143 -0
  558. package/tests/components/mu-switch.test.js +292 -0
  559. package/tests/components/mu-table.test.js +124 -0
  560. package/tests/components/mu-tabs.test.js +104 -0
  561. package/tests/components/mu-textarea.test.js +115 -0
  562. package/tests/components/mu-toast.test.js +321 -0
  563. package/tests/components/mu-tooltip.test.js +133 -0
  564. package/tests/components/mu-virtual-list.test.js +109 -0
  565. package/tests/core/MuElement.test.js +120 -0
  566. package/tests/core/agent-api.test.js +125 -0
  567. package/tests/core/all-core-modules.test.js +442 -0
  568. package/tests/core/bus.test.js +364 -0
  569. package/tests/core/component-schema.test.js +160 -0
  570. package/tests/core/feature-registry.test.js +198 -0
  571. package/tests/core/form-state.test.js +167 -0
  572. package/tests/core/http.test.js +119 -0
  573. package/tests/core/keyboard.test.js +319 -0
  574. package/tests/core/layers.test.js +129 -0
  575. package/tests/core/namespaced-stores.test.js +114 -0
  576. package/tests/core/render.test.js +121 -0
  577. package/tests/core/ripple.test.js +131 -0
  578. package/tests/core/router.test.js +89 -0
  579. package/tests/core/scheduler.test.js +121 -0
  580. package/tests/core/signals.test.js +128 -0
  581. package/tests/core/store.test.js +171 -0
  582. package/tests/core/transitions.test.js +82 -0
  583. package/tests/e2e/accessibility-harness.html +58 -0
  584. package/tests/e2e/accessibility.test.js +401 -0
  585. package/tests/e2e/agent-features.test.js +372 -0
  586. package/tests/e2e/card-spacing.test.js +287 -0
  587. package/tests/e2e/components.test.js +439 -0
  588. package/tests/e2e/demo-routes.test.js +478 -0
  589. package/tests/e2e/layout-css-fallback.test.js +334 -0
  590. package/tests/e2e/mu-alert.e2e.test.js +111 -0
  591. package/tests/e2e/mu-checkbox.test.js +489 -0
  592. package/tests/e2e/mu-chip.test.js +347 -0
  593. package/tests/e2e/mu-form.test.js +499 -0
  594. package/tests/e2e/mu-icon.test.js +114 -0
  595. package/tests/e2e/mu-radio.test.js +113 -0
  596. package/tests/e2e/mu-skeleton.test.js +140 -0
  597. package/tests/e2e/mu-switch.test.js +415 -0
  598. package/tests/e2e/mu-tabs.test.js +494 -0
  599. package/tests/e2e/mu-textarea.test.js +242 -0
  600. package/tests/e2e/mu-virtual-list.test.js +427 -0
  601. package/tests/e2e/perf-memory.test.js +161 -0
  602. package/tests/e2e/puppeteer-helper.js +137 -0
  603. package/tests/e2e/puppeteer.test.js +226 -0
  604. package/tests/e2e/pwa.test.js +261 -0
  605. package/tests/e2e/test-harness.html +319 -0
  606. package/tests/manual/test-components.html +120 -0
  607. package/tests/memory-test.html +309 -0
  608. package/tests/setup-dom.js +93 -0
  609. package/tests/visual-test.html +301 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * @fileoverview Comprehensive E2E Tests for mu-textarea Component
3
+ */
4
+
5
+ import { describe, test, expect, beforeAll, afterEach, afterAll } from 'bun:test';
6
+ import { launchBrowser, puppeteer } from './puppeteer-helper.js';
7
+ import { dirname, join } from 'path';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
13
+
14
+ describe('mu-textarea E2E Tests', () => {
15
+ let browser, page, server;
16
+
17
+ beforeAll(async () => {
18
+ const projectRoot = join(__dirname, '../..');
19
+ server = Bun.serve({
20
+ port: 0,
21
+ async fetch(req) {
22
+ const url = new URL(req.url);
23
+ let filePath = url.pathname === '/' ? join(projectRoot, 'demo/shell.html') : join(projectRoot, url.pathname);
24
+ try {
25
+ const file = Bun.file(filePath);
26
+ if (!await file.exists()) return new Response('Not Found', { status: 404 });
27
+ const ext = filePath.split('.').pop();
28
+ const types = { 'html': 'text/html', 'js': 'text/javascript', 'css': 'text/css', 'json': 'application/json', 'svg': 'image/svg+xml' };
29
+ return new Response(await file.arrayBuffer(), { headers: { 'Content-Type': types[ext] || 'application/octet-stream' } });
30
+ } catch (e) { return new Response('Error', { status: 500 }); }
31
+ }
32
+ });
33
+ console.log(`[mu-textarea test] Server at http://localhost:${server.port}`);
34
+ browser = await puppeteer.launch({
35
+ headless: true,
36
+ executablePath: '/tmp/puppeteer/chrome-headless-shell/mac_arm-144.0.7559.96/chrome-headless-shell-mac-arm64/chrome-headless-shell',
37
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
38
+ userDataDir: `./.tmp/puppeteer-mu_textarea-${Date.now()}`
39
+ });
40
+ page = await browser.newPage();
41
+ }, 60000);
42
+
43
+ afterEach(async () => { await page.goto('about:blank'); });
44
+ afterAll(async () => { if (browser) await browser.close(); if (server) server.stop(); });
45
+
46
+ test('should be registered as custom element', async () => {
47
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
48
+ await delay(500);
49
+ const isRegistered = await page.evaluate(() => customElements.get('mu-textarea') !== undefined);
50
+ expect(isRegistered).toBe(true);
51
+ });
52
+
53
+ test('should render with base class', async () => {
54
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
55
+ await delay(500);
56
+ const hasClass = await page.evaluate(() => document.querySelector('mu-textarea')?.classList.contains('mu-textarea'));
57
+ expect(hasClass).toBe(true);
58
+ });
59
+
60
+ test('should render native textarea element', async () => {
61
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
62
+ await delay(500);
63
+ const hasTextarea = await page.evaluate(() => document.querySelector('mu-textarea textarea') !== null);
64
+ expect(hasTextarea).toBe(true);
65
+ });
66
+
67
+ test('should have placeholder attribute forwarded', async () => {
68
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
69
+ await delay(500);
70
+ const placeholder = await page.evaluate(() => {
71
+ const ta = document.querySelector('mu-textarea[placeholder] textarea');
72
+ return ta?.placeholder;
73
+ });
74
+ expect(placeholder).toBeTruthy();
75
+ });
76
+
77
+ test('should support value getter/setter', async () => {
78
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
79
+ await delay(500);
80
+ const result = await page.evaluate(() => {
81
+ const ta = document.querySelector('mu-textarea');
82
+ const initial = ta.value;
83
+ ta.value = 'Test content';
84
+ const afterSet = ta.value;
85
+ return { initial, afterSet };
86
+ });
87
+ expect(result.afterSet).toBe('Test content');
88
+ });
89
+
90
+ test('should emit mu-input event on input', async () => {
91
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
92
+ await delay(500);
93
+ const result = await page.evaluate(() => {
94
+ return new Promise(resolve => {
95
+ const ta = document.querySelector('mu-textarea');
96
+ let fired = false;
97
+ ta.addEventListener('mu-input', () => { fired = true; });
98
+ const native = ta.querySelector('textarea');
99
+ native.value = 'test';
100
+ native.dispatchEvent(new Event('input', { bubbles: true }));
101
+ setTimeout(() => resolve({ fired }), 100);
102
+ });
103
+ });
104
+ expect(result.fired).toBe(true);
105
+ });
106
+
107
+ test('should emit mu-change event on change', async () => {
108
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
109
+ await delay(500);
110
+ const result = await page.evaluate(() => {
111
+ return new Promise(resolve => {
112
+ const ta = document.querySelector('mu-textarea');
113
+ let fired = false;
114
+ ta.addEventListener('mu-change', () => { fired = true; });
115
+ const native = ta.querySelector('textarea');
116
+ native.value = 'test';
117
+ native.dispatchEvent(new Event('change', { bubbles: true }));
118
+ setTimeout(() => resolve({ fired }), 100);
119
+ });
120
+ });
121
+ expect(result.fired).toBe(true);
122
+ });
123
+
124
+ test('should have display block', async () => {
125
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
126
+ await delay(500);
127
+ const display = await page.evaluate(() => getComputedStyle(document.querySelector('mu-textarea')).display);
128
+ expect(display).toBe('block');
129
+ });
130
+
131
+ test('should have focus method', async () => {
132
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
133
+ await delay(500);
134
+ const hasFocus = await page.evaluate(() => typeof document.querySelector('mu-textarea').focus === 'function');
135
+ expect(hasFocus).toBe(true);
136
+ });
137
+
138
+ // ========================================
139
+ // ROWS ATTRIBUTE (was skipped in unit tests)
140
+ // Note: mu-textarea applies rows at render time only
141
+ // ========================================
142
+
143
+ test('should support rows attribute at render time', async () => {
144
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
145
+ await delay(500);
146
+ const result = await page.evaluate(() => {
147
+ // Create a new mu-textarea with rows attribute
148
+ const container = document.createElement('div');
149
+ container.innerHTML = '<mu-textarea rows="8" placeholder="Test rows"></mu-textarea>';
150
+ document.body.appendChild(container);
151
+
152
+ // Wait for render
153
+ return new Promise(resolve => {
154
+ setTimeout(() => {
155
+ const ta = container.querySelector('mu-textarea');
156
+ const native = ta?.querySelector('textarea');
157
+ resolve({
158
+ hasAttr: ta?.getAttribute('rows') === '8',
159
+ nativeRows: native?.rows || native?.getAttribute('rows'),
160
+ rowsApplied: native?.rows === 8 || native?.getAttribute('rows') === '8'
161
+ });
162
+ }, 200);
163
+ });
164
+ });
165
+ expect(result.hasAttr).toBe(true);
166
+ expect(result.rowsApplied).toBe(true);
167
+ });
168
+
169
+ test('should reflect rows attribute to native textarea', async () => {
170
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
171
+ await delay(500);
172
+ const result = await page.evaluate(() => {
173
+ const ta = document.querySelector('mu-textarea');
174
+ const native = ta.querySelector('textarea');
175
+ return {
176
+ hasTextarea: native !== null,
177
+ defaultRows: native?.rows || null
178
+ };
179
+ });
180
+ expect(result.hasTextarea).toBe(true);
181
+ // Default rows should be a number > 0
182
+ expect(result.defaultRows).toBeGreaterThan(0);
183
+ });
184
+
185
+ // ========================================
186
+ // READONLY ATTRIBUTE (was skipped in unit tests)
187
+ // Note: mu-textarea applies readonly at render time only
188
+ // ========================================
189
+
190
+ test('should support readonly attribute at render time', async () => {
191
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
192
+ await delay(500);
193
+ const result = await page.evaluate(() => {
194
+ // Create a new mu-textarea with readonly attribute
195
+ const container = document.createElement('div');
196
+ container.innerHTML = '<mu-textarea readonly placeholder="Readonly test"></mu-textarea>';
197
+ document.body.appendChild(container);
198
+
199
+ // Wait for render
200
+ return new Promise(resolve => {
201
+ setTimeout(() => {
202
+ const ta = container.querySelector('mu-textarea');
203
+ const native = ta?.querySelector('textarea');
204
+ resolve({
205
+ hasAttr: ta?.hasAttribute('readonly'),
206
+ isReadOnly: native?.readOnly || native?.hasAttribute('readonly')
207
+ });
208
+ }, 200);
209
+ });
210
+ });
211
+ expect(result.hasAttr).toBe(true);
212
+ expect(result.isReadOnly).toBe(true);
213
+ });
214
+
215
+ test('readonly textarea should have readonly native element', async () => {
216
+ await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
217
+ await delay(500);
218
+ const result = await page.evaluate(() => {
219
+ // Find existing textarea with readonly or create one
220
+ let ta = document.querySelector('mu-textarea[readonly]');
221
+ if (!ta) {
222
+ const container = document.createElement('div');
223
+ container.innerHTML = '<mu-textarea readonly placeholder="Test readonly"></mu-textarea>';
224
+ document.body.appendChild(container);
225
+ ta = container.querySelector('mu-textarea');
226
+ }
227
+
228
+ return new Promise(resolve => {
229
+ setTimeout(() => {
230
+ const native = ta?.querySelector('textarea');
231
+ resolve({
232
+ hasNative: native !== null,
233
+ isReadOnly: native?.readOnly || native?.hasAttribute('readonly'),
234
+ hasAttr: ta?.hasAttribute('readonly')
235
+ });
236
+ }, 200);
237
+ });
238
+ });
239
+ expect(result.hasNative).toBe(true);
240
+ expect(result.hasAttr).toBe(true);
241
+ });
242
+ });
@@ -0,0 +1,427 @@
1
+ /**
2
+ * @fileoverview Comprehensive E2E Tests for mu-virtual-list Component
3
+ *
4
+ * Tests:
5
+ * - Component registration and basic rendering
6
+ * - Virtual scrolling with large datasets
7
+ * - Item rendering and visibility
8
+ * - scrollToIndex method
9
+ * - Performance with large lists
10
+ */
11
+
12
+ import { describe, test, expect, beforeAll, afterEach, afterAll } from 'bun:test';
13
+ import { launchBrowser, puppeteer } from './puppeteer-helper.js';
14
+ import { fileURLToPath } from 'url';
15
+ import { dirname, join } from 'path';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+
20
+ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
21
+
22
+ describe('mu-virtual-list E2E Tests', () => {
23
+ let browser;
24
+ let page;
25
+ let server;
26
+
27
+ beforeAll(async () => {
28
+ const projectRoot = join(__dirname, '../..');
29
+
30
+ server = Bun.serve({
31
+ port: 0,
32
+ async fetch(req) {
33
+ const url = new URL(req.url);
34
+ let filePath = join(projectRoot, url.pathname);
35
+
36
+ if (url.pathname === '/') {
37
+ filePath = join(projectRoot, 'demo/shell.html');
38
+ }
39
+
40
+ // Serve the virtual-list test harness
41
+ if (url.pathname === '/test-virtual-list.html') {
42
+ return new Response(`
43
+ <!DOCTYPE html>
44
+ <html>
45
+ <head>
46
+ <title>Virtual List Test</title>
47
+ <script type="module" src="/dist/microui.esm.js"></script>
48
+ <style>
49
+ #test-list {
50
+ height: 400px;
51
+ border: 1px solid #ccc;
52
+ }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <mu-virtual-list id="test-list" item-height="50" buffer="5"></mu-virtual-list>
57
+ <script type="module">
58
+ const list = document.getElementById('test-list');
59
+ // Create 1000 items
60
+ const items = Array.from({ length: 1000 }, (_, i) => ({
61
+ id: i,
62
+ name: 'Item ' + i
63
+ }));
64
+ list.items = items;
65
+ list.renderItem = (item) => '<div class="item">Item ' + item.id + ': ' + item.name + '</div>';
66
+ </script>
67
+ </body>
68
+ </html>
69
+ `, { headers: { 'Content-Type': 'text/html' } });
70
+ }
71
+
72
+ try {
73
+ const file = Bun.file(filePath);
74
+ if (!await file.exists()) {
75
+ return new Response('Not Found', { status: 404 });
76
+ }
77
+
78
+ const content = await file.arrayBuffer();
79
+ const ext = filePath.split('.').pop();
80
+ const mimeTypes = {
81
+ 'html': 'text/html',
82
+ 'js': 'text/javascript',
83
+ 'css': 'text/css',
84
+ 'json': 'application/json',
85
+ 'svg': 'image/svg+xml'
86
+ };
87
+
88
+ return new Response(content, {
89
+ headers: { 'Content-Type': mimeTypes[ext] || 'application/octet-stream' }
90
+ });
91
+ } catch (e) {
92
+ return new Response('Error: ' + e.message, { status: 500 });
93
+ }
94
+ }
95
+ });
96
+
97
+ console.log(`[mu-virtual-list test] Server started at http://localhost:${server.port}`);
98
+
99
+ browser = await puppeteer.launch({
100
+ headless: true,
101
+ executablePath: '/tmp/puppeteer/chrome-headless-shell/mac_arm-144.0.7559.96/chrome-headless-shell-mac-arm64/chrome-headless-shell',
102
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
103
+ userDataDir: `./.tmp/puppeteer-mu_virtual_list-${Date.now()}-${Math.random().toString(36).slice(2)}`
104
+ });
105
+ page = await browser.newPage();
106
+ }, 60000);
107
+
108
+ afterEach(async () => {
109
+ await page.goto('about:blank');
110
+ });
111
+
112
+ afterAll(async () => {
113
+ if (browser) await browser.close();
114
+ if (server) server.stop();
115
+ });
116
+
117
+ // ========================================
118
+ // COMPONENT REGISTRATION
119
+ // ========================================
120
+
121
+ test('should be registered as custom element', async () => {
122
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
123
+ waitUntil: 'networkidle0'
124
+ });
125
+ await delay(500);
126
+
127
+ const isRegistered = await page.evaluate(() =>
128
+ customElements.get('mu-virtual-list') !== undefined
129
+ );
130
+
131
+ expect(isRegistered).toBe(true);
132
+ });
133
+
134
+ test('should render with base class', async () => {
135
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
136
+ waitUntil: 'networkidle0'
137
+ });
138
+ await delay(500);
139
+
140
+ const hasBaseClass = await page.evaluate(() => {
141
+ const el = document.querySelector('mu-virtual-list');
142
+ return el?.classList.contains('mu-virtual-list');
143
+ });
144
+
145
+ expect(hasBaseClass).toBe(true);
146
+ });
147
+
148
+ // ========================================
149
+ // VIEWPORT AND CONTAINER STRUCTURE
150
+ // ========================================
151
+
152
+ test('should render viewport container', async () => {
153
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
154
+ waitUntil: 'networkidle0'
155
+ });
156
+ await delay(500);
157
+
158
+ const result = await page.evaluate(() => {
159
+ const el = document.querySelector('mu-virtual-list');
160
+ const viewport = el?.querySelector('.mu-virtual-list__viewport');
161
+ return {
162
+ hasViewport: viewport !== null,
163
+ overflowY: viewport ? getComputedStyle(viewport).overflowY : null
164
+ };
165
+ });
166
+
167
+ expect(result.hasViewport).toBe(true);
168
+ expect(result.overflowY).toBe('auto');
169
+ });
170
+
171
+ test('should render content container with correct total height', async () => {
172
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
173
+ waitUntil: 'networkidle0'
174
+ });
175
+ await delay(500);
176
+
177
+ const result = await page.evaluate(() => {
178
+ const el = document.querySelector('mu-virtual-list');
179
+ const content = el?.querySelector('.mu-virtual-list__content');
180
+ const height = content ? parseInt(content.style.height) : 0;
181
+ // 1000 items * 50px = 50000px
182
+ return { height, expected: 1000 * 50 };
183
+ });
184
+
185
+ expect(result.height).toBe(result.expected);
186
+ });
187
+
188
+ // ========================================
189
+ // VIRTUAL RENDERING
190
+ // ========================================
191
+
192
+ test('should only render visible items (virtual scrolling)', async () => {
193
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
194
+ waitUntil: 'networkidle0'
195
+ });
196
+ await delay(500);
197
+
198
+ const result = await page.evaluate(() => {
199
+ const el = document.querySelector('mu-virtual-list');
200
+ const items = el?.querySelectorAll('.mu-virtual-list__item');
201
+ // With 400px height, 50px items, and buffer of 5, should have ~18-20 items
202
+ return {
203
+ renderedCount: items?.length || 0,
204
+ totalItems: 1000
205
+ };
206
+ });
207
+
208
+ // Should NOT render all 1000 items
209
+ expect(result.renderedCount).toBeLessThan(50);
210
+ expect(result.renderedCount).toBeGreaterThan(0);
211
+ });
212
+
213
+ test('should render items with correct positioning', async () => {
214
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
215
+ waitUntil: 'networkidle0'
216
+ });
217
+ await delay(500);
218
+
219
+ const result = await page.evaluate(() => {
220
+ const el = document.querySelector('mu-virtual-list');
221
+ const items = el?.querySelectorAll('.mu-virtual-list__item');
222
+ if (!items || items.length === 0) return { error: 'No items' };
223
+
224
+ const firstItem = items[0];
225
+ const top = parseInt(firstItem.style.top);
226
+ const height = parseInt(firstItem.style.height);
227
+ const position = getComputedStyle(firstItem).position;
228
+
229
+ return { top, height, position };
230
+ });
231
+
232
+ expect(result.position).toBe('absolute');
233
+ expect(result.height).toBe(50);
234
+ expect(result.top).toBe(0); // First visible item starts at 0
235
+ });
236
+
237
+ // ========================================
238
+ // SCROLL BEHAVIOR
239
+ // ========================================
240
+
241
+ test('should update visible items on scroll', async () => {
242
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
243
+ waitUntil: 'networkidle0'
244
+ });
245
+ await delay(500);
246
+
247
+ const result = await page.evaluate(async () => {
248
+ const el = document.querySelector('mu-virtual-list');
249
+ const viewport = el.querySelector('.mu-virtual-list__viewport');
250
+
251
+ // Get first visible item before scroll
252
+ const beforeItems = el.querySelectorAll('.mu-virtual-list__item');
253
+ const beforeIndex = parseInt(beforeItems[0]?.getAttribute('data-index') || '0');
254
+
255
+ // Scroll down 500px (10 items)
256
+ viewport.scrollTop = 500;
257
+
258
+ // Wait for throttled scroll handler (16ms) + render time
259
+ await new Promise(r => setTimeout(r, 150));
260
+
261
+ const afterItems = el.querySelectorAll('.mu-virtual-list__item');
262
+ const afterIndex = parseInt(afterItems[0]?.getAttribute('data-index') || '0');
263
+
264
+ return { beforeIndex, afterIndex, scrolled: afterIndex > beforeIndex };
265
+ });
266
+
267
+ expect(result.scrolled).toBe(true);
268
+ expect(result.afterIndex).toBeGreaterThan(result.beforeIndex);
269
+ });
270
+
271
+ // ========================================
272
+ // scrollToIndex METHOD
273
+ // ========================================
274
+
275
+ test('should have scrollToIndex method', async () => {
276
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
277
+ waitUntil: 'networkidle0'
278
+ });
279
+ await delay(500);
280
+
281
+ const hasMethod = await page.evaluate(() => {
282
+ const el = document.querySelector('mu-virtual-list');
283
+ return typeof el?.scrollToIndex === 'function';
284
+ });
285
+
286
+ expect(hasMethod).toBe(true);
287
+ });
288
+
289
+ test('scrollToIndex should scroll to correct position', async () => {
290
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
291
+ waitUntil: 'networkidle0'
292
+ });
293
+ await delay(500);
294
+
295
+ const result = await page.evaluate(async () => {
296
+ const el = document.querySelector('mu-virtual-list');
297
+ const viewport = el.querySelector('.mu-virtual-list__viewport');
298
+
299
+ el.scrollToIndex(100);
300
+
301
+ // Wait for scroll
302
+ await new Promise(r => setTimeout(r, 100));
303
+
304
+ const scrollTop = viewport.scrollTop;
305
+ const expectedScrollTop = 100 * 50; // index * itemHeight
306
+
307
+ return { scrollTop, expectedScrollTop };
308
+ });
309
+
310
+ expect(result.scrollTop).toBe(result.expectedScrollTop);
311
+ });
312
+
313
+ // ========================================
314
+ // ITEMS PROPERTY
315
+ // ========================================
316
+
317
+ test('should have items getter and setter', async () => {
318
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
319
+ waitUntil: 'networkidle0'
320
+ });
321
+ await delay(500);
322
+
323
+ const result = await page.evaluate(() => {
324
+ const el = document.querySelector('mu-virtual-list');
325
+ const items = el.items;
326
+ return {
327
+ isArray: Array.isArray(items),
328
+ length: items?.length
329
+ };
330
+ });
331
+
332
+ expect(result.isArray).toBe(true);
333
+ expect(result.length).toBe(1000);
334
+ });
335
+
336
+ test('should update when items are changed', async () => {
337
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
338
+ waitUntil: 'networkidle0'
339
+ });
340
+ await delay(500);
341
+
342
+ const result = await page.evaluate(async () => {
343
+ const el = document.querySelector('mu-virtual-list');
344
+ const content = el.querySelector('.mu-virtual-list__content');
345
+
346
+ const heightBefore = parseInt(content.style.height);
347
+
348
+ // Set new items array
349
+ el.items = Array.from({ length: 500 }, (_, i) => ({ id: i, name: 'New Item ' + i }));
350
+
351
+ // Wait for render
352
+ await new Promise(r => setTimeout(r, 100));
353
+
354
+ const heightAfter = parseInt(content.style.height);
355
+
356
+ return { heightBefore, heightAfter, changed: heightBefore !== heightAfter };
357
+ });
358
+
359
+ expect(result.changed).toBe(true);
360
+ expect(result.heightAfter).toBe(500 * 50); // 500 items * 50px
361
+ });
362
+
363
+ // ========================================
364
+ // RENDER ITEM CALLBACK
365
+ // ========================================
366
+
367
+ test('should use custom renderItem callback', async () => {
368
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
369
+ waitUntil: 'networkidle0'
370
+ });
371
+ await delay(500);
372
+
373
+ const result = await page.evaluate(() => {
374
+ const el = document.querySelector('mu-virtual-list');
375
+ const item = el.querySelector('.mu-virtual-list__item');
376
+ return {
377
+ hasCustomContent: item !== null,
378
+ text: item?.textContent?.trim()
379
+ };
380
+ });
381
+
382
+ expect(result.hasCustomContent).toBe(true);
383
+ // The item renders the content from renderItem callback
384
+ expect(result.text).toBeTruthy();
385
+ });
386
+
387
+ // ========================================
388
+ // DATA ATTRIBUTES
389
+ // ========================================
390
+
391
+ test('should have data-index attribute on items', async () => {
392
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
393
+ waitUntil: 'networkidle0'
394
+ });
395
+ await delay(500);
396
+
397
+ const result = await page.evaluate(() => {
398
+ const el = document.querySelector('mu-virtual-list');
399
+ const items = el.querySelectorAll('.mu-virtual-list__item');
400
+ const indices = Array.from(items).map(i => parseInt(i.getAttribute('data-index')));
401
+ return {
402
+ hasIndices: indices.length > 0,
403
+ firstIndex: indices[0],
404
+ allHaveIndex: indices.every(i => !isNaN(i))
405
+ };
406
+ });
407
+
408
+ expect(result.hasIndices).toBe(true);
409
+ expect(result.firstIndex).toBe(0);
410
+ expect(result.allHaveIndex).toBe(true);
411
+ });
412
+
413
+ test('should have data-mu-state=visible on rendered items', async () => {
414
+ await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
415
+ waitUntil: 'networkidle0'
416
+ });
417
+ await delay(500);
418
+
419
+ const result = await page.evaluate(() => {
420
+ const el = document.querySelector('mu-virtual-list');
421
+ const items = el.querySelectorAll('.mu-virtual-list__item[data-mu-state="visible"]');
422
+ return { count: items.length };
423
+ });
424
+
425
+ expect(result.count).toBeGreaterThan(0);
426
+ });
427
+ });