@birdapi/velinstyle 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/README.de.md +89 -161
  2. package/README.md +187 -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 +128 -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 +560 -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/transparency.js +221 -0
  42. package/cli/wc-api.js +89 -0
  43. package/cli/workflow.js +42 -0
  44. package/components/index.js +2 -0
  45. package/components/runtime/component-loaders.js +2 -0
  46. package/components/velin-accordion.js +62 -13
  47. package/components/velin-bottom-nav.js +10 -0
  48. package/components/velin-calendar.js +212 -0
  49. package/components/velin-command.js +18 -2
  50. package/components/velin-data-table.js +78 -1
  51. package/components/velin-dialog.js +4 -1
  52. package/components/velin-drawer.js +2 -4
  53. package/components/velin-file-dropzone.js +162 -0
  54. package/components/velin-icon.js +22 -1
  55. package/components/velin-lightbox.js +5 -3
  56. package/components/velin-modal.js +2 -4
  57. package/components/velin-search.js +1 -1
  58. package/components/velin-sheet.js +2 -4
  59. package/core/a11y/component-contracts.json +20 -3
  60. package/core/attributes/registry.js +14 -0
  61. package/core/meta/build.js +125 -5
  62. package/core/meta/design-constraints/contact.json +9 -0
  63. package/core/meta/design-constraints/faq.json +9 -0
  64. package/core/meta/design-constraints/hero.json +13 -0
  65. package/core/meta/knowledge/components.json +622 -0
  66. package/core/meta/knowledge/tokens.json +282 -0
  67. package/core/meta/pages/registry.json +294 -0
  68. package/core/meta/schema.js +13 -1
  69. package/core/meta/sections/registry.json +185 -0
  70. package/core/transparency/attach.js +74 -0
  71. package/core/transparency/claims.js +97 -0
  72. package/core/transparency/doctor.js +142 -0
  73. package/core/transparency/engine.js +75 -0
  74. package/core/transparency/export.js +98 -0
  75. package/core/transparency/index.js +30 -0
  76. package/core/transparency/migrate.js +130 -0
  77. package/core/transparency/normalize.js +125 -0
  78. package/core/transparency/policy.js +105 -0
  79. package/core/transparency/providers.js +235 -0
  80. package/core/transparency/registry.js +104 -0
  81. package/core/transparency/renderer.js +111 -0
  82. package/core/transparency/reporter.js +113 -0
  83. package/core/transparency/validator.js +112 -0
  84. package/dist/chunks/attach-H2ZSEAE6.js +365 -0
  85. package/dist/chunks/{attributes-STYCS3O5.js → attributes-A423NX5J.js} +3 -3
  86. package/dist/chunks/attributes-DZCXX2RZ.js +404 -0
  87. package/dist/chunks/{attributes-353XDOAX.js → attributes-F6UPIEVP.js} +1 -1
  88. package/dist/chunks/{attributes-ADOKLKA7.js → attributes-GLYXGGAL.js} +3 -3
  89. package/dist/chunks/{attributes-TPANLMLF.js → attributes-UFTYOSAU.js} +3 -3
  90. package/dist/chunks/attributes-XGLXLR2T.js +391 -0
  91. package/dist/chunks/attributes-YF4NI2QS.js +391 -0
  92. package/dist/chunks/browser-AUABBYUK.js +1544 -0
  93. package/dist/chunks/{chunk-RSMRR27R.js → chunk-2LSKX57P.js} +12 -10
  94. package/dist/chunks/{chunk-IEHKRARD.js → chunk-2TGCVDUL.js} +11 -9
  95. package/dist/chunks/{chunk-SRL6EWHP.js → chunk-5JWGUAPC.js} +17 -13
  96. package/dist/chunks/chunk-7YSO2OQ2.js +113 -0
  97. package/dist/chunks/{chunk-QVHYL3R4.js → chunk-BHR3ZG6U.js} +6 -6
  98. package/dist/chunks/chunk-CQMTCEI6.js +113 -0
  99. package/dist/chunks/{chunk-J472ERN2.js → chunk-JMSIHEYX.js} +15 -4
  100. package/dist/chunks/{chunk-MKZP2TBH.js → chunk-L7ZGUU4D.js} +2 -2
  101. package/dist/chunks/chunk-TUGIPKW5.js +113 -0
  102. package/dist/chunks/{highlight-GJHHV4KM.js → highlight-M2ELQNYK.js} +1 -1
  103. package/dist/chunks/runtime-entry.js +1 -1
  104. package/dist/chunks/{velin-accordion-FBIPAEXG.js → velin-accordion-WSY6BMLA.js} +64 -10
  105. package/dist/chunks/{velin-bottom-nav-7GEMPIM6.js → velin-bottom-nav-5RVRNGIP.js} +25 -0
  106. package/dist/chunks/{velin-bottom-nav-N3DFQR66.js → velin-bottom-nav-N5F4UO2I.js} +26 -1
  107. package/dist/chunks/velin-calendar-OFE6THUD.js +210 -0
  108. package/dist/chunks/velin-calendar-PGVNJTXK.js +217 -0
  109. package/dist/chunks/velin-calendar-XC7JRDSH.js +217 -0
  110. package/dist/chunks/{velin-code-block-CIAURDRJ.js → velin-code-block-ZGWG7JI5.js} +1 -1
  111. package/dist/chunks/{velin-combobox-NXS4QB4R.js → velin-combobox-56V2ZWT5.js} +1 -1
  112. package/dist/chunks/{velin-command-ONKO2A3J.js → velin-command-2HO2EKR6.js} +29 -2
  113. package/dist/chunks/{velin-command-TMLSRGIU.js → velin-command-ZJSLPK7B.js} +30 -3
  114. package/dist/chunks/{velin-copy-NYDBOKXT.js → velin-copy-XDQSV2XU.js} +1 -1
  115. package/dist/chunks/{velin-countdown-4ZVDGK4R.js → velin-countdown-V4Z5MA63.js} +1 -1
  116. package/dist/chunks/velin-data-table-UVR42UUM.js +372 -0
  117. package/dist/chunks/{velin-dialog-GTTZI564.js → velin-dialog-3YK2RPRV.js} +9 -2
  118. package/dist/chunks/{velin-dialog-ELFOZIXZ.js → velin-dialog-S24KAP4S.js} +3 -1
  119. package/dist/chunks/{velin-drawer-BE6TYN3H.js → velin-drawer-5PNU624J.js} +2 -4
  120. package/dist/chunks/{velin-drawer-DLL2GLT6.js → velin-drawer-CCM6S25D.js} +3 -5
  121. package/dist/chunks/{velin-email-RBX6ZR4S.js → velin-email-TNFONXPW.js} +1 -1
  122. package/dist/chunks/velin-file-dropzone-QRGF2JNA.js +154 -0
  123. package/dist/chunks/velin-file-dropzone-XIZMJ3NB.js +154 -0
  124. package/dist/chunks/{velin-icon-7M76DOPB.js → velin-icon-A6NCXGUK.js} +59 -17
  125. package/dist/chunks/{velin-icon-ZTSKUOVN.js → velin-icon-EC4B5XCM.js} +18 -5
  126. package/dist/chunks/{velin-lightbox-6E7CAATP.js → velin-lightbox-FFK4TUUP.js} +7 -6
  127. package/dist/chunks/{velin-lightbox-NXA3U2FM.js → velin-lightbox-P725FLTY.js} +5 -4
  128. package/dist/chunks/{velin-menubar-J66JXRWW.js → velin-menubar-6I2LM5HL.js} +1 -1
  129. package/dist/chunks/{velin-modal-5HYNNNUI.js → velin-modal-ICTMGRSU.js} +7 -8
  130. package/dist/chunks/{velin-modal-AEOTZ67O.js → velin-modal-JBNODH7D.js} +2 -4
  131. package/dist/chunks/{velin-popover-T72HK3DF.js → velin-popover-WZG2GHBL.js} +25 -4
  132. package/dist/chunks/{velin-rating-N4DI2XNL.js → velin-rating-QBBYRRBU.js} +1 -1
  133. package/dist/chunks/{velin-search-7B4KJ6TX.js → velin-search-B3L6J2RP.js} +2 -2
  134. package/dist/chunks/{velin-search-2XRQFBCS.js → velin-search-L3UG6NDQ.js} +5 -4
  135. package/dist/chunks/{velin-secure-field-6PV7RZDJ.js → velin-secure-field-DGYMP7OK.js} +1 -1
  136. package/dist/chunks/{velin-segmented-control-GYD5BIXQ.js → velin-segmented-control-P74GJPAR.js} +1 -1
  137. package/dist/chunks/{velin-sheet-R2Y67X6H.js → velin-sheet-M5GZZD7G.js} +2 -4
  138. package/dist/chunks/{velin-sheet-OUK6572T.js → velin-sheet-X7RXIWKV.js} +3 -5
  139. package/dist/chunks/{velin-stepper-X54WGHDG.js → velin-stepper-4YQJUHPC.js} +12 -5
  140. package/dist/chunks/{velin-toast-OMMOA2DK.js → velin-toast-UG5LWTG2.js} +5 -1
  141. package/dist/chunks/{velin-tooltip-BDHGRTQB.js → velin-tooltip-ZDH5SDLU.js} +1 -1
  142. package/dist/llms.txt +31 -8
  143. package/dist/search-index.json +182 -10
  144. package/dist/velin-agent.json +3980 -31
  145. package/dist/velinstyle-components.iife.js +1622 -286
  146. package/dist/velinstyle-components.js +1624 -286
  147. package/dist/velinstyle-components.min.js +262 -114
  148. package/dist/velinstyle.css +412 -67
  149. package/dist/velinstyle.d.ts +2 -0
  150. package/dist/velinstyle.min.css +1 -1
  151. package/package.json +21 -6
  152. package/packages/skill-engine/README.md +24 -0
  153. package/packages/skill-engine/package.json +7 -0
  154. package/packages/skill-engine/src/catalog.js +29 -0
  155. package/packages/skill-engine/src/index.js +5 -0
  156. package/packages/skill-engine/src/install.js +69 -0
  157. package/packages/skill-engine/src/registry.js +27 -0
  158. package/packages/skill-engine/src/resolve.js +68 -0
  159. package/packages/skill-engine/src/validate.js +40 -0
  160. package/packages/velinstyle-skills/README.md +6 -0
  161. package/packages/velinstyle-skills/adapter.json +8 -0
  162. package/packages/velinstyle-skills/bundles/landing-starter.bundle.yaml +12 -0
  163. package/packages/velinstyle-skills/bundles/review-suite.bundle.yaml +11 -0
  164. package/packages/velinstyle-skills/catalog.json +1004 -0
  165. package/packages/velinstyle-skills/graphs/component-ship.graph.yaml +14 -0
  166. package/packages/velinstyle-skills/graphs/landingpage.graph.yaml +21 -0
  167. package/packages/velinstyle-skills/graphs/release-gate.graph.yaml +15 -0
  168. package/packages/velinstyle-skills/index.js +22 -0
  169. package/packages/velinstyle-skills/package.json +7 -0
  170. package/packages/velinstyle-skills/packs/accessibility.pack.yaml +9 -0
  171. package/packages/velinstyle-skills/packs/design.pack.yaml +10 -0
  172. package/packages/velinstyle-skills/packs/documentation.pack.yaml +8 -0
  173. package/packages/velinstyle-skills/packs/enterprise.pack.yaml +10 -0
  174. package/packages/velinstyle-skills/packs/frontend.pack.yaml +10 -0
  175. package/packages/velinstyle-skills/packs/review.pack.yaml +9 -0
  176. package/packages/velinstyle-skills/projects/admin-panel.project.yaml +12 -0
  177. package/packages/velinstyle-skills/projects/blog.project.yaml +11 -0
  178. package/packages/velinstyle-skills/projects/documentation-portal.project.yaml +13 -0
  179. package/packages/velinstyle-skills/projects/ecommerce-shop.project.yaml +12 -0
  180. package/packages/velinstyle-skills/projects/landing-page.project.yaml +12 -0
  181. package/packages/velinstyle-skills/projects/laravel-dashboard.project.yaml +12 -0
  182. package/packages/velinstyle-skills/projects/marketing-website.project.yaml +14 -0
  183. package/packages/velinstyle-skills/projects/portfolio.project.yaml +12 -0
  184. package/packages/velinstyle-skills/registry.json +142 -0
  185. package/packages/velinstyle-skills/scripts/generate-assets.mjs +163 -0
  186. package/packages/velinstyle-skills/skills/velin-a11y-focus-keyboard/SKILL.md +57 -0
  187. package/packages/velinstyle-skills/skills/velin-a11y-page-checklist/SKILL.md +57 -0
  188. package/packages/velinstyle-skills/skills/velin-a11y-scan-fix/SKILL.md +57 -0
  189. package/packages/velinstyle-skills/skills/velin-ai-agent-briefing/SKILL.md +57 -0
  190. package/packages/velinstyle-skills/skills/velin-ai-plan-scaffold-review/SKILL.md +57 -0
  191. package/packages/velinstyle-skills/skills/velin-ai-skill-authoring/SKILL.md +57 -0
  192. package/packages/velinstyle-skills/skills/velin-components-accessibility-map/SKILL.md +57 -0
  193. package/packages/velinstyle-skills/skills/velin-components-api-contract/SKILL.md +57 -0
  194. package/packages/velinstyle-skills/skills/velin-components-recipe/SKILL.md +57 -0
  195. package/packages/velinstyle-skills/skills/velin-components-select-compose/SKILL.md +57 -0
  196. package/packages/velinstyle-skills/skills/velin-design-hero-compose/SKILL.md +57 -0
  197. package/packages/velinstyle-skills/skills/velin-design-section-rhythm/SKILL.md +57 -0
  198. package/packages/velinstyle-skills/skills/velin-design-surface-roles/SKILL.md +57 -0
  199. package/packages/velinstyle-skills/skills/velin-dev-component-wire/SKILL.md +57 -0
  200. package/packages/velinstyle-skills/skills/velin-dev-page-scaffold/SKILL.md +57 -0
  201. package/packages/velinstyle-skills/skills/velin-dev-plan-first/SKILL.md +57 -0
  202. package/packages/velinstyle-skills/skills/velin-docs-component-guide/SKILL.md +57 -0
  203. package/packages/velinstyle-skills/skills/velin-docs-generate-api/SKILL.md +57 -0
  204. package/packages/velinstyle-skills/skills/velin-dx-doctor-setup/SKILL.md +57 -0
  205. package/packages/velinstyle-skills/skills/velin-dx-project-init/SKILL.md +57 -0
  206. package/packages/velinstyle-skills/skills/velin-migration-legacy-wc/SKILL.md +57 -0
  207. package/packages/velinstyle-skills/skills/velin-migration-prefix-classes/SKILL.md +57 -0
  208. package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +57 -0
  209. package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +57 -0
  210. package/packages/velinstyle-skills/skills/velin-perf-audit-fix/SKILL.md +57 -0
  211. package/packages/velinstyle-skills/skills/velin-perf-runtime-tree-shake/SKILL.md +57 -0
  212. package/packages/velinstyle-skills/skills/velin-release-changelog-sync/SKILL.md +57 -0
  213. package/packages/velinstyle-skills/skills/velin-release-meta-rebuild/SKILL.md +57 -0
  214. package/packages/velinstyle-skills/skills/velin-review-conversion-hero/SKILL.md +57 -0
  215. package/packages/velinstyle-skills/skills/velin-review-page-gate/SKILL.md +57 -0
  216. package/packages/velinstyle-skills/skills/velin-review-seo-meta/SKILL.md +57 -0
  217. package/packages/velinstyle-skills/skills/velin-security-sanitize-html/SKILL.md +57 -0
  218. package/packages/velinstyle-skills/skills/velin-security-scan-pii/SKILL.md +57 -0
  219. package/packages/velinstyle-skills/skills/velin-testing-a11y-suite/SKILL.md +57 -0
  220. package/packages/velinstyle-skills/skills/velin-testing-visual-smoke/SKILL.md +57 -0
  221. package/packages/velinstyle-skills/skills/velin-themes-apply-token/SKILL.md +57 -0
  222. package/packages/velinstyle-skills/skills/velin-themes-contrast-check/SKILL.md +57 -0
  223. package/packages/velinstyle-skills/skills/velin-utilities-layout-apply/SKILL.md +57 -0
  224. package/packages/velinstyle-skills/skills/velin-utilities-prefix-fix/SKILL.md +57 -0
  225. package/packages/velinstyle-skills/skills/velin-utilities-token-map/SKILL.md +57 -0
  226. package/packages/velinstyle-skills/templates/admin-panel.template.yaml +12 -0
  227. package/packages/velinstyle-skills/templates/blog.template.yaml +12 -0
  228. package/packages/velinstyle-skills/templates/documentation-portal.template.yaml +12 -0
  229. package/packages/velinstyle-skills/templates/ecommerce-shop.template.yaml +13 -0
  230. package/packages/velinstyle-skills/templates/landing-page.template.yaml +12 -0
  231. package/packages/velinstyle-skills/templates/laravel-dashboard.template.yaml +11 -0
  232. package/packages/velinstyle-skills/templates/marketing-website.template.yaml +13 -0
  233. package/packages/velinstyle-skills/templates/portfolio.template.yaml +12 -0
  234. package/schemas/component-knowledge.schema.json +33 -0
  235. package/schemas/design-constraint.schema.json +15 -0
  236. package/schemas/design-intelligence.schema.json +19 -0
  237. package/schemas/design-rule.schema.json +19 -0
  238. package/schemas/page-template.schema.json +24 -0
  239. package/schemas/review-report.schema.json +36 -0
  240. package/schemas/section.schema.json +22 -0
  241. package/schemas/skill-bundle.schema.json +15 -0
  242. package/schemas/skill-graph.schema.json +25 -0
  243. package/schemas/skill-pack.schema.json +14 -0
  244. package/schemas/skill-project.schema.json +16 -0
  245. package/schemas/skill-record.schema.json +27 -0
  246. package/schemas/skill-registry.schema.json +17 -0
  247. package/schemas/skill-template.schema.json +17 -0
  248. package/schemas/token-graph.schema.json +27 -0
  249. package/schemas/utility.schema.json +166 -0
  250. package/src/components/alert.css +22 -1
  251. package/src/components/calendar-dropzone.css +5 -0
  252. package/src/components/transparency.css +138 -0
  253. package/src/layout/app-shell.css +33 -0
  254. package/src/tokens/shadow.css +13 -0
  255. package/src/utilities/border.css +2 -0
  256. package/src/utilities/display.css +1 -24
  257. package/src/utilities/responsive.css +16 -1
  258. package/src/utilities/spacing.css +32 -0
  259. package/src/velinstyle.css +3 -0
  260. package/dist/chunks/attributes-3BZLI7XF.js +0 -364
  261. package/dist/chunks/attributes-4N5JP4CG.js +0 -364
  262. package/dist/chunks/attributes-5Q2WXGRC.js +0 -374
  263. package/dist/chunks/attributes-AIR7SVLK.js +0 -364
  264. package/dist/chunks/attributes-BGRN5XZO.js +0 -364
  265. package/dist/chunks/attributes-BVSQAYLR.js +0 -364
  266. package/dist/chunks/attributes-CDYFUY7Y.js +0 -364
  267. package/dist/chunks/attributes-JMYJZBPE.js +0 -364
  268. package/dist/chunks/attributes-R5JY3X4O.js +0 -374
  269. package/dist/chunks/attributes-TNSPQTHX.js +0 -374
  270. package/dist/chunks/chunk-2354NEBP.js +0 -109
  271. package/dist/chunks/chunk-2VNA7G7S.js +0 -109
  272. package/dist/chunks/chunk-AQLRJSGG.js +0 -109
  273. package/dist/chunks/chunk-EWPSDMQT.js +0 -109
  274. package/dist/chunks/chunk-LA7264K4.js +0 -85
  275. package/dist/chunks/chunk-QTJJ3DPO.js +0 -109
  276. package/dist/chunks/chunk-RZQVWFHW.js +0 -109
  277. package/dist/chunks/chunk-U4XHMZ7D.js +0 -109
  278. package/dist/chunks/chunk-V5YIUA6G.js +0 -109
  279. package/dist/chunks/chunk-XIET2VSZ.js +0 -109
  280. package/dist/chunks/chunk-XYLJ2GCB.js +0 -205
  281. package/dist/chunks/chunk-ZPSPKUYD.js +0 -109
  282. package/dist/chunks/highlight-XTHRKBA3.js +0 -37
  283. package/dist/chunks/velin-bottom-nav-2N2TYWOF.js +0 -86
  284. package/dist/chunks/velin-carousel-Y5VCOABZ.js +0 -213
  285. package/dist/chunks/velin-code-block-ASUBTAQV.js +0 -119
  286. package/dist/chunks/velin-code-block-TSUKPY4C.js +0 -123
  287. package/dist/chunks/velin-code-block-Y6CM726Q.js +0 -119
  288. package/dist/chunks/velin-combobox-2KDTCQ7N.js +0 -155
  289. package/dist/chunks/velin-command-M7RNHUPR.js +0 -136
  290. package/dist/chunks/velin-copy-T5KDTDXP.js +0 -81
  291. package/dist/chunks/velin-countdown-5XSTN3BH.js +0 -101
  292. package/dist/chunks/velin-counter-ISCTEAHI.js +0 -118
  293. package/dist/chunks/velin-dialog-D2GUHSCD.js +0 -144
  294. package/dist/chunks/velin-dialog-IJLFDXAJ.js +0 -144
  295. package/dist/chunks/velin-dialog-TNS3WXMB.js +0 -144
  296. package/dist/chunks/velin-drawer-MJS6WCF7.js +0 -130
  297. package/dist/chunks/velin-email-N5B5ZYTE.js +0 -90
  298. package/dist/chunks/velin-icon-KZO3IEHG.js +0 -164
  299. package/dist/chunks/velin-lightbox-I75U6JF4.js +0 -140
  300. package/dist/chunks/velin-lightbox-QOTB4PRL.js +0 -140
  301. package/dist/chunks/velin-lightbox-UCD6N7HF.js +0 -140
  302. package/dist/chunks/velin-live-dot-XHUZCIDZ.js +0 -67
  303. package/dist/chunks/velin-menubar-SFEXJE56.js +0 -86
  304. package/dist/chunks/velin-modal-2HOBXE6F.js +0 -176
  305. package/dist/chunks/velin-modal-GLI2XZZB.js +0 -176
  306. package/dist/chunks/velin-popover-EXINRIGJ.js +0 -145
  307. package/dist/chunks/velin-popover-OXX7MRZ6.js +0 -145
  308. package/dist/chunks/velin-popover-YKCCROJG.js +0 -145
  309. package/dist/chunks/velin-rating-XRQJV4LO.js +0 -91
  310. package/dist/chunks/velin-scroll-top-F2FCZNYD.js +0 -58
  311. package/dist/chunks/velin-search-4YHJWYSS.js +0 -556
  312. package/dist/chunks/velin-search-LWHY23Q5.js +0 -474
  313. package/dist/chunks/velin-secure-field-H7FSBUJW.js +0 -95
  314. package/dist/chunks/velin-segmented-control-PQX6LKOJ.js +0 -109
  315. package/dist/chunks/velin-sheet-3OGI4HD7.js +0 -119
  316. package/dist/chunks/velin-sparkline-GVEANJVW.js +0 -184
  317. package/dist/chunks/velin-sparkline-KDA3GQAN.js +0 -162
  318. package/dist/chunks/velin-stepper-2ZIHLKGM.js +0 -141
  319. package/dist/chunks/velin-stepper-FYEUEWE6.js +0 -141
  320. package/dist/chunks/velin-stepper-wc-QU3KRVQR.js +0 -133
  321. package/dist/chunks/velin-theme-toggle-BNNS7WLV.js +0 -285
  322. package/dist/chunks/velin-theme-toggle-NO7V4TWI.js +0 -285
  323. package/dist/chunks/velin-theme-toggle-QELCJM2Q.js +0 -291
  324. package/dist/chunks/velin-toast-RCZ4CSUC.js +0 -109
  325. package/dist/chunks/velin-toast-WEUBTJG6.js +0 -109
  326. package/dist/chunks/velin-tooltip-664IOHZA.js +0 -112
  327. package/dist/chunks/velin-tooltip-wc-LXDVT6DE.js +0 -103
  328. 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
+ };