@birdapi/velinstyle 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/README.de.md +87 -162
  2. package/README.md +188 -119
  3. package/cli/blueprint.js +98 -5
  4. package/cli/blueprints/app-chrome.html +18 -0
  5. package/cli/blueprints/benefits-grid.html +9 -0
  6. package/cli/blueprints/bottom-nav-mobile.html +2 -1
  7. package/cli/blueprints/cookie-consent.html +1 -1
  8. package/cli/blueprints/cta-band.html +6 -0
  9. package/cli/blueprints/faq-section.html +18 -0
  10. package/cli/blueprints/filter-bar.html +4 -4
  11. package/cli/blueprints/form-contact.html +6 -5
  12. package/cli/blueprints/hero-section.html +3 -3
  13. package/cli/blueprints/notification-center.html +1 -1
  14. package/cli/blueprints/onboarding.html +2 -1
  15. package/cli/blueprints/ops-console.html +29 -0
  16. package/cli/blueprints/pricing-band.html +37 -0
  17. package/cli/blueprints/process-steps.html +10 -0
  18. package/cli/blueprints/services-grid.html +9 -0
  19. package/cli/blueprints/settings-panel.html +8 -6
  20. package/cli/blueprints/split-hero.html +25 -0
  21. package/cli/blueprints/testimonials.html +8 -0
  22. package/cli/blueprints/trust-bar.html +10 -0
  23. package/cli/cli-manifest.json +81 -17
  24. package/cli/contrast-utils.js +97 -0
  25. package/cli/create.js +370 -0
  26. package/cli/docgen/extract-attributes.js +126 -10
  27. package/cli/docgen/extract-cli.js +3 -2
  28. package/cli/docgen/extract-components.js +61 -2
  29. package/cli/docgen/extract-utilities.js +10 -10
  30. package/cli/doctor.js +119 -0
  31. package/cli/index.js +531 -44
  32. package/cli/meta.js +84 -1
  33. package/cli/perf-audit.js +15 -2
  34. package/cli/prompt-engine.js +243 -0
  35. package/cli/review.js +268 -0
  36. package/cli/scaffold.js +119 -4
  37. package/cli/scanner-rules-data.js +7 -0
  38. package/cli/scanner.js +357 -4
  39. package/cli/serve.js +81 -0
  40. package/cli/skills.js +223 -0
  41. package/cli/wc-api.js +89 -0
  42. package/cli/workflow.js +42 -0
  43. package/components/index.js +2 -0
  44. package/components/runtime/component-loaders.js +2 -0
  45. package/components/velin-accordion.js +62 -13
  46. package/components/velin-bottom-nav.js +10 -0
  47. package/components/velin-calendar.js +212 -0
  48. package/components/velin-command.js +18 -2
  49. package/components/velin-data-table.js +78 -1
  50. package/components/velin-dialog.js +4 -1
  51. package/components/velin-drawer.js +2 -4
  52. package/components/velin-file-dropzone.js +162 -0
  53. package/components/velin-icon.js +22 -1
  54. package/components/velin-lightbox.js +5 -3
  55. package/components/velin-modal.js +2 -4
  56. package/components/velin-search.js +1 -1
  57. package/components/velin-sheet.js +2 -4
  58. package/core/a11y/component-contracts.json +20 -3
  59. package/core/meta/build.js +125 -5
  60. package/core/meta/design-constraints/contact.json +9 -0
  61. package/core/meta/design-constraints/faq.json +9 -0
  62. package/core/meta/design-constraints/hero.json +13 -0
  63. package/core/meta/knowledge/components.json +622 -0
  64. package/core/meta/knowledge/tokens.json +282 -0
  65. package/core/meta/pages/registry.json +294 -0
  66. package/core/meta/schema.js +3 -1
  67. package/core/meta/sections/registry.json +185 -0
  68. package/dist/chunks/{attributes-STYCS3O5.js → attributes-A423NX5J.js} +3 -3
  69. package/dist/chunks/{attributes-353XDOAX.js → attributes-F6UPIEVP.js} +1 -1
  70. package/dist/chunks/{attributes-ADOKLKA7.js → attributes-GLYXGGAL.js} +3 -3
  71. package/dist/chunks/{attributes-TPANLMLF.js → attributes-UFTYOSAU.js} +3 -3
  72. package/dist/chunks/attributes-XGLXLR2T.js +391 -0
  73. package/dist/chunks/attributes-YF4NI2QS.js +391 -0
  74. package/dist/chunks/browser-AUABBYUK.js +1544 -0
  75. package/dist/chunks/{chunk-RSMRR27R.js → chunk-2LSKX57P.js} +12 -10
  76. package/dist/chunks/{chunk-IEHKRARD.js → chunk-2TGCVDUL.js} +11 -9
  77. package/dist/chunks/{chunk-SRL6EWHP.js → chunk-5JWGUAPC.js} +17 -13
  78. package/dist/chunks/chunk-7YSO2OQ2.js +113 -0
  79. package/dist/chunks/{chunk-QVHYL3R4.js → chunk-BHR3ZG6U.js} +6 -6
  80. package/dist/chunks/{chunk-J472ERN2.js → chunk-JMSIHEYX.js} +15 -4
  81. package/dist/chunks/{chunk-MKZP2TBH.js → chunk-L7ZGUU4D.js} +2 -2
  82. package/dist/chunks/chunk-TUGIPKW5.js +113 -0
  83. package/dist/chunks/{highlight-GJHHV4KM.js → highlight-M2ELQNYK.js} +1 -1
  84. package/dist/chunks/runtime-entry.js +1 -1
  85. package/dist/chunks/{velin-accordion-FBIPAEXG.js → velin-accordion-WSY6BMLA.js} +64 -10
  86. package/dist/chunks/{velin-bottom-nav-7GEMPIM6.js → velin-bottom-nav-5RVRNGIP.js} +25 -0
  87. package/dist/chunks/{velin-bottom-nav-N3DFQR66.js → velin-bottom-nav-N5F4UO2I.js} +26 -1
  88. package/dist/chunks/velin-calendar-OFE6THUD.js +210 -0
  89. package/dist/chunks/velin-calendar-PGVNJTXK.js +217 -0
  90. package/dist/chunks/velin-calendar-XC7JRDSH.js +217 -0
  91. package/dist/chunks/{velin-code-block-CIAURDRJ.js → velin-code-block-ZGWG7JI5.js} +1 -1
  92. package/dist/chunks/{velin-combobox-NXS4QB4R.js → velin-combobox-56V2ZWT5.js} +1 -1
  93. package/dist/chunks/{velin-command-ONKO2A3J.js → velin-command-2HO2EKR6.js} +29 -2
  94. package/dist/chunks/{velin-command-TMLSRGIU.js → velin-command-ZJSLPK7B.js} +30 -3
  95. package/dist/chunks/{velin-copy-NYDBOKXT.js → velin-copy-XDQSV2XU.js} +1 -1
  96. package/dist/chunks/{velin-countdown-4ZVDGK4R.js → velin-countdown-V4Z5MA63.js} +1 -1
  97. package/dist/chunks/velin-data-table-UVR42UUM.js +372 -0
  98. package/dist/chunks/{velin-dialog-GTTZI564.js → velin-dialog-3YK2RPRV.js} +9 -2
  99. package/dist/chunks/{velin-dialog-ELFOZIXZ.js → velin-dialog-S24KAP4S.js} +3 -1
  100. package/dist/chunks/{velin-drawer-BE6TYN3H.js → velin-drawer-5PNU624J.js} +2 -4
  101. package/dist/chunks/{velin-drawer-DLL2GLT6.js → velin-drawer-CCM6S25D.js} +3 -5
  102. package/dist/chunks/{velin-email-RBX6ZR4S.js → velin-email-TNFONXPW.js} +1 -1
  103. package/dist/chunks/velin-file-dropzone-QRGF2JNA.js +154 -0
  104. package/dist/chunks/velin-file-dropzone-XIZMJ3NB.js +154 -0
  105. package/dist/chunks/{velin-icon-7M76DOPB.js → velin-icon-A6NCXGUK.js} +59 -17
  106. package/dist/chunks/{velin-icon-ZTSKUOVN.js → velin-icon-EC4B5XCM.js} +18 -5
  107. package/dist/chunks/{velin-lightbox-6E7CAATP.js → velin-lightbox-FFK4TUUP.js} +7 -6
  108. package/dist/chunks/{velin-lightbox-NXA3U2FM.js → velin-lightbox-P725FLTY.js} +5 -4
  109. package/dist/chunks/{velin-menubar-J66JXRWW.js → velin-menubar-6I2LM5HL.js} +1 -1
  110. package/dist/chunks/{velin-modal-5HYNNNUI.js → velin-modal-ICTMGRSU.js} +7 -8
  111. package/dist/chunks/{velin-modal-AEOTZ67O.js → velin-modal-JBNODH7D.js} +2 -4
  112. package/dist/chunks/{velin-popover-T72HK3DF.js → velin-popover-WZG2GHBL.js} +25 -4
  113. package/dist/chunks/{velin-rating-N4DI2XNL.js → velin-rating-QBBYRRBU.js} +1 -1
  114. package/dist/chunks/{velin-search-7B4KJ6TX.js → velin-search-B3L6J2RP.js} +2 -2
  115. package/dist/chunks/{velin-search-2XRQFBCS.js → velin-search-L3UG6NDQ.js} +5 -4
  116. package/dist/chunks/{velin-secure-field-6PV7RZDJ.js → velin-secure-field-DGYMP7OK.js} +1 -1
  117. package/dist/chunks/{velin-segmented-control-GYD5BIXQ.js → velin-segmented-control-P74GJPAR.js} +1 -1
  118. package/dist/chunks/{velin-sheet-R2Y67X6H.js → velin-sheet-M5GZZD7G.js} +2 -4
  119. package/dist/chunks/{velin-sheet-OUK6572T.js → velin-sheet-X7RXIWKV.js} +3 -5
  120. package/dist/chunks/{velin-stepper-X54WGHDG.js → velin-stepper-4YQJUHPC.js} +12 -5
  121. package/dist/chunks/{velin-toast-OMMOA2DK.js → velin-toast-UG5LWTG2.js} +5 -1
  122. package/dist/chunks/{velin-tooltip-BDHGRTQB.js → velin-tooltip-ZDH5SDLU.js} +1 -1
  123. package/dist/llms.txt +29 -7
  124. package/dist/search-index.json +156 -10
  125. package/dist/velin-agent.json +3973 -28
  126. package/dist/velinstyle-components.iife.js +1211 -284
  127. package/dist/velinstyle-components.js +1213 -284
  128. package/dist/velinstyle-components.min.js +266 -118
  129. package/dist/velinstyle.css +252 -67
  130. package/dist/velinstyle.d.ts +2 -0
  131. package/dist/velinstyle.min.css +1 -1
  132. package/package.json +20 -6
  133. package/packages/skill-engine/README.md +24 -0
  134. package/packages/skill-engine/package.json +7 -0
  135. package/packages/skill-engine/src/catalog.js +29 -0
  136. package/packages/skill-engine/src/index.js +5 -0
  137. package/packages/skill-engine/src/install.js +69 -0
  138. package/packages/skill-engine/src/registry.js +27 -0
  139. package/packages/skill-engine/src/resolve.js +68 -0
  140. package/packages/skill-engine/src/validate.js +40 -0
  141. package/packages/velinstyle-skills/README.md +6 -0
  142. package/packages/velinstyle-skills/adapter.json +8 -0
  143. package/packages/velinstyle-skills/bundles/landing-starter.bundle.yaml +12 -0
  144. package/packages/velinstyle-skills/bundles/review-suite.bundle.yaml +11 -0
  145. package/packages/velinstyle-skills/catalog.json +1004 -0
  146. package/packages/velinstyle-skills/graphs/component-ship.graph.yaml +14 -0
  147. package/packages/velinstyle-skills/graphs/landingpage.graph.yaml +21 -0
  148. package/packages/velinstyle-skills/graphs/release-gate.graph.yaml +15 -0
  149. package/packages/velinstyle-skills/index.js +22 -0
  150. package/packages/velinstyle-skills/package.json +7 -0
  151. package/packages/velinstyle-skills/packs/accessibility.pack.yaml +9 -0
  152. package/packages/velinstyle-skills/packs/design.pack.yaml +10 -0
  153. package/packages/velinstyle-skills/packs/documentation.pack.yaml +8 -0
  154. package/packages/velinstyle-skills/packs/enterprise.pack.yaml +10 -0
  155. package/packages/velinstyle-skills/packs/frontend.pack.yaml +10 -0
  156. package/packages/velinstyle-skills/packs/review.pack.yaml +9 -0
  157. package/packages/velinstyle-skills/projects/admin-panel.project.yaml +12 -0
  158. package/packages/velinstyle-skills/projects/blog.project.yaml +11 -0
  159. package/packages/velinstyle-skills/projects/documentation-portal.project.yaml +13 -0
  160. package/packages/velinstyle-skills/projects/ecommerce-shop.project.yaml +12 -0
  161. package/packages/velinstyle-skills/projects/landing-page.project.yaml +12 -0
  162. package/packages/velinstyle-skills/projects/laravel-dashboard.project.yaml +12 -0
  163. package/packages/velinstyle-skills/projects/marketing-website.project.yaml +14 -0
  164. package/packages/velinstyle-skills/projects/portfolio.project.yaml +12 -0
  165. package/packages/velinstyle-skills/registry.json +142 -0
  166. package/packages/velinstyle-skills/scripts/generate-assets.mjs +163 -0
  167. package/packages/velinstyle-skills/skills/velin-a11y-focus-keyboard/SKILL.md +57 -0
  168. package/packages/velinstyle-skills/skills/velin-a11y-page-checklist/SKILL.md +57 -0
  169. package/packages/velinstyle-skills/skills/velin-a11y-scan-fix/SKILL.md +57 -0
  170. package/packages/velinstyle-skills/skills/velin-ai-agent-briefing/SKILL.md +57 -0
  171. package/packages/velinstyle-skills/skills/velin-ai-plan-scaffold-review/SKILL.md +57 -0
  172. package/packages/velinstyle-skills/skills/velin-ai-skill-authoring/SKILL.md +57 -0
  173. package/packages/velinstyle-skills/skills/velin-components-accessibility-map/SKILL.md +57 -0
  174. package/packages/velinstyle-skills/skills/velin-components-api-contract/SKILL.md +57 -0
  175. package/packages/velinstyle-skills/skills/velin-components-recipe/SKILL.md +57 -0
  176. package/packages/velinstyle-skills/skills/velin-components-select-compose/SKILL.md +57 -0
  177. package/packages/velinstyle-skills/skills/velin-design-hero-compose/SKILL.md +57 -0
  178. package/packages/velinstyle-skills/skills/velin-design-section-rhythm/SKILL.md +57 -0
  179. package/packages/velinstyle-skills/skills/velin-design-surface-roles/SKILL.md +57 -0
  180. package/packages/velinstyle-skills/skills/velin-dev-component-wire/SKILL.md +57 -0
  181. package/packages/velinstyle-skills/skills/velin-dev-page-scaffold/SKILL.md +57 -0
  182. package/packages/velinstyle-skills/skills/velin-dev-plan-first/SKILL.md +57 -0
  183. package/packages/velinstyle-skills/skills/velin-docs-component-guide/SKILL.md +57 -0
  184. package/packages/velinstyle-skills/skills/velin-docs-generate-api/SKILL.md +57 -0
  185. package/packages/velinstyle-skills/skills/velin-dx-doctor-setup/SKILL.md +57 -0
  186. package/packages/velinstyle-skills/skills/velin-dx-project-init/SKILL.md +57 -0
  187. package/packages/velinstyle-skills/skills/velin-migration-legacy-wc/SKILL.md +57 -0
  188. package/packages/velinstyle-skills/skills/velin-migration-prefix-classes/SKILL.md +57 -0
  189. package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +57 -0
  190. package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +57 -0
  191. package/packages/velinstyle-skills/skills/velin-perf-audit-fix/SKILL.md +57 -0
  192. package/packages/velinstyle-skills/skills/velin-perf-runtime-tree-shake/SKILL.md +57 -0
  193. package/packages/velinstyle-skills/skills/velin-release-changelog-sync/SKILL.md +57 -0
  194. package/packages/velinstyle-skills/skills/velin-release-meta-rebuild/SKILL.md +57 -0
  195. package/packages/velinstyle-skills/skills/velin-review-conversion-hero/SKILL.md +57 -0
  196. package/packages/velinstyle-skills/skills/velin-review-page-gate/SKILL.md +57 -0
  197. package/packages/velinstyle-skills/skills/velin-review-seo-meta/SKILL.md +57 -0
  198. package/packages/velinstyle-skills/skills/velin-security-sanitize-html/SKILL.md +57 -0
  199. package/packages/velinstyle-skills/skills/velin-security-scan-pii/SKILL.md +57 -0
  200. package/packages/velinstyle-skills/skills/velin-testing-a11y-suite/SKILL.md +57 -0
  201. package/packages/velinstyle-skills/skills/velin-testing-visual-smoke/SKILL.md +57 -0
  202. package/packages/velinstyle-skills/skills/velin-themes-apply-token/SKILL.md +57 -0
  203. package/packages/velinstyle-skills/skills/velin-themes-contrast-check/SKILL.md +57 -0
  204. package/packages/velinstyle-skills/skills/velin-utilities-layout-apply/SKILL.md +57 -0
  205. package/packages/velinstyle-skills/skills/velin-utilities-prefix-fix/SKILL.md +57 -0
  206. package/packages/velinstyle-skills/skills/velin-utilities-token-map/SKILL.md +57 -0
  207. package/packages/velinstyle-skills/templates/admin-panel.template.yaml +12 -0
  208. package/packages/velinstyle-skills/templates/blog.template.yaml +12 -0
  209. package/packages/velinstyle-skills/templates/documentation-portal.template.yaml +12 -0
  210. package/packages/velinstyle-skills/templates/ecommerce-shop.template.yaml +13 -0
  211. package/packages/velinstyle-skills/templates/landing-page.template.yaml +12 -0
  212. package/packages/velinstyle-skills/templates/laravel-dashboard.template.yaml +11 -0
  213. package/packages/velinstyle-skills/templates/marketing-website.template.yaml +13 -0
  214. package/packages/velinstyle-skills/templates/portfolio.template.yaml +12 -0
  215. package/schemas/component-knowledge.schema.json +33 -0
  216. package/schemas/design-constraint.schema.json +15 -0
  217. package/schemas/design-intelligence.schema.json +19 -0
  218. package/schemas/design-rule.schema.json +19 -0
  219. package/schemas/page-template.schema.json +24 -0
  220. package/schemas/review-report.schema.json +36 -0
  221. package/schemas/section.schema.json +22 -0
  222. package/schemas/skill-bundle.schema.json +15 -0
  223. package/schemas/skill-graph.schema.json +25 -0
  224. package/schemas/skill-pack.schema.json +14 -0
  225. package/schemas/skill-project.schema.json +16 -0
  226. package/schemas/skill-record.schema.json +27 -0
  227. package/schemas/skill-registry.schema.json +17 -0
  228. package/schemas/skill-template.schema.json +17 -0
  229. package/schemas/token-graph.schema.json +27 -0
  230. package/schemas/utility.schema.json +166 -0
  231. package/src/components/alert.css +22 -1
  232. package/src/components/calendar-dropzone.css +5 -0
  233. package/src/layout/app-shell.css +33 -0
  234. package/src/tokens/shadow.css +13 -0
  235. package/src/utilities/border.css +2 -0
  236. package/src/utilities/display.css +1 -24
  237. package/src/utilities/responsive.css +16 -1
  238. package/src/utilities/spacing.css +32 -0
  239. package/src/velinstyle.css +2 -0
  240. package/dist/chunks/attributes-3BZLI7XF.js +0 -364
  241. package/dist/chunks/attributes-4N5JP4CG.js +0 -364
  242. package/dist/chunks/attributes-5Q2WXGRC.js +0 -374
  243. package/dist/chunks/attributes-AIR7SVLK.js +0 -364
  244. package/dist/chunks/attributes-BGRN5XZO.js +0 -364
  245. package/dist/chunks/attributes-BVSQAYLR.js +0 -364
  246. package/dist/chunks/attributes-CDYFUY7Y.js +0 -364
  247. package/dist/chunks/attributes-JMYJZBPE.js +0 -364
  248. package/dist/chunks/attributes-R5JY3X4O.js +0 -374
  249. package/dist/chunks/attributes-TNSPQTHX.js +0 -374
  250. package/dist/chunks/chunk-2354NEBP.js +0 -109
  251. package/dist/chunks/chunk-2VNA7G7S.js +0 -109
  252. package/dist/chunks/chunk-AQLRJSGG.js +0 -109
  253. package/dist/chunks/chunk-EWPSDMQT.js +0 -109
  254. package/dist/chunks/chunk-LA7264K4.js +0 -85
  255. package/dist/chunks/chunk-QTJJ3DPO.js +0 -109
  256. package/dist/chunks/chunk-RZQVWFHW.js +0 -109
  257. package/dist/chunks/chunk-U4XHMZ7D.js +0 -109
  258. package/dist/chunks/chunk-V5YIUA6G.js +0 -109
  259. package/dist/chunks/chunk-XIET2VSZ.js +0 -109
  260. package/dist/chunks/chunk-XYLJ2GCB.js +0 -205
  261. package/dist/chunks/chunk-ZPSPKUYD.js +0 -109
  262. package/dist/chunks/highlight-XTHRKBA3.js +0 -37
  263. package/dist/chunks/velin-bottom-nav-2N2TYWOF.js +0 -86
  264. package/dist/chunks/velin-carousel-Y5VCOABZ.js +0 -213
  265. package/dist/chunks/velin-code-block-ASUBTAQV.js +0 -119
  266. package/dist/chunks/velin-code-block-TSUKPY4C.js +0 -123
  267. package/dist/chunks/velin-code-block-Y6CM726Q.js +0 -119
  268. package/dist/chunks/velin-combobox-2KDTCQ7N.js +0 -155
  269. package/dist/chunks/velin-command-M7RNHUPR.js +0 -136
  270. package/dist/chunks/velin-copy-T5KDTDXP.js +0 -81
  271. package/dist/chunks/velin-countdown-5XSTN3BH.js +0 -101
  272. package/dist/chunks/velin-counter-ISCTEAHI.js +0 -118
  273. package/dist/chunks/velin-dialog-D2GUHSCD.js +0 -144
  274. package/dist/chunks/velin-dialog-IJLFDXAJ.js +0 -144
  275. package/dist/chunks/velin-dialog-TNS3WXMB.js +0 -144
  276. package/dist/chunks/velin-drawer-MJS6WCF7.js +0 -130
  277. package/dist/chunks/velin-email-N5B5ZYTE.js +0 -90
  278. package/dist/chunks/velin-icon-KZO3IEHG.js +0 -164
  279. package/dist/chunks/velin-lightbox-I75U6JF4.js +0 -140
  280. package/dist/chunks/velin-lightbox-QOTB4PRL.js +0 -140
  281. package/dist/chunks/velin-lightbox-UCD6N7HF.js +0 -140
  282. package/dist/chunks/velin-live-dot-XHUZCIDZ.js +0 -67
  283. package/dist/chunks/velin-menubar-SFEXJE56.js +0 -86
  284. package/dist/chunks/velin-modal-2HOBXE6F.js +0 -176
  285. package/dist/chunks/velin-modal-GLI2XZZB.js +0 -176
  286. package/dist/chunks/velin-popover-EXINRIGJ.js +0 -145
  287. package/dist/chunks/velin-popover-OXX7MRZ6.js +0 -145
  288. package/dist/chunks/velin-popover-YKCCROJG.js +0 -145
  289. package/dist/chunks/velin-rating-XRQJV4LO.js +0 -91
  290. package/dist/chunks/velin-scroll-top-F2FCZNYD.js +0 -58
  291. package/dist/chunks/velin-search-4YHJWYSS.js +0 -556
  292. package/dist/chunks/velin-search-LWHY23Q5.js +0 -474
  293. package/dist/chunks/velin-secure-field-H7FSBUJW.js +0 -95
  294. package/dist/chunks/velin-segmented-control-PQX6LKOJ.js +0 -109
  295. package/dist/chunks/velin-sheet-3OGI4HD7.js +0 -119
  296. package/dist/chunks/velin-sparkline-GVEANJVW.js +0 -184
  297. package/dist/chunks/velin-sparkline-KDA3GQAN.js +0 -162
  298. package/dist/chunks/velin-stepper-2ZIHLKGM.js +0 -141
  299. package/dist/chunks/velin-stepper-FYEUEWE6.js +0 -141
  300. package/dist/chunks/velin-stepper-wc-QU3KRVQR.js +0 -133
  301. package/dist/chunks/velin-theme-toggle-BNNS7WLV.js +0 -285
  302. package/dist/chunks/velin-theme-toggle-NO7V4TWI.js +0 -285
  303. package/dist/chunks/velin-theme-toggle-QELCJM2Q.js +0 -291
  304. package/dist/chunks/velin-toast-RCZ4CSUC.js +0 -109
  305. package/dist/chunks/velin-toast-WEUBTJG6.js +0 -109
  306. package/dist/chunks/velin-tooltip-664IOHZA.js +0 -112
  307. package/dist/chunks/velin-tooltip-wc-LXDVT6DE.js +0 -103
  308. package/dist/chunks/worker-client-R6DBYPFT.js +0 -46
@@ -0,0 +1,372 @@
1
+ import {
2
+ announce
3
+ } from "./chunk-Z4JYQ2XX.js";
4
+
5
+ // components/velin-data-table.js
6
+ var SORT_TYPES = /* @__PURE__ */ new Set(["text", "number", "date"]);
7
+ var warnedMissingName = false;
8
+ function sortValue(cell, type) {
9
+ const raw = cell?.dataset?.sortValue ?? cell?.textContent ?? "";
10
+ const text = raw.trim();
11
+ if (type === "number") {
12
+ const num = Number.parseFloat(text.replace(/[^\d.,-]/g, "").replace(",", "."));
13
+ return Number.isNaN(num) ? Number.NEGATIVE_INFINITY : num;
14
+ }
15
+ if (type === "date") {
16
+ const time = Date.parse(text);
17
+ return Number.isNaN(time) ? Number.NEGATIVE_INFINITY : time;
18
+ }
19
+ return text.toLowerCase();
20
+ }
21
+ var VelinDataTable = class extends HTMLElement {
22
+ static get observedAttributes() {
23
+ return ["page-size", "filter-input", "empty-text", "label", "editable"];
24
+ }
25
+ constructor() {
26
+ super();
27
+ this._table = null;
28
+ this._page = 1;
29
+ this._query = "";
30
+ this._sortIndex = -1;
31
+ this._sortDirection = "ascending";
32
+ this._filterEl = null;
33
+ this._pagination = null;
34
+ this._emptyRow = null;
35
+ this._filterTimer = null;
36
+ this._onFilterInput = this._onFilterInput.bind(this);
37
+ }
38
+ connectedCallback() {
39
+ this.classList.add("velin-data-table");
40
+ requestAnimationFrame(() => this._init());
41
+ }
42
+ disconnectedCallback() {
43
+ if (this._filterEl) this._filterEl.removeEventListener("input", this._onFilterInput);
44
+ if (this._filterTimer) clearTimeout(this._filterTimer);
45
+ }
46
+ attributeChangedCallback(name, previous, next) {
47
+ if (previous === next || !this._table) return;
48
+ if (name === "page-size") {
49
+ this._page = 1;
50
+ this._render();
51
+ } else if (name === "filter-input") {
52
+ this._bindFilterInput();
53
+ } else if (name === "label") {
54
+ this._ensureAccessibleName();
55
+ }
56
+ }
57
+ // ── Public API ─────────────────────────────────────────────────────────────
58
+ /** @returns {HTMLTableRowElement[]} */
59
+ get rows() {
60
+ const body = this._table?.tBodies?.[0];
61
+ return body ? [...body.rows].filter((row) => row !== this._emptyRow) : [];
62
+ }
63
+ /** Rows matching the current filter, across all pages. */
64
+ get matchingRows() {
65
+ return this.rows.filter((row) => !row.dataset.velinFiltered);
66
+ }
67
+ /** Rows visible on the current page. */
68
+ get visibleRows() {
69
+ return this.matchingRows.filter((row) => !row.hidden);
70
+ }
71
+ get page() {
72
+ return this._page;
73
+ }
74
+ get pageSize() {
75
+ const size = Number.parseInt(this.getAttribute("page-size") || "", 10);
76
+ return Number.isFinite(size) && size > 0 ? size : 0;
77
+ }
78
+ get pageCount() {
79
+ const size = this.pageSize;
80
+ if (!size) return 1;
81
+ return Math.max(1, Math.ceil(this.matchingRows.length / size));
82
+ }
83
+ /**
84
+ * @param {number} index Column index
85
+ * @param {'ascending' | 'descending'} [direction]
86
+ */
87
+ sort(index, direction) {
88
+ const headers = this._headers();
89
+ const header = headers[index];
90
+ if (!header) return;
91
+ const type = this._sortType(header);
92
+ if (type === "none") return;
93
+ this._sortDirection = direction || (this._sortIndex === index && this._sortDirection === "ascending" ? "descending" : "ascending");
94
+ this._sortIndex = index;
95
+ const factor = this._sortDirection === "ascending" ? 1 : -1;
96
+ const body = this._table.tBodies[0];
97
+ const sorted = this.rows.slice().sort((a, b) => {
98
+ const av = sortValue(a.cells[index], type);
99
+ const bv = sortValue(b.cells[index], type);
100
+ if (av < bv) return -1 * factor;
101
+ if (av > bv) return 1 * factor;
102
+ return 0;
103
+ });
104
+ for (const row of sorted) body.appendChild(row);
105
+ this._syncSortState();
106
+ this._page = 1;
107
+ this._render();
108
+ const label = header.dataset.sortLabel || header.textContent.trim();
109
+ announce(`${label} sorted ${this._sortDirection}`);
110
+ this.dispatchEvent(new CustomEvent("velin-data-table-sort", {
111
+ bubbles: true,
112
+ detail: { index, direction: this._sortDirection, column: label }
113
+ }));
114
+ }
115
+ /** @param {string} query */
116
+ filter(query) {
117
+ this._query = String(query || "").trim().toLowerCase();
118
+ for (const row of this.rows) {
119
+ const match = !this._query || this._rowText(row).includes(this._query);
120
+ if (match) delete row.dataset.velinFiltered;
121
+ else row.dataset.velinFiltered = "true";
122
+ }
123
+ this._page = 1;
124
+ this._render();
125
+ const count = this.matchingRows.length;
126
+ announce(count === 1 ? "1 row matches" : `${count} rows match`);
127
+ this.dispatchEvent(new CustomEvent("velin-data-table-filter", {
128
+ bubbles: true,
129
+ detail: { query: this._query, count }
130
+ }));
131
+ }
132
+ /** @param {number} page */
133
+ goToPage(page) {
134
+ const target = Math.min(Math.max(1, Math.trunc(page) || 1), this.pageCount);
135
+ if (target === this._page) return;
136
+ this._page = target;
137
+ this._render();
138
+ announce(`Page ${this._page} of ${this.pageCount}`);
139
+ this.dispatchEvent(new CustomEvent("velin-data-table-page", {
140
+ bubbles: true,
141
+ detail: { page: this._page, pageCount: this.pageCount }
142
+ }));
143
+ }
144
+ // ── Setup ──────────────────────────────────────────────────────────────────
145
+ _init() {
146
+ this._table = this.querySelector("table");
147
+ if (!this._table || !this._table.tBodies.length) return;
148
+ this._ensureAccessibleName();
149
+ this._setupSorting();
150
+ this._setupEditable();
151
+ this._bindFilterInput();
152
+ this._render();
153
+ }
154
+ _headers() {
155
+ const headRow = this._table.tHead?.rows?.[0];
156
+ return headRow ? [...headRow.cells] : [];
157
+ }
158
+ /** @param {HTMLTableCellElement} header */
159
+ _sortType(header) {
160
+ const declared = (header.dataset.sort || "").toLowerCase();
161
+ if (declared === "none") return "none";
162
+ if (SORT_TYPES.has(declared)) return declared;
163
+ return this.hasAttribute("sortable") ? "text" : "none";
164
+ }
165
+ /**
166
+ * A table without a name is unusable with a screen reader, so surface it
167
+ * instead of failing silently (WCAG 1.3.1 / 2.4.6).
168
+ */
169
+ _ensureAccessibleName() {
170
+ const table = this._table;
171
+ if (!table) return;
172
+ const label = this.getAttribute("label");
173
+ const hasName = table.caption?.textContent.trim() || table.getAttribute("aria-label")?.trim() || table.getAttribute("aria-labelledby")?.trim();
174
+ if (!hasName && label) {
175
+ table.setAttribute("aria-label", label);
176
+ return;
177
+ }
178
+ if (!hasName && !warnedMissingName) {
179
+ warnedMissingName = true;
180
+ console.warn("[velinstyle] <velin-data-table> needs a <caption>, aria-label, or a label attribute.");
181
+ }
182
+ }
183
+ /** Sortable headers get a real button so keyboard and AT support come for free. */
184
+ _setupSorting() {
185
+ this._headers().forEach((header, index) => {
186
+ if (this._sortType(header) === "none") return;
187
+ if (header.querySelector(".velin-data-table__sort")) return;
188
+ const label = header.textContent.trim();
189
+ header.dataset.sortLabel = label;
190
+ header.setAttribute("aria-sort", "none");
191
+ header.classList.add("velin-data-table__th");
192
+ const button = document.createElement("button");
193
+ button.type = "button";
194
+ button.className = "velin-data-table__sort";
195
+ button.textContent = label;
196
+ const icon = document.createElement("span");
197
+ icon.className = "velin-data-table__sort-icon";
198
+ icon.setAttribute("aria-hidden", "true");
199
+ button.appendChild(icon);
200
+ button.addEventListener("click", () => this.sort(index));
201
+ header.textContent = "";
202
+ header.appendChild(button);
203
+ });
204
+ }
205
+ /**
206
+ * Inline edit: double-click or Enter on focused cell with data-editable / host editable.
207
+ * Enter saves, Escape cancels. Dispatches velin-data-table-edit.
208
+ */
209
+ _setupEditable() {
210
+ if (!this.hasAttribute("editable") && !this.querySelector("[data-editable]")) return;
211
+ const body = this._table.tBodies[0];
212
+ if (!body || body.dataset.velinEditBound) return;
213
+ body.dataset.velinEditBound = "true";
214
+ body.addEventListener("dblclick", (e) => {
215
+ const cell = e.target.closest("td");
216
+ if (!cell || !body.contains(cell)) return;
217
+ if (!this.hasAttribute("editable") && !cell.hasAttribute("data-editable")) return;
218
+ this._beginEdit(cell);
219
+ });
220
+ body.addEventListener("keydown", (e) => {
221
+ const cell = e.target.closest("td");
222
+ if (!cell || e.target !== cell) return;
223
+ if (e.key === "Enter" || e.key === "F2") {
224
+ if (!this.hasAttribute("editable") && !cell.hasAttribute("data-editable")) return;
225
+ e.preventDefault();
226
+ this._beginEdit(cell);
227
+ }
228
+ });
229
+ for (const cell of body.querySelectorAll("td[data-editable], td")) {
230
+ if (!this.hasAttribute("editable") && !cell.hasAttribute("data-editable")) continue;
231
+ if (!cell.hasAttribute("tabindex")) cell.setAttribute("tabindex", "0");
232
+ }
233
+ }
234
+ /** @param {HTMLTableCellElement} cell */
235
+ _beginEdit(cell) {
236
+ if (cell.querySelector("input")) return;
237
+ const previous = cell.textContent.trim();
238
+ const input = document.createElement("input");
239
+ input.className = "velin-input velin-data-table__edit";
240
+ input.value = previous;
241
+ input.setAttribute("aria-label", "Edit cell");
242
+ cell.textContent = "";
243
+ cell.appendChild(input);
244
+ input.focus();
245
+ input.select();
246
+ const commit = (save) => {
247
+ const next = save ? input.value.trim() : previous;
248
+ cell.textContent = next;
249
+ if (save && next !== previous) {
250
+ this.dispatchEvent(new CustomEvent("velin-data-table-edit", {
251
+ bubbles: true,
252
+ detail: {
253
+ cell,
254
+ row: cell.parentElement,
255
+ previous,
256
+ value: next,
257
+ columnIndex: cell.cellIndex
258
+ }
259
+ }));
260
+ announce("Cell updated");
261
+ }
262
+ };
263
+ input.addEventListener("keydown", (e) => {
264
+ if (e.key === "Enter") {
265
+ e.preventDefault();
266
+ commit(true);
267
+ } else if (e.key === "Escape") {
268
+ e.preventDefault();
269
+ commit(false);
270
+ }
271
+ });
272
+ input.addEventListener("blur", () => commit(true));
273
+ }
274
+ _syncSortState() {
275
+ this._headers().forEach((header, index) => {
276
+ if (this._sortType(header) === "none") return;
277
+ const active = index === this._sortIndex;
278
+ header.setAttribute("aria-sort", active ? this._sortDirection : "none");
279
+ header.querySelector(".velin-data-table__sort")?.classList.toggle("velin-data-table__sort--active", active);
280
+ });
281
+ }
282
+ _bindFilterInput() {
283
+ if (this._filterEl) this._filterEl.removeEventListener("input", this._onFilterInput);
284
+ const selector = this.getAttribute("filter-input");
285
+ this._filterEl = selector ? document.querySelector(selector) : null;
286
+ if (this._filterEl) this._filterEl.addEventListener("input", this._onFilterInput);
287
+ }
288
+ _onFilterInput(event) {
289
+ if (this._filterTimer) clearTimeout(this._filterTimer);
290
+ const value = event.target.value;
291
+ this._filterTimer = setTimeout(() => this.filter(value), 150);
292
+ }
293
+ /** @param {HTMLTableRowElement} row */
294
+ _rowText(row) {
295
+ const scoped = [...row.cells].filter((cell) => cell.hasAttribute("data-filter"));
296
+ const cells = scoped.length ? scoped : [...row.cells];
297
+ return cells.map((cell) => cell.textContent || "").join(" ").toLowerCase();
298
+ }
299
+ // ── Rendering ──────────────────────────────────────────────────────────────
300
+ _render() {
301
+ const size = this.pageSize;
302
+ this._page = Math.min(this._page, this.pageCount);
303
+ const matching = this.matchingRows;
304
+ const start = size ? (this._page - 1) * size : 0;
305
+ const end = size ? start + size : matching.length;
306
+ for (const row of this.rows) {
307
+ const index = matching.indexOf(row);
308
+ row.hidden = index === -1 || index < start || index >= end;
309
+ }
310
+ this._renderEmptyState(matching.length === 0);
311
+ this._renderPagination();
312
+ }
313
+ /** @param {boolean} isEmpty */
314
+ _renderEmptyState(isEmpty) {
315
+ if (!isEmpty) {
316
+ this._emptyRow?.remove();
317
+ this._emptyRow = null;
318
+ return;
319
+ }
320
+ if (this._emptyRow?.isConnected) return;
321
+ const columns = this._headers().length || 1;
322
+ const row = document.createElement("tr");
323
+ row.className = "velin-data-table__empty";
324
+ const cell = document.createElement("td");
325
+ cell.colSpan = columns;
326
+ cell.textContent = this.getAttribute("empty-text") || "No matching rows";
327
+ row.appendChild(cell);
328
+ this._table.tBodies[0].appendChild(row);
329
+ this._emptyRow = row;
330
+ }
331
+ _renderPagination() {
332
+ if (!this.pageSize || this.pageCount <= 1) {
333
+ this._pagination?.remove();
334
+ this._pagination = null;
335
+ return;
336
+ }
337
+ if (!this._pagination?.isConnected) {
338
+ const nav = document.createElement("nav");
339
+ nav.className = "velin-data-table__pagination";
340
+ nav.setAttribute("aria-label", this.getAttribute("pagination-label") || "Table pagination");
341
+ const previous2 = document.createElement("button");
342
+ previous2.type = "button";
343
+ previous2.className = "velin-btn velin-btn--outline velin-btn--sm";
344
+ previous2.dataset.velinPage = "previous";
345
+ previous2.textContent = this.getAttribute("previous-text") || "Previous";
346
+ previous2.addEventListener("click", () => this.goToPage(this._page - 1));
347
+ const status2 = document.createElement("p");
348
+ status2.className = "velin-data-table__page-status";
349
+ status2.dataset.velinPage = "status";
350
+ const next2 = document.createElement("button");
351
+ next2.type = "button";
352
+ next2.className = "velin-btn velin-btn--outline velin-btn--sm";
353
+ next2.dataset.velinPage = "next";
354
+ next2.textContent = this.getAttribute("next-text") || "Next";
355
+ next2.addEventListener("click", () => this.goToPage(this._page + 1));
356
+ nav.append(previous2, status2, next2);
357
+ this.appendChild(nav);
358
+ this._pagination = nav;
359
+ }
360
+ const status = this._pagination.querySelector('[data-velin-page="status"]');
361
+ if (status) status.textContent = `Page ${this._page} of ${this.pageCount}`;
362
+ const previous = this._pagination.querySelector('[data-velin-page="previous"]');
363
+ const next = this._pagination.querySelector('[data-velin-page="next"]');
364
+ if (previous) previous.disabled = this._page <= 1;
365
+ if (next) next.disabled = this._page >= this.pageCount;
366
+ }
367
+ };
368
+ customElements.define("velin-data-table", VelinDataTable);
369
+ var velin_data_table_default = VelinDataTable;
370
+ export {
371
+ velin_data_table_default as default
372
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  escapeHTML
3
- } from "./chunk-RDE3QE7Z.js";
3
+ } from "./chunk-L7ZGUU4D.js";
4
4
 
5
5
  // components/velin-dialog.js
6
6
  var styles = `
@@ -70,6 +70,10 @@ var VelinDialog = class extends HTMLElement {
70
70
  }
71
71
  connectedCallback() {
72
72
  this.shadowRoot.innerHTML = `<style>${styles}</style><dialog part="dialog"></dialog>`;
73
+ const dialog = this.shadowRoot.querySelector("dialog");
74
+ if (dialog) {
75
+ dialog.setAttribute("aria-label", this.getAttribute("aria-label") || "Dialog");
76
+ }
73
77
  }
74
78
  alert(message, { title = "Alert", confirmText = "OK" } = {}) {
75
79
  return this._show("alert", message, { title, confirmText });
@@ -99,6 +103,7 @@ var VelinDialog = class extends HTMLElement {
99
103
  <div class="footer">${footerBtns}</div>
100
104
  `;
101
105
  dialog.setAttribute("aria-modal", "true");
106
+ dialog.removeAttribute("aria-label");
102
107
  dialog.setAttribute("aria-labelledby", titleId);
103
108
  dialog.showModal();
104
109
  if (type !== "prompt") {
@@ -137,7 +142,9 @@ var VelinDialog = class extends HTMLElement {
137
142
  }
138
143
  this._resolve(value);
139
144
  this._resolve = null;
140
- this.dispatchEvent(new CustomEvent("velin-dialog-close", { bubbles: true, detail: { value } }));
145
+ const detail = { value };
146
+ this.dispatchEvent(new CustomEvent("velin-close", { bubbles: true, detail }));
147
+ this.dispatchEvent(new CustomEvent("velin-dialog-close", { bubbles: true, detail }));
141
148
  }
142
149
  };
143
150
  customElements.define("velin-dialog", VelinDialog);
@@ -142,7 +142,9 @@ var VelinDialog = class extends HTMLElement {
142
142
  }
143
143
  this._resolve(value);
144
144
  this._resolve = null;
145
- this.dispatchEvent(new CustomEvent("velin-dialog-close", { bubbles: true, detail: { value } }));
145
+ const detail = { value };
146
+ this.dispatchEvent(new CustomEvent("velin-close", { bubbles: true, detail }));
147
+ this.dispatchEvent(new CustomEvent("velin-dialog-close", { bubbles: true, detail }));
146
148
  }
147
149
  };
148
150
  customElements.define("velin-dialog", VelinDialog);
@@ -99,7 +99,6 @@ var VelinDrawer = class extends HTMLElement {
99
99
  this._prev = saveFocus();
100
100
  setBackgroundInert(this);
101
101
  document.addEventListener("keydown", this._onKey);
102
- document.body.style.overflow = "hidden";
103
102
  requestAnimationFrame(() => {
104
103
  const f = getFocusableElements(this.shadowRoot);
105
104
  if (f.length) f[0].focus();
@@ -107,8 +106,7 @@ var VelinDrawer = class extends HTMLElement {
107
106
  }
108
107
  _close() {
109
108
  document.removeEventListener("keydown", this._onKey);
110
- document.body.style.overflow = "";
111
- clearBackgroundInert();
109
+ clearBackgroundInert(this);
112
110
  restoreFocus(this._prev);
113
111
  }
114
112
  _onKey(e) {
@@ -120,7 +118,7 @@ var VelinDrawer = class extends HTMLElement {
120
118
  }
121
119
  disconnectedCallback() {
122
120
  document.removeEventListener("keydown", this._onKey);
123
- document.body.style.overflow = "";
121
+ clearBackgroundInert(this);
124
122
  }
125
123
  };
126
124
  customElements.define("velin-drawer", VelinDrawer);
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-42VVPW6N.js";
9
9
  import {
10
10
  escapeHTML
11
- } from "./chunk-MKZP2TBH.js";
11
+ } from "./chunk-L7ZGUU4D.js";
12
12
 
13
13
  // components/velin-drawer.js
14
14
  var styles = `
@@ -99,7 +99,6 @@ var VelinDrawer = class extends HTMLElement {
99
99
  this._prev = saveFocus();
100
100
  setBackgroundInert(this);
101
101
  document.addEventListener("keydown", this._onKey);
102
- document.body.style.overflow = "hidden";
103
102
  requestAnimationFrame(() => {
104
103
  const f = getFocusableElements(this.shadowRoot);
105
104
  if (f.length) f[0].focus();
@@ -107,8 +106,7 @@ var VelinDrawer = class extends HTMLElement {
107
106
  }
108
107
  _close() {
109
108
  document.removeEventListener("keydown", this._onKey);
110
- document.body.style.overflow = "";
111
- clearBackgroundInert();
109
+ clearBackgroundInert(this);
112
110
  restoreFocus(this._prev);
113
111
  }
114
112
  _onKey(e) {
@@ -120,7 +118,7 @@ var VelinDrawer = class extends HTMLElement {
120
118
  }
121
119
  disconnectedCallback() {
122
120
  document.removeEventListener("keydown", this._onKey);
123
- document.body.style.overflow = "";
121
+ clearBackgroundInert(this);
124
122
  }
125
123
  };
126
124
  customElements.define("velin-drawer", VelinDrawer);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  escapeHTML,
3
3
  escapeHTMLAttribute
4
- } from "./chunk-LA7264K4.js";
4
+ } from "./chunk-L7ZGUU4D.js";
5
5
 
6
6
  // components/velin-email.js
7
7
  var styles = `
@@ -0,0 +1,154 @@
1
+ import {
2
+ escapeHTML
3
+ } from "./chunk-L7ZGUU4D.js";
4
+
5
+ // components/velin-file-dropzone.js
6
+ var styles = `
7
+ :host { display: block; }
8
+ .zone {
9
+ border: 2px dashed var(--velin-color-border, #cbd5e1);
10
+ border-radius: var(--velin-radius-md, 0.5rem);
11
+ padding: var(--velin-space-6, 1.5rem);
12
+ background: var(--velin-color-surface-dim, var(--velin-color-bg-subtle, #f8fafc));
13
+ color: var(--velin-color-text, #111);
14
+ text-align: center;
15
+ transition: border-color 150ms ease, background 150ms ease;
16
+ }
17
+ :host([dragging]) .zone {
18
+ border-color: var(--velin-color-primary, #2563eb);
19
+ background: var(--velin-color-primary-subtle, #eff6ff);
20
+ }
21
+ .hint { margin: 0 0 var(--velin-space-3, 0.75rem); color: var(--velin-color-text-muted, #64748b); }
22
+ .browse {
23
+ display: inline-flex; align-items: center; justify-content: center;
24
+ min-block-size: 2.75rem; padding-inline: var(--velin-space-4, 1rem);
25
+ border-radius: var(--velin-radius-md, 0.5rem);
26
+ border: none; cursor: pointer;
27
+ background: var(--velin-color-primary, #2563eb);
28
+ color: var(--velin-color-on-primary, #fff);
29
+ font: inherit;
30
+ }
31
+ .browse:focus-visible { outline: 2px solid var(--velin-color-focus, #2563eb); outline-offset: 2px; }
32
+ input[type="file"] {
33
+ position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px;
34
+ overflow: hidden; clip: rect(0,0,0,0); border: 0;
35
+ }
36
+ .list { list-style: none; padding: 0; margin: var(--velin-space-4, 1rem) 0 0; text-align: start; }
37
+ .list li {
38
+ display: flex; justify-content: space-between; gap: var(--velin-space-3, 0.75rem);
39
+ padding: var(--velin-space-2, 0.5rem) 0;
40
+ border-block-end: 1px solid var(--velin-color-border, #e2e8f0);
41
+ font-size: var(--velin-text-sm, 0.875rem);
42
+ }
43
+ .progress {
44
+ margin-block-start: var(--velin-space-3, 0.75rem);
45
+ block-size: 0.5rem; border-radius: 999px;
46
+ background: var(--velin-color-border, #e2e8f0); overflow: hidden;
47
+ }
48
+ .bar {
49
+ block-size: 100%; inline-size: 0%;
50
+ background: var(--velin-color-primary, #2563eb);
51
+ transition: inline-size 150ms ease;
52
+ }
53
+ .status { margin: var(--velin-space-2, 0.5rem) 0 0; font-size: var(--velin-text-sm, 0.875rem); }
54
+ .status[data-tone="error"] { color: var(--velin-color-danger, #b91c1c); }
55
+ `;
56
+ var VelinFileDropzone = class extends HTMLElement {
57
+ static get observedAttributes() {
58
+ return ["accept", "multiple", "label", "progress"];
59
+ }
60
+ constructor() {
61
+ super();
62
+ this.attachShadow({ mode: "open" });
63
+ this._files = [];
64
+ }
65
+ connectedCallback() {
66
+ this._render();
67
+ }
68
+ attributeChangedCallback() {
69
+ if (this.shadowRoot?.querySelector(".zone")) this._render();
70
+ }
71
+ get files() {
72
+ return this._files.slice();
73
+ }
74
+ _emitFiles(fileList) {
75
+ const accept = this.getAttribute("accept");
76
+ const files = [...fileList];
77
+ if (accept) {
78
+ const parts = accept.split(",").map((s) => s.trim().toLowerCase());
79
+ const bad = files.find((f) => {
80
+ const name = f.name.toLowerCase();
81
+ const type = (f.type || "").toLowerCase();
82
+ return !parts.some((p) => p.startsWith(".") ? name.endsWith(p) : type === p || type.startsWith(p.replace("/*", "/")));
83
+ });
84
+ if (bad) {
85
+ this._setStatus(`File type not allowed: ${bad.name}`, "error");
86
+ this.dispatchEvent(new CustomEvent("velin-error", { bubbles: true, detail: { message: "accept", file: bad } }));
87
+ return;
88
+ }
89
+ }
90
+ this._files = files;
91
+ this._setStatus(files.length ? `${files.length} file(s) ready` : "", "ok");
92
+ this.dispatchEvent(new CustomEvent("velin-files", { bubbles: true, detail: { files } }));
93
+ this._renderList();
94
+ }
95
+ _setStatus(text, tone = "ok") {
96
+ const el = this.shadowRoot?.querySelector(".status");
97
+ if (!el) return;
98
+ el.textContent = text;
99
+ el.dataset.tone = tone;
100
+ }
101
+ _renderList() {
102
+ const ul = this.shadowRoot?.querySelector(".list");
103
+ if (!ul) return;
104
+ ul.innerHTML = this._files.map((f) => `<li><span>${escapeHTML(f.name)}</span><span>${Math.round(f.size / 1024)} KB</span></li>`).join("");
105
+ }
106
+ _render() {
107
+ const label = escapeHTML(this.getAttribute("label") || "Upload files");
108
+ const accept = this.getAttribute("accept") || "";
109
+ const multiple = this.hasAttribute("multiple");
110
+ const progress = Math.max(0, Math.min(100, Number(this.getAttribute("progress") || 0)));
111
+ this.shadowRoot.innerHTML = `
112
+ <style>${styles}</style>
113
+ <div class="zone" role="group" aria-label="${label}">
114
+ <p class="hint">Drag and drop files here, or browse. Client-side only \u2014 wire <code>velin-files</code> to your upload API.</p>
115
+ <button type="button" class="browse">Browse files</button>
116
+ <input type="file" ${accept ? `accept="${escapeHTML(accept)}"` : ""} ${multiple ? "multiple" : ""} />
117
+ <div class="progress" hidden="${progress <= 0 ? "true" : "false"}" aria-hidden="${progress <= 0 ? "true" : "false"}">
118
+ <div class="bar" style="inline-size:${progress}%"></div>
119
+ </div>
120
+ <p class="status" role="status" aria-live="polite"></p>
121
+ <ul class="list"></ul>
122
+ </div>
123
+ `;
124
+ const input = this.shadowRoot.querySelector("input");
125
+ const browse = this.shadowRoot.querySelector(".browse");
126
+ const zone = this.shadowRoot.querySelector(".zone");
127
+ browse.addEventListener("click", () => input.click());
128
+ input.addEventListener("change", () => {
129
+ if (input.files?.length) this._emitFiles(input.files);
130
+ });
131
+ ["dragenter", "dragover"].forEach((ev) => {
132
+ zone.addEventListener(ev, (e) => {
133
+ e.preventDefault();
134
+ this.setAttribute("dragging", "");
135
+ });
136
+ });
137
+ ["dragleave", "drop"].forEach((ev) => {
138
+ zone.addEventListener(ev, (e) => {
139
+ e.preventDefault();
140
+ this.removeAttribute("dragging");
141
+ });
142
+ });
143
+ zone.addEventListener("drop", (e) => {
144
+ const list = e.dataTransfer?.files;
145
+ if (list?.length) this._emitFiles(list);
146
+ });
147
+ this._renderList();
148
+ }
149
+ };
150
+ customElements.define("velin-file-dropzone", VelinFileDropzone);
151
+ var velin_file_dropzone_default = VelinFileDropzone;
152
+ export {
153
+ velin_file_dropzone_default as default
154
+ };