@birdapi/velinstyle 1.0.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.
- package/README.de.md +87 -158
- package/README.md +188 -115
- package/cli/blueprint.js +98 -5
- package/cli/blueprints/app-chrome.html +18 -0
- package/cli/blueprints/benefits-grid.html +9 -0
- package/cli/blueprints/bottom-nav-mobile.html +2 -1
- package/cli/blueprints/cookie-consent.html +1 -1
- package/cli/blueprints/cta-band.html +6 -0
- package/cli/blueprints/faq-section.html +18 -0
- package/cli/blueprints/filter-bar.html +4 -4
- package/cli/blueprints/form-contact.html +6 -5
- package/cli/blueprints/hero-section.html +3 -3
- package/cli/blueprints/notification-center.html +1 -1
- package/cli/blueprints/onboarding.html +2 -1
- package/cli/blueprints/ops-console.html +29 -0
- package/cli/blueprints/pricing-band.html +37 -0
- package/cli/blueprints/process-steps.html +10 -0
- package/cli/blueprints/services-grid.html +9 -0
- package/cli/blueprints/settings-panel.html +8 -6
- package/cli/blueprints/split-hero.html +25 -0
- package/cli/blueprints/testimonials.html +8 -0
- package/cli/blueprints/trust-bar.html +10 -0
- package/cli/cli-manifest.json +81 -17
- package/cli/contrast-utils.js +97 -0
- package/cli/create.js +370 -0
- package/cli/docgen/extract-a11y.js +4 -2
- package/cli/docgen/extract-attributes.js +126 -10
- package/cli/docgen/extract-cli.js +3 -2
- package/cli/docgen/extract-components.js +61 -2
- package/cli/docgen/extract-utilities.js +10 -10
- package/cli/doctor.js +119 -0
- package/cli/index.js +535 -46
- package/cli/meta.js +84 -1
- package/cli/perf-audit.js +15 -2
- package/cli/prompt-engine.js +243 -0
- package/cli/review.js +268 -0
- package/cli/scaffold.js +119 -4
- package/cli/scanner-rules-data.js +7 -0
- package/cli/scanner.js +357 -4
- package/cli/serve.js +81 -0
- package/cli/skills.js +223 -0
- package/cli/wc-api.js +89 -0
- package/cli/workflow.js +42 -0
- package/components/index.js +4 -0
- package/components/runtime/component-loaders.js +4 -0
- package/components/velin-accordion.js +62 -13
- package/components/velin-bottom-nav.js +10 -0
- package/components/velin-calendar.js +212 -0
- package/components/velin-command.js +18 -2
- package/components/velin-data-table.js +438 -0
- package/components/velin-dialog.js +4 -1
- package/components/velin-drawer.js +2 -4
- package/components/velin-file-dropzone.js +162 -0
- package/components/velin-form-summary.js +348 -0
- package/components/velin-icon.js +22 -1
- package/components/velin-lightbox.js +5 -3
- package/components/velin-modal.js +2 -4
- package/components/velin-search.js +1 -1
- package/components/velin-sheet.js +2 -4
- package/core/a11y/component-contracts.json +35 -1
- package/core/highlight/languages/go.js +28 -0
- package/core/highlight/languages/python.js +29 -0
- package/core/highlight/languages/rust.js +32 -0
- package/core/highlight/languages/yaml.js +23 -0
- package/core/highlight/registry.js +13 -2
- package/core/meta/build.js +126 -6
- package/core/meta/design-constraints/contact.json +9 -0
- package/core/meta/design-constraints/faq.json +9 -0
- package/core/meta/design-constraints/hero.json +13 -0
- package/core/meta/knowledge/components.json +622 -0
- package/core/meta/knowledge/tokens.json +282 -0
- package/core/meta/pages/registry.json +294 -0
- package/core/meta/schema.js +3 -1
- package/core/meta/sections/registry.json +185 -0
- package/dist/chunks/{attributes-TPANLMLF.js → attributes-A423NX5J.js} +3 -3
- package/dist/chunks/{attributes-ADOKLKA7.js → attributes-F6UPIEVP.js} +2 -2
- package/dist/chunks/{attributes-STYCS3O5.js → attributes-GLYXGGAL.js} +3 -3
- package/dist/chunks/{attributes-5Q2WXGRC.js → attributes-UFTYOSAU.js} +20 -3
- package/dist/chunks/attributes-XGLXLR2T.js +391 -0
- package/dist/chunks/attributes-YF4NI2QS.js +391 -0
- package/dist/chunks/browser-AUABBYUK.js +1544 -0
- package/dist/chunks/{chunk-SRL6EWHP.js → chunk-2LSKX57P.js} +13 -11
- package/dist/chunks/{chunk-RSMRR27R.js → chunk-2TGCVDUL.js} +11 -9
- package/dist/chunks/{chunk-XIET2VSZ.js → chunk-5JWGUAPC.js} +17 -13
- package/dist/chunks/chunk-7YSO2OQ2.js +113 -0
- package/dist/chunks/{chunk-IEHKRARD.js → chunk-BHR3ZG6U.js} +10 -8
- package/dist/chunks/{chunk-J472ERN2.js → chunk-ERF5YVP4.js} +15 -4
- package/dist/chunks/{chunk-XYLJ2GCB.js → chunk-JMSIHEYX.js} +55 -7
- package/dist/chunks/{chunk-MKZP2TBH.js → chunk-L7ZGUU4D.js} +2 -2
- package/dist/chunks/chunk-TUGIPKW5.js +113 -0
- package/dist/chunks/go-HZ6XTFCK.js +31 -0
- package/dist/chunks/{highlight-GJHHV4KM.js → highlight-IBDX4XWS.js} +1 -1
- package/dist/chunks/{highlight-XTHRKBA3.js → highlight-M2ELQNYK.js} +1 -1
- package/dist/chunks/python-BMPKDKNO.js +32 -0
- package/dist/chunks/runtime-entry.js +1 -1
- package/dist/chunks/rust-2BWKI2MN.js +35 -0
- package/dist/chunks/{velin-accordion-FBIPAEXG.js → velin-accordion-WSY6BMLA.js} +64 -10
- package/dist/chunks/{velin-bottom-nav-7GEMPIM6.js → velin-bottom-nav-5RVRNGIP.js} +25 -0
- package/dist/chunks/{velin-bottom-nav-2N2TYWOF.js → velin-bottom-nav-N5F4UO2I.js} +26 -1
- package/dist/chunks/velin-calendar-OFE6THUD.js +210 -0
- package/dist/chunks/velin-calendar-PGVNJTXK.js +217 -0
- package/dist/chunks/velin-calendar-XC7JRDSH.js +217 -0
- package/dist/chunks/{velin-code-block-CIAURDRJ.js → velin-code-block-ITCLIC6T.js} +1 -1
- package/dist/chunks/{velin-code-block-TSUKPY4C.js → velin-code-block-ZGWG7JI5.js} +12 -3
- package/dist/chunks/{velin-combobox-NXS4QB4R.js → velin-combobox-56V2ZWT5.js} +1 -1
- package/dist/chunks/{velin-command-ONKO2A3J.js → velin-command-2HO2EKR6.js} +29 -2
- package/dist/chunks/{velin-command-TMLSRGIU.js → velin-command-ZJSLPK7B.js} +30 -3
- package/dist/chunks/{velin-copy-NYDBOKXT.js → velin-copy-XDQSV2XU.js} +1 -1
- package/dist/chunks/{velin-countdown-4ZVDGK4R.js → velin-countdown-V4Z5MA63.js} +1 -1
- package/dist/chunks/velin-data-table-KK7IDGTP.js +302 -0
- package/dist/chunks/velin-data-table-UVR42UUM.js +372 -0
- package/dist/chunks/{velin-dialog-GTTZI564.js → velin-dialog-3YK2RPRV.js} +9 -2
- package/dist/chunks/{velin-dialog-ELFOZIXZ.js → velin-dialog-S24KAP4S.js} +3 -1
- package/dist/chunks/{velin-drawer-BE6TYN3H.js → velin-drawer-5PNU624J.js} +2 -4
- package/dist/chunks/{velin-drawer-DLL2GLT6.js → velin-drawer-CCM6S25D.js} +3 -5
- package/dist/chunks/{velin-email-RBX6ZR4S.js → velin-email-TNFONXPW.js} +1 -1
- package/dist/chunks/velin-file-dropzone-QRGF2JNA.js +154 -0
- package/dist/chunks/velin-file-dropzone-XIZMJ3NB.js +154 -0
- package/dist/chunks/velin-form-summary-XRQ7COQH.js +273 -0
- package/dist/chunks/{velin-icon-7M76DOPB.js → velin-icon-A6NCXGUK.js} +59 -17
- package/dist/chunks/{velin-icon-ZTSKUOVN.js → velin-icon-EC4B5XCM.js} +18 -5
- package/dist/chunks/{velin-lightbox-6E7CAATP.js → velin-lightbox-FFK4TUUP.js} +7 -6
- package/dist/chunks/{velin-lightbox-I75U6JF4.js → velin-lightbox-P725FLTY.js} +15 -5
- package/dist/chunks/{velin-menubar-J66JXRWW.js → velin-menubar-6I2LM5HL.js} +1 -1
- package/dist/chunks/{velin-modal-5HYNNNUI.js → velin-modal-ICTMGRSU.js} +7 -8
- package/dist/chunks/{velin-modal-AEOTZ67O.js → velin-modal-JBNODH7D.js} +2 -4
- package/dist/chunks/{velin-popover-T72HK3DF.js → velin-popover-WZG2GHBL.js} +25 -4
- package/dist/chunks/{velin-rating-N4DI2XNL.js → velin-rating-QBBYRRBU.js} +1 -1
- package/dist/chunks/{velin-search-7B4KJ6TX.js → velin-search-B3L6J2RP.js} +2 -2
- package/dist/chunks/{velin-search-2XRQFBCS.js → velin-search-L3UG6NDQ.js} +5 -4
- package/dist/chunks/{velin-secure-field-6PV7RZDJ.js → velin-secure-field-DGYMP7OK.js} +1 -1
- package/dist/chunks/{velin-segmented-control-GYD5BIXQ.js → velin-segmented-control-P74GJPAR.js} +1 -1
- package/dist/chunks/{velin-sheet-R2Y67X6H.js → velin-sheet-M5GZZD7G.js} +2 -4
- package/dist/chunks/{velin-sheet-OUK6572T.js → velin-sheet-X7RXIWKV.js} +3 -5
- package/dist/chunks/{velin-stepper-X54WGHDG.js → velin-stepper-4YQJUHPC.js} +12 -5
- package/dist/chunks/{velin-toast-OMMOA2DK.js → velin-toast-UG5LWTG2.js} +5 -1
- package/dist/chunks/{velin-tooltip-BDHGRTQB.js → velin-tooltip-ZDH5SDLU.js} +1 -1
- package/dist/chunks/yaml-O67LEQYT.js +26 -0
- package/dist/llms.txt +29 -7
- package/dist/search-index.json +182 -10
- package/dist/velin-agent.json +3997 -26
- package/dist/velinstyle-components.iife.js +1952 -287
- package/dist/velinstyle-components.js +1956 -287
- package/dist/velinstyle-components.min.js +267 -119
- package/dist/velinstyle.css +381 -67
- package/dist/velinstyle.d.ts +4 -0
- package/dist/velinstyle.min.css +1 -1
- package/package.json +26 -6
- package/packages/skill-engine/README.md +24 -0
- package/packages/skill-engine/package.json +7 -0
- package/packages/skill-engine/src/catalog.js +29 -0
- package/packages/skill-engine/src/index.js +5 -0
- package/packages/skill-engine/src/install.js +69 -0
- package/packages/skill-engine/src/registry.js +27 -0
- package/packages/skill-engine/src/resolve.js +68 -0
- package/packages/skill-engine/src/validate.js +40 -0
- package/packages/velinstyle-skills/README.md +6 -0
- package/packages/velinstyle-skills/adapter.json +8 -0
- package/packages/velinstyle-skills/bundles/landing-starter.bundle.yaml +12 -0
- package/packages/velinstyle-skills/bundles/review-suite.bundle.yaml +11 -0
- package/packages/velinstyle-skills/catalog.json +1004 -0
- package/packages/velinstyle-skills/graphs/component-ship.graph.yaml +14 -0
- package/packages/velinstyle-skills/graphs/landingpage.graph.yaml +21 -0
- package/packages/velinstyle-skills/graphs/release-gate.graph.yaml +15 -0
- package/packages/velinstyle-skills/index.js +22 -0
- package/packages/velinstyle-skills/package.json +7 -0
- package/packages/velinstyle-skills/packs/accessibility.pack.yaml +9 -0
- package/packages/velinstyle-skills/packs/design.pack.yaml +10 -0
- package/packages/velinstyle-skills/packs/documentation.pack.yaml +8 -0
- package/packages/velinstyle-skills/packs/enterprise.pack.yaml +10 -0
- package/packages/velinstyle-skills/packs/frontend.pack.yaml +10 -0
- package/packages/velinstyle-skills/packs/review.pack.yaml +9 -0
- package/packages/velinstyle-skills/projects/admin-panel.project.yaml +12 -0
- package/packages/velinstyle-skills/projects/blog.project.yaml +11 -0
- package/packages/velinstyle-skills/projects/documentation-portal.project.yaml +13 -0
- package/packages/velinstyle-skills/projects/ecommerce-shop.project.yaml +12 -0
- package/packages/velinstyle-skills/projects/landing-page.project.yaml +12 -0
- package/packages/velinstyle-skills/projects/laravel-dashboard.project.yaml +12 -0
- package/packages/velinstyle-skills/projects/marketing-website.project.yaml +14 -0
- package/packages/velinstyle-skills/projects/portfolio.project.yaml +12 -0
- package/packages/velinstyle-skills/registry.json +142 -0
- package/packages/velinstyle-skills/scripts/generate-assets.mjs +163 -0
- package/packages/velinstyle-skills/skills/velin-a11y-focus-keyboard/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-a11y-page-checklist/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-a11y-scan-fix/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-ai-agent-briefing/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-ai-plan-scaffold-review/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-ai-skill-authoring/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-components-accessibility-map/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-components-api-contract/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-components-recipe/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-components-select-compose/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-design-hero-compose/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-design-section-rhythm/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-design-surface-roles/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-dev-component-wire/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-dev-page-scaffold/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-dev-plan-first/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-docs-component-guide/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-docs-generate-api/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-dx-doctor-setup/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-dx-project-init/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-migration-legacy-wc/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-migration-prefix-classes/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-perf-audit-fix/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-perf-runtime-tree-shake/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-release-changelog-sync/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-release-meta-rebuild/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-review-conversion-hero/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-review-page-gate/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-review-seo-meta/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-security-sanitize-html/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-security-scan-pii/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-testing-a11y-suite/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-testing-visual-smoke/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-themes-apply-token/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-themes-contrast-check/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-utilities-layout-apply/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-utilities-prefix-fix/SKILL.md +57 -0
- package/packages/velinstyle-skills/skills/velin-utilities-token-map/SKILL.md +57 -0
- package/packages/velinstyle-skills/templates/admin-panel.template.yaml +12 -0
- package/packages/velinstyle-skills/templates/blog.template.yaml +12 -0
- package/packages/velinstyle-skills/templates/documentation-portal.template.yaml +12 -0
- package/packages/velinstyle-skills/templates/ecommerce-shop.template.yaml +13 -0
- package/packages/velinstyle-skills/templates/landing-page.template.yaml +12 -0
- package/packages/velinstyle-skills/templates/laravel-dashboard.template.yaml +11 -0
- package/packages/velinstyle-skills/templates/marketing-website.template.yaml +13 -0
- package/packages/velinstyle-skills/templates/portfolio.template.yaml +12 -0
- package/schemas/component-knowledge.schema.json +33 -0
- package/schemas/design-constraint.schema.json +15 -0
- package/schemas/design-intelligence.schema.json +19 -0
- package/schemas/design-rule.schema.json +19 -0
- package/schemas/page-template.schema.json +24 -0
- package/schemas/review-report.schema.json +36 -0
- package/schemas/section.schema.json +22 -0
- package/schemas/skill-bundle.schema.json +15 -0
- package/schemas/skill-graph.schema.json +25 -0
- package/schemas/skill-pack.schema.json +14 -0
- package/schemas/skill-project.schema.json +16 -0
- package/schemas/skill-record.schema.json +27 -0
- package/schemas/skill-registry.schema.json +17 -0
- package/schemas/skill-template.schema.json +17 -0
- package/schemas/token-graph.schema.json +27 -0
- package/schemas/utility.schema.json +166 -0
- package/src/base/wc-placeholder.css +7 -0
- package/src/components/alert.css +22 -1
- package/src/components/calendar-dropzone.css +5 -0
- package/src/components/data-table.css +93 -0
- package/src/components/form-validation.css +40 -0
- package/src/layout/app-shell.css +33 -0
- package/src/tokens/shadow.css +13 -0
- package/src/utilities/border.css +2 -0
- package/src/utilities/display.css +1 -24
- package/src/utilities/responsive.css +16 -1
- package/src/utilities/spacing.css +32 -0
- package/src/velinstyle.css +3 -0
- package/dist/chunks/attributes-3BZLI7XF.js +0 -364
- package/dist/chunks/attributes-4N5JP4CG.js +0 -364
- package/dist/chunks/attributes-AIR7SVLK.js +0 -364
- package/dist/chunks/attributes-BGRN5XZO.js +0 -364
- package/dist/chunks/attributes-BVSQAYLR.js +0 -364
- package/dist/chunks/attributes-CDYFUY7Y.js +0 -364
- package/dist/chunks/attributes-JMYJZBPE.js +0 -364
- package/dist/chunks/attributes-R5JY3X4O.js +0 -374
- package/dist/chunks/attributes-TNSPQTHX.js +0 -374
- package/dist/chunks/chunk-2354NEBP.js +0 -109
- package/dist/chunks/chunk-2VNA7G7S.js +0 -109
- package/dist/chunks/chunk-AQLRJSGG.js +0 -109
- package/dist/chunks/chunk-EWPSDMQT.js +0 -109
- package/dist/chunks/chunk-LA7264K4.js +0 -85
- package/dist/chunks/chunk-QTJJ3DPO.js +0 -109
- package/dist/chunks/chunk-RZQVWFHW.js +0 -109
- package/dist/chunks/chunk-U4XHMZ7D.js +0 -109
- package/dist/chunks/chunk-V5YIUA6G.js +0 -109
- package/dist/chunks/chunk-ZPSPKUYD.js +0 -109
- package/dist/chunks/velin-bottom-nav-N3DFQR66.js +0 -86
- package/dist/chunks/velin-carousel-Y5VCOABZ.js +0 -213
- package/dist/chunks/velin-code-block-ASUBTAQV.js +0 -119
- package/dist/chunks/velin-code-block-Y6CM726Q.js +0 -119
- package/dist/chunks/velin-combobox-2KDTCQ7N.js +0 -155
- package/dist/chunks/velin-command-M7RNHUPR.js +0 -136
- package/dist/chunks/velin-copy-T5KDTDXP.js +0 -81
- package/dist/chunks/velin-countdown-5XSTN3BH.js +0 -101
- package/dist/chunks/velin-counter-ISCTEAHI.js +0 -118
- package/dist/chunks/velin-dialog-D2GUHSCD.js +0 -144
- package/dist/chunks/velin-dialog-IJLFDXAJ.js +0 -144
- package/dist/chunks/velin-dialog-TNS3WXMB.js +0 -144
- package/dist/chunks/velin-drawer-MJS6WCF7.js +0 -130
- package/dist/chunks/velin-email-N5B5ZYTE.js +0 -90
- package/dist/chunks/velin-icon-KZO3IEHG.js +0 -164
- package/dist/chunks/velin-lightbox-QOTB4PRL.js +0 -140
- package/dist/chunks/velin-lightbox-UCD6N7HF.js +0 -140
- package/dist/chunks/velin-live-dot-XHUZCIDZ.js +0 -67
- package/dist/chunks/velin-menubar-SFEXJE56.js +0 -86
- package/dist/chunks/velin-modal-2HOBXE6F.js +0 -176
- package/dist/chunks/velin-modal-GLI2XZZB.js +0 -176
- package/dist/chunks/velin-popover-EXINRIGJ.js +0 -145
- package/dist/chunks/velin-popover-OXX7MRZ6.js +0 -145
- package/dist/chunks/velin-popover-YKCCROJG.js +0 -145
- package/dist/chunks/velin-rating-XRQJV4LO.js +0 -91
- package/dist/chunks/velin-scroll-top-F2FCZNYD.js +0 -58
- package/dist/chunks/velin-search-4YHJWYSS.js +0 -556
- package/dist/chunks/velin-search-LWHY23Q5.js +0 -474
- package/dist/chunks/velin-secure-field-H7FSBUJW.js +0 -95
- package/dist/chunks/velin-segmented-control-PQX6LKOJ.js +0 -109
- package/dist/chunks/velin-sheet-3OGI4HD7.js +0 -119
- package/dist/chunks/velin-sparkline-GVEANJVW.js +0 -184
- package/dist/chunks/velin-sparkline-KDA3GQAN.js +0 -162
- package/dist/chunks/velin-stepper-2ZIHLKGM.js +0 -141
- package/dist/chunks/velin-stepper-FYEUEWE6.js +0 -141
- package/dist/chunks/velin-stepper-wc-QU3KRVQR.js +0 -133
- package/dist/chunks/velin-theme-toggle-BNNS7WLV.js +0 -285
- package/dist/chunks/velin-theme-toggle-NO7V4TWI.js +0 -285
- package/dist/chunks/velin-theme-toggle-QELCJM2Q.js +0 -291
- package/dist/chunks/velin-toast-RCZ4CSUC.js +0 -109
- package/dist/chunks/velin-toast-WEUBTJG6.js +0 -109
- package/dist/chunks/velin-tooltip-664IOHZA.js +0 -112
- package/dist/chunks/velin-tooltip-wc-LXDVT6DE.js +0 -103
- package/dist/chunks/worker-client-R6DBYPFT.js +0 -46
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import {
|
|
2
|
+
announce
|
|
3
|
+
} from "./chunk-Z4JYQ2XX.js";
|
|
4
|
+
|
|
5
|
+
// components/velin-form-summary.js
|
|
6
|
+
var FIELD_SELECTOR = "input, select, textarea";
|
|
7
|
+
var IGNORED_TYPES = /* @__PURE__ */ new Set(["submit", "reset", "button", "image", "hidden"]);
|
|
8
|
+
var fieldIdCounter = 0;
|
|
9
|
+
function escapeSelector(value) {
|
|
10
|
+
const text = String(value ?? "");
|
|
11
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") return CSS.escape(text);
|
|
12
|
+
return text.replace(/[^\w-]/g, (char) => `\\${char}`);
|
|
13
|
+
}
|
|
14
|
+
function fieldLabel(field) {
|
|
15
|
+
const explicit = field.getAttribute("data-error-label");
|
|
16
|
+
if (explicit) return explicit;
|
|
17
|
+
const ariaLabel = field.getAttribute("aria-label");
|
|
18
|
+
if (ariaLabel?.trim()) return ariaLabel.trim();
|
|
19
|
+
const labelledBy = field.getAttribute("aria-labelledby");
|
|
20
|
+
if (labelledBy) {
|
|
21
|
+
const text = labelledBy.split(/\s+/).map((id) => field.ownerDocument.getElementById(id)?.textContent?.trim() || "").filter(Boolean).join(" ");
|
|
22
|
+
if (text) return text;
|
|
23
|
+
}
|
|
24
|
+
if (field.id) {
|
|
25
|
+
const label = field.ownerDocument.querySelector(`label[for="${escapeSelector(field.id)}"]`);
|
|
26
|
+
if (label?.textContent.trim()) return label.textContent.trim();
|
|
27
|
+
}
|
|
28
|
+
const wrapping = field.closest("label");
|
|
29
|
+
if (wrapping?.textContent.trim()) return wrapping.textContent.trim();
|
|
30
|
+
return field.name || "This field";
|
|
31
|
+
}
|
|
32
|
+
function fieldMessage(field) {
|
|
33
|
+
return field.getAttribute("data-error-message")?.trim() || field.validationMessage || "Invalid value";
|
|
34
|
+
}
|
|
35
|
+
function addDescribedBy(field, id) {
|
|
36
|
+
const ids = (field.getAttribute("aria-describedby") || "").split(/\s+/).filter(Boolean);
|
|
37
|
+
if (!ids.includes(id)) {
|
|
38
|
+
ids.push(id);
|
|
39
|
+
field.setAttribute("aria-describedby", ids.join(" "));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function removeDescribedBy(field, id) {
|
|
43
|
+
const ids = (field.getAttribute("aria-describedby") || "").split(/\s+/).filter(Boolean);
|
|
44
|
+
const next = ids.filter((value) => value !== id);
|
|
45
|
+
if (next.length) field.setAttribute("aria-describedby", next.join(" "));
|
|
46
|
+
else field.removeAttribute("aria-describedby");
|
|
47
|
+
}
|
|
48
|
+
var VelinFormSummary = class extends HTMLElement {
|
|
49
|
+
static get observedAttributes() {
|
|
50
|
+
return ["for", "heading"];
|
|
51
|
+
}
|
|
52
|
+
constructor() {
|
|
53
|
+
super();
|
|
54
|
+
this._form = null;
|
|
55
|
+
this._panel = null;
|
|
56
|
+
this._errors = [];
|
|
57
|
+
this._onSubmit = this._onSubmit.bind(this);
|
|
58
|
+
this._onFieldChange = this._onFieldChange.bind(this);
|
|
59
|
+
this._onReset = this._onReset.bind(this);
|
|
60
|
+
}
|
|
61
|
+
connectedCallback() {
|
|
62
|
+
this.classList.add("velin-form-summary");
|
|
63
|
+
requestAnimationFrame(() => this._bindForm());
|
|
64
|
+
}
|
|
65
|
+
disconnectedCallback() {
|
|
66
|
+
this._unbindForm();
|
|
67
|
+
}
|
|
68
|
+
attributeChangedCallback(name, previous, next) {
|
|
69
|
+
if (previous === next) return;
|
|
70
|
+
if (name === "for" && this.isConnected) {
|
|
71
|
+
this._unbindForm();
|
|
72
|
+
this._bindForm();
|
|
73
|
+
} else if (name === "heading" && this._panel) {
|
|
74
|
+
const heading = this._panel.querySelector(".velin-form-summary__heading");
|
|
75
|
+
if (heading) heading.textContent = this.headingText;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
79
|
+
get form() {
|
|
80
|
+
return this._form;
|
|
81
|
+
}
|
|
82
|
+
/** @returns {{ field: HTMLElement, label: string, message: string }[]} */
|
|
83
|
+
get errors() {
|
|
84
|
+
return this._errors.slice();
|
|
85
|
+
}
|
|
86
|
+
get headingText() {
|
|
87
|
+
return this.getAttribute("heading") || "There is a problem";
|
|
88
|
+
}
|
|
89
|
+
/** Validate the form and render the summary. @returns {boolean} valid */
|
|
90
|
+
validate() {
|
|
91
|
+
if (!this._form) return true;
|
|
92
|
+
const errors = [];
|
|
93
|
+
for (const field of this._fields()) {
|
|
94
|
+
if (field.checkValidity()) {
|
|
95
|
+
this._clearFieldError(field);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const error = { field, label: fieldLabel(field), message: fieldMessage(field) };
|
|
99
|
+
this._markFieldError(field, error.message);
|
|
100
|
+
errors.push(error);
|
|
101
|
+
}
|
|
102
|
+
this._errors = errors;
|
|
103
|
+
this._render();
|
|
104
|
+
return errors.length === 0;
|
|
105
|
+
}
|
|
106
|
+
/** Remove the summary and all field error state. */
|
|
107
|
+
clear() {
|
|
108
|
+
for (const field of this._fields()) this._clearFieldError(field);
|
|
109
|
+
this._errors = [];
|
|
110
|
+
this._render();
|
|
111
|
+
}
|
|
112
|
+
/** Move focus to the first field with an error. */
|
|
113
|
+
focusFirstError() {
|
|
114
|
+
const first = this._errors[0];
|
|
115
|
+
if (first) this._focusField(first.field);
|
|
116
|
+
}
|
|
117
|
+
// ── Form wiring ────────────────────────────────────────────────────────────
|
|
118
|
+
_bindForm() {
|
|
119
|
+
const id = this.getAttribute("for");
|
|
120
|
+
this._form = id ? this.ownerDocument.getElementById(id) : this.closest("form");
|
|
121
|
+
if (!this._form) return;
|
|
122
|
+
if (!this.hasAttribute("native-validation")) this._form.noValidate = true;
|
|
123
|
+
this._form.addEventListener("submit", this._onSubmit);
|
|
124
|
+
this._form.addEventListener("reset", this._onReset);
|
|
125
|
+
this._form.addEventListener("input", this._onFieldChange);
|
|
126
|
+
this._form.addEventListener("change", this._onFieldChange);
|
|
127
|
+
}
|
|
128
|
+
_unbindForm() {
|
|
129
|
+
if (!this._form) return;
|
|
130
|
+
this._form.removeEventListener("submit", this._onSubmit);
|
|
131
|
+
this._form.removeEventListener("reset", this._onReset);
|
|
132
|
+
this._form.removeEventListener("input", this._onFieldChange);
|
|
133
|
+
this._form.removeEventListener("change", this._onFieldChange);
|
|
134
|
+
this._form = null;
|
|
135
|
+
}
|
|
136
|
+
/** @returns {HTMLElement[]} */
|
|
137
|
+
_fields() {
|
|
138
|
+
if (!this._form) return [];
|
|
139
|
+
const seenRadioNames = /* @__PURE__ */ new Set();
|
|
140
|
+
return [...this._form.querySelectorAll(FIELD_SELECTOR)].filter((field) => {
|
|
141
|
+
if (IGNORED_TYPES.has(field.type)) return false;
|
|
142
|
+
if (field.disabled || field.hasAttribute("data-error-ignore")) return false;
|
|
143
|
+
if (typeof field.checkValidity !== "function") return false;
|
|
144
|
+
if (field.type === "radio" && field.name) {
|
|
145
|
+
if (seenRadioNames.has(field.name)) return false;
|
|
146
|
+
seenRadioNames.add(field.name);
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
_onSubmit(event) {
|
|
152
|
+
if (this.validate()) return;
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
this._announceErrors();
|
|
155
|
+
this._focusPanel();
|
|
156
|
+
this.dispatchEvent(new CustomEvent("velin-form-invalid", {
|
|
157
|
+
bubbles: true,
|
|
158
|
+
detail: { errors: this.errors.map(({ label, message }) => ({ label, message })) }
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
_onReset() {
|
|
162
|
+
requestAnimationFrame(() => this.clear());
|
|
163
|
+
}
|
|
164
|
+
/** Re-validate a single field once it already had an error, never before. */
|
|
165
|
+
_onFieldChange(event) {
|
|
166
|
+
const field = event.target;
|
|
167
|
+
if (!field || !this._errors.some((error) => error.field === field)) return;
|
|
168
|
+
if (!field.checkValidity()) return;
|
|
169
|
+
this._clearFieldError(field);
|
|
170
|
+
this._errors = this._errors.filter((error) => error.field !== field);
|
|
171
|
+
this._render();
|
|
172
|
+
if (this._errors.length === 0) {
|
|
173
|
+
this.dispatchEvent(new CustomEvent("velin-form-valid", { bubbles: true }));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ── Field state ────────────────────────────────────────────────────────────
|
|
177
|
+
/** @param {HTMLElement} field */
|
|
178
|
+
_errorId(field) {
|
|
179
|
+
if (!field.id) field.id = `velin-field-${++fieldIdCounter}`;
|
|
180
|
+
return `${field.id}-error`;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* @param {HTMLElement} field
|
|
184
|
+
* @param {string} message
|
|
185
|
+
*/
|
|
186
|
+
_markFieldError(field, message) {
|
|
187
|
+
const errorId = this._errorId(field);
|
|
188
|
+
field.setAttribute("aria-invalid", "true");
|
|
189
|
+
let holder = this.ownerDocument.getElementById(errorId);
|
|
190
|
+
if (!holder) {
|
|
191
|
+
holder = this._form.querySelector(`[data-velin-error-for="${escapeSelector(field.name || field.id)}"]`);
|
|
192
|
+
}
|
|
193
|
+
if (!holder) {
|
|
194
|
+
holder = this.ownerDocument.createElement("p");
|
|
195
|
+
holder.dataset.velinErrorGenerated = "true";
|
|
196
|
+
field.insertAdjacentElement("afterend", holder);
|
|
197
|
+
}
|
|
198
|
+
holder.id = errorId;
|
|
199
|
+
holder.classList.add("velin-field-error");
|
|
200
|
+
holder.textContent = message;
|
|
201
|
+
addDescribedBy(field, errorId);
|
|
202
|
+
}
|
|
203
|
+
/** @param {HTMLElement} field */
|
|
204
|
+
_clearFieldError(field) {
|
|
205
|
+
if (!field.id) return;
|
|
206
|
+
const errorId = `${field.id}-error`;
|
|
207
|
+
field.removeAttribute("aria-invalid");
|
|
208
|
+
removeDescribedBy(field, errorId);
|
|
209
|
+
const holder = this.ownerDocument.getElementById(errorId);
|
|
210
|
+
if (!holder) return;
|
|
211
|
+
if (holder.dataset.velinErrorGenerated) holder.remove();
|
|
212
|
+
else holder.textContent = "";
|
|
213
|
+
}
|
|
214
|
+
/** @param {HTMLElement} field */
|
|
215
|
+
_focusField(field) {
|
|
216
|
+
const target = field.type === "radio" && field.name ? this._form.querySelector(`input[type="radio"][name="${escapeSelector(field.name)}"]`) || field : field;
|
|
217
|
+
target.focus();
|
|
218
|
+
this.dispatchEvent(new CustomEvent("velin-form-error-focus", {
|
|
219
|
+
bubbles: true,
|
|
220
|
+
detail: { name: target.name || target.id }
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
// ── Summary panel ──────────────────────────────────────────────────────────
|
|
224
|
+
_render() {
|
|
225
|
+
if (!this._errors.length) {
|
|
226
|
+
this._panel?.remove();
|
|
227
|
+
this._panel = null;
|
|
228
|
+
this.hidden = true;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
this.hidden = false;
|
|
232
|
+
if (!this._panel?.isConnected) {
|
|
233
|
+
const panel = this.ownerDocument.createElement("div");
|
|
234
|
+
panel.className = "velin-form-summary__panel velin-alert velin-alert--danger";
|
|
235
|
+
panel.setAttribute("role", "alert");
|
|
236
|
+
panel.tabIndex = -1;
|
|
237
|
+
const heading = this.ownerDocument.createElement("p");
|
|
238
|
+
heading.className = "velin-form-summary__heading";
|
|
239
|
+
heading.textContent = this.headingText;
|
|
240
|
+
const list2 = this.ownerDocument.createElement("ul");
|
|
241
|
+
list2.className = "velin-form-summary__list";
|
|
242
|
+
panel.append(heading, list2);
|
|
243
|
+
this.appendChild(panel);
|
|
244
|
+
this._panel = panel;
|
|
245
|
+
}
|
|
246
|
+
const list = this._panel.querySelector(".velin-form-summary__list");
|
|
247
|
+
list.textContent = "";
|
|
248
|
+
for (const error of this._errors) {
|
|
249
|
+
const item = this.ownerDocument.createElement("li");
|
|
250
|
+
const link = this.ownerDocument.createElement("a");
|
|
251
|
+
link.href = `#${this._errorId(error.field).replace(/-error$/, "")}`;
|
|
252
|
+
link.textContent = `${error.label}: ${error.message}`;
|
|
253
|
+
link.addEventListener("click", (event) => {
|
|
254
|
+
event.preventDefault();
|
|
255
|
+
this._focusField(error.field);
|
|
256
|
+
});
|
|
257
|
+
item.appendChild(link);
|
|
258
|
+
list.appendChild(item);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
_focusPanel() {
|
|
262
|
+
this._panel?.focus();
|
|
263
|
+
}
|
|
264
|
+
_announceErrors() {
|
|
265
|
+
const count = this._errors.length;
|
|
266
|
+
announce(count === 1 ? "1 field needs attention" : `${count} fields need attention`, "assertive");
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
customElements.define("velin-form-summary", VelinFormSummary);
|
|
270
|
+
var velin_form_summary_default = VelinFormSummary;
|
|
271
|
+
export {
|
|
272
|
+
velin_form_summary_default as default
|
|
273
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sanitizeSVG,
|
|
3
3
|
sanitizeURL
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-L7ZGUU4D.js";
|
|
5
5
|
|
|
6
6
|
// components/velin-icon.js
|
|
7
7
|
var PROVIDER_CDNS = {
|
|
@@ -29,7 +29,21 @@ function resolveProviderUrl(provider, variant) {
|
|
|
29
29
|
return PROVIDER_CDNS[provider];
|
|
30
30
|
}
|
|
31
31
|
var _svgCache = /* @__PURE__ */ new Map();
|
|
32
|
+
var DEFAULT_SPRITE = "velin-icons.svg";
|
|
33
|
+
function resolveDefaultSpriteUrl() {
|
|
34
|
+
if (typeof document !== "undefined") {
|
|
35
|
+
const meta = document.querySelector('meta[name="velin-icon-sprite"]');
|
|
36
|
+
const fromMeta = meta?.getAttribute("content")?.trim();
|
|
37
|
+
if (fromMeta) return fromMeta;
|
|
38
|
+
const fromHtml = document.documentElement?.getAttribute("data-velin-icon-sprite")?.trim();
|
|
39
|
+
if (fromHtml) return fromHtml;
|
|
40
|
+
}
|
|
41
|
+
const configured = typeof VelinIcon.defaultSprite === "string" ? VelinIcon.defaultSprite.trim() : "";
|
|
42
|
+
return configured || DEFAULT_SPRITE;
|
|
43
|
+
}
|
|
32
44
|
var VelinIcon = class extends HTMLElement {
|
|
45
|
+
/** @type {string} Relative or absolute sprite URL used when `sprite` attribute is omitted. */
|
|
46
|
+
static defaultSprite = DEFAULT_SPRITE;
|
|
33
47
|
static get observedAttributes() {
|
|
34
48
|
return ["name", "size", "label", "provider", "variant", "sprite"];
|
|
35
49
|
}
|
|
@@ -46,7 +60,7 @@ var VelinIcon = class extends HTMLElement {
|
|
|
46
60
|
_render() {
|
|
47
61
|
const name = this.getAttribute("name");
|
|
48
62
|
const size = this.getAttribute("size") || "24";
|
|
49
|
-
const
|
|
63
|
+
const a11y = this._resolveA11y();
|
|
50
64
|
const provider = this.getAttribute("provider");
|
|
51
65
|
const variant = this.getAttribute("variant");
|
|
52
66
|
if (!name) {
|
|
@@ -54,12 +68,18 @@ var VelinIcon = class extends HTMLElement {
|
|
|
54
68
|
return;
|
|
55
69
|
}
|
|
56
70
|
if (provider && (PROVIDER_CDNS[provider] || PROVIDER_VARIANTS[provider])) {
|
|
57
|
-
this._renderFromCDN(name, size,
|
|
71
|
+
this._renderFromCDN(name, size, a11y, provider, variant);
|
|
58
72
|
return;
|
|
59
73
|
}
|
|
60
|
-
this._renderFromSprite(name, size,
|
|
74
|
+
this._renderFromSprite(name, size, a11y);
|
|
75
|
+
}
|
|
76
|
+
_resolveA11y() {
|
|
77
|
+
const label = (this.getAttribute("label") || this.getAttribute("aria-label") || "").trim();
|
|
78
|
+
if (label) return { label, hidden: false };
|
|
79
|
+
const hidden = this.getAttribute("aria-hidden") !== "false";
|
|
80
|
+
return { label: "", hidden };
|
|
61
81
|
}
|
|
62
|
-
_renderFromSprite(name, size,
|
|
82
|
+
_renderFromSprite(name, size, a11y) {
|
|
63
83
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
64
84
|
const svg = document.createElementNS(svgNS, "svg");
|
|
65
85
|
svg.setAttribute("width", size);
|
|
@@ -71,7 +91,6 @@ var VelinIcon = class extends HTMLElement {
|
|
|
71
91
|
svg.setAttribute("stroke-linecap", "round");
|
|
72
92
|
svg.setAttribute("stroke-linejoin", "round");
|
|
73
93
|
this._applyStyle(svg);
|
|
74
|
-
this._applyA11y(svg, label);
|
|
75
94
|
const use = document.createElementNS(svgNS, "use");
|
|
76
95
|
const spriteAttr = this.getAttribute("sprite");
|
|
77
96
|
const localSymbol = document.getElementById(name);
|
|
@@ -80,29 +99,31 @@ var VelinIcon = class extends HTMLElement {
|
|
|
80
99
|
if (spriteAttr === "" || spriteAttr == null && isLocalSymbol) {
|
|
81
100
|
href = `#${name}`;
|
|
82
101
|
} else {
|
|
83
|
-
const
|
|
102
|
+
const fallback = resolveDefaultSpriteUrl();
|
|
103
|
+
const spriteUrl = sanitizeURL(spriteAttr || fallback) || fallback || DEFAULT_SPRITE;
|
|
84
104
|
href = `${spriteUrl}#${name}`;
|
|
85
105
|
}
|
|
86
106
|
use.setAttribute("href", href);
|
|
87
107
|
svg.appendChild(use);
|
|
108
|
+
this._applyA11y(svg, a11y);
|
|
88
109
|
this.innerHTML = "";
|
|
89
110
|
this.appendChild(svg);
|
|
90
111
|
this._rendered = true;
|
|
91
112
|
}
|
|
92
|
-
async _renderFromCDN(name, size,
|
|
113
|
+
async _renderFromCDN(name, size, a11y, provider, variant) {
|
|
93
114
|
const cacheKey = `${provider}:${variant || "default"}:${name}`;
|
|
94
115
|
if (_svgCache.has(cacheKey)) {
|
|
95
|
-
this._injectSVG(_svgCache.get(cacheKey), size,
|
|
116
|
+
this._injectSVG(_svgCache.get(cacheKey), size, a11y);
|
|
96
117
|
return;
|
|
97
118
|
}
|
|
98
119
|
const template = resolveProviderUrl(provider, variant);
|
|
99
120
|
if (!template) {
|
|
100
|
-
this._renderFromSprite(name, size,
|
|
121
|
+
this._renderFromSprite(name, size, a11y);
|
|
101
122
|
return;
|
|
102
123
|
}
|
|
103
124
|
const url = sanitizeURL(template.replace("{name}", name));
|
|
104
125
|
if (!url) {
|
|
105
|
-
this._renderFromSprite(name, size,
|
|
126
|
+
this._renderFromSprite(name, size, a11y);
|
|
106
127
|
return;
|
|
107
128
|
}
|
|
108
129
|
try {
|
|
@@ -112,12 +133,20 @@ var VelinIcon = class extends HTMLElement {
|
|
|
112
133
|
if (!text.includes("<svg")) throw new Error("Not SVG");
|
|
113
134
|
const clean = await sanitizeSVG(text);
|
|
114
135
|
_svgCache.set(cacheKey, clean);
|
|
115
|
-
this._injectSVG(clean, size,
|
|
136
|
+
this._injectSVG(clean, size, a11y);
|
|
116
137
|
} catch {
|
|
117
|
-
this._renderFromSprite(name, size,
|
|
138
|
+
this._renderFromSprite(name, size, a11y);
|
|
118
139
|
}
|
|
119
140
|
}
|
|
120
|
-
|
|
141
|
+
_stripForeignA11y(svg) {
|
|
142
|
+
svg.removeAttribute("role");
|
|
143
|
+
svg.removeAttribute("aria-label");
|
|
144
|
+
svg.removeAttribute("aria-labelledby");
|
|
145
|
+
svg.removeAttribute("aria-hidden");
|
|
146
|
+
svg.querySelector("title")?.remove();
|
|
147
|
+
svg.querySelector("desc")?.remove();
|
|
148
|
+
}
|
|
149
|
+
_injectSVG(svgText, size, a11y) {
|
|
121
150
|
const parser = new DOMParser();
|
|
122
151
|
const doc = parser.parseFromString(svgText, "image/svg+xml");
|
|
123
152
|
const svg = doc.querySelector("svg");
|
|
@@ -125,11 +154,12 @@ var VelinIcon = class extends HTMLElement {
|
|
|
125
154
|
this.innerHTML = "";
|
|
126
155
|
return;
|
|
127
156
|
}
|
|
157
|
+
this._stripForeignA11y(svg);
|
|
128
158
|
svg.setAttribute("width", size);
|
|
129
159
|
svg.setAttribute("height", size);
|
|
130
160
|
if (!svg.getAttribute("viewBox")) svg.setAttribute("viewBox", "0 0 24 24");
|
|
131
161
|
this._applyStyle(svg);
|
|
132
|
-
this._applyA11y(svg,
|
|
162
|
+
this._applyA11y(svg, a11y);
|
|
133
163
|
this.innerHTML = "";
|
|
134
164
|
this.appendChild(document.importNode(svg, true));
|
|
135
165
|
this._rendered = true;
|
|
@@ -139,12 +169,24 @@ var VelinIcon = class extends HTMLElement {
|
|
|
139
169
|
svg.style.verticalAlign = "middle";
|
|
140
170
|
svg.style.flexShrink = "0";
|
|
141
171
|
}
|
|
142
|
-
_applyA11y(svg,
|
|
172
|
+
_applyA11y(svg, a11y) {
|
|
173
|
+
const { label, hidden } = a11y;
|
|
174
|
+
const use = svg.querySelector("use");
|
|
143
175
|
if (label) {
|
|
144
176
|
svg.setAttribute("role", "img");
|
|
145
177
|
svg.setAttribute("aria-label", label);
|
|
146
|
-
|
|
178
|
+
svg.removeAttribute("aria-hidden");
|
|
179
|
+
svg.removeAttribute("focusable");
|
|
180
|
+
if (use) use.removeAttribute("aria-hidden");
|
|
181
|
+
} else if (hidden) {
|
|
182
|
+
svg.setAttribute("role", "presentation");
|
|
147
183
|
svg.setAttribute("aria-hidden", "true");
|
|
184
|
+
svg.setAttribute("focusable", "false");
|
|
185
|
+
svg.removeAttribute("aria-label");
|
|
186
|
+
if (use) {
|
|
187
|
+
use.setAttribute("aria-hidden", "true");
|
|
188
|
+
use.setAttribute("focusable", "false");
|
|
189
|
+
}
|
|
148
190
|
}
|
|
149
191
|
}
|
|
150
192
|
static get providers() {
|
|
@@ -29,9 +29,23 @@ function resolveProviderUrl(provider, variant) {
|
|
|
29
29
|
return PROVIDER_CDNS[provider];
|
|
30
30
|
}
|
|
31
31
|
var _svgCache = /* @__PURE__ */ new Map();
|
|
32
|
+
var DEFAULT_SPRITE = "velin-icons.svg";
|
|
33
|
+
function resolveDefaultSpriteUrl() {
|
|
34
|
+
if (typeof document !== "undefined") {
|
|
35
|
+
const meta = document.querySelector('meta[name="velin-icon-sprite"]');
|
|
36
|
+
const fromMeta = meta?.getAttribute("content")?.trim();
|
|
37
|
+
if (fromMeta) return fromMeta;
|
|
38
|
+
const fromHtml = document.documentElement?.getAttribute("data-velin-icon-sprite")?.trim();
|
|
39
|
+
if (fromHtml) return fromHtml;
|
|
40
|
+
}
|
|
41
|
+
const configured = typeof VelinIcon.defaultSprite === "string" ? VelinIcon.defaultSprite.trim() : "";
|
|
42
|
+
return configured || DEFAULT_SPRITE;
|
|
43
|
+
}
|
|
32
44
|
var VelinIcon = class extends HTMLElement {
|
|
45
|
+
/** @type {string} Relative or absolute sprite URL used when `sprite` attribute is omitted. */
|
|
46
|
+
static defaultSprite = DEFAULT_SPRITE;
|
|
33
47
|
static get observedAttributes() {
|
|
34
|
-
return ["name", "size", "label", "provider", "variant", "sprite"
|
|
48
|
+
return ["name", "size", "label", "provider", "variant", "sprite"];
|
|
35
49
|
}
|
|
36
50
|
constructor() {
|
|
37
51
|
super();
|
|
@@ -77,7 +91,6 @@ var VelinIcon = class extends HTMLElement {
|
|
|
77
91
|
svg.setAttribute("stroke-linecap", "round");
|
|
78
92
|
svg.setAttribute("stroke-linejoin", "round");
|
|
79
93
|
this._applyStyle(svg);
|
|
80
|
-
this._applyA11y(svg, a11y);
|
|
81
94
|
const use = document.createElementNS(svgNS, "use");
|
|
82
95
|
const spriteAttr = this.getAttribute("sprite");
|
|
83
96
|
const localSymbol = document.getElementById(name);
|
|
@@ -86,11 +99,13 @@ var VelinIcon = class extends HTMLElement {
|
|
|
86
99
|
if (spriteAttr === "" || spriteAttr == null && isLocalSymbol) {
|
|
87
100
|
href = `#${name}`;
|
|
88
101
|
} else {
|
|
89
|
-
const
|
|
102
|
+
const fallback = resolveDefaultSpriteUrl();
|
|
103
|
+
const spriteUrl = sanitizeURL(spriteAttr || fallback) || fallback || DEFAULT_SPRITE;
|
|
90
104
|
href = `${spriteUrl}#${name}`;
|
|
91
105
|
}
|
|
92
106
|
use.setAttribute("href", href);
|
|
93
107
|
svg.appendChild(use);
|
|
108
|
+
this._applyA11y(svg, a11y);
|
|
94
109
|
this.innerHTML = "";
|
|
95
110
|
this.appendChild(svg);
|
|
96
111
|
this._rendered = true;
|
|
@@ -162,14 +177,12 @@ var VelinIcon = class extends HTMLElement {
|
|
|
162
177
|
svg.setAttribute("aria-label", label);
|
|
163
178
|
svg.removeAttribute("aria-hidden");
|
|
164
179
|
svg.removeAttribute("focusable");
|
|
165
|
-
this.removeAttribute("aria-hidden");
|
|
166
180
|
if (use) use.removeAttribute("aria-hidden");
|
|
167
181
|
} else if (hidden) {
|
|
168
182
|
svg.setAttribute("role", "presentation");
|
|
169
183
|
svg.setAttribute("aria-hidden", "true");
|
|
170
184
|
svg.setAttribute("focusable", "false");
|
|
171
185
|
svg.removeAttribute("aria-label");
|
|
172
|
-
this.setAttribute("aria-hidden", "true");
|
|
173
186
|
if (use) {
|
|
174
187
|
use.setAttribute("aria-hidden", "true");
|
|
175
188
|
use.setAttribute("focusable", "false");
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
announce
|
|
3
|
-
} from "./chunk-Z4JYQ2XX.js";
|
|
4
1
|
import {
|
|
5
2
|
clearBackgroundInert,
|
|
6
3
|
setBackgroundInert,
|
|
7
4
|
trapFocus
|
|
8
5
|
} from "./chunk-42VVPW6N.js";
|
|
6
|
+
import {
|
|
7
|
+
announce
|
|
8
|
+
} from "./chunk-Z4JYQ2XX.js";
|
|
9
9
|
import {
|
|
10
10
|
escapeHTML,
|
|
11
11
|
sanitizeURL
|
|
@@ -101,7 +101,6 @@ var VelinLightbox = class extends HTMLElement {
|
|
|
101
101
|
this.setAttribute("open", "");
|
|
102
102
|
setBackgroundInert(this);
|
|
103
103
|
this._render();
|
|
104
|
-
document.body.style.overflow = "hidden";
|
|
105
104
|
const overlay = this.shadowRoot.querySelector(".overlay");
|
|
106
105
|
overlay.removeEventListener("keydown", this._onTrapKey);
|
|
107
106
|
overlay.addEventListener("keydown", this._onTrapKey);
|
|
@@ -109,14 +108,16 @@ var VelinLightbox = class extends HTMLElement {
|
|
|
109
108
|
}
|
|
110
109
|
close() {
|
|
111
110
|
this.removeAttribute("open");
|
|
112
|
-
clearBackgroundInert();
|
|
113
|
-
document.body.style.overflow = "";
|
|
111
|
+
clearBackgroundInert(this);
|
|
114
112
|
if (this._previousFocus) {
|
|
115
113
|
this._previousFocus.focus();
|
|
116
114
|
this._previousFocus = null;
|
|
117
115
|
}
|
|
118
116
|
this.dispatchEvent(new CustomEvent("velin-close", { bubbles: true }));
|
|
119
117
|
}
|
|
118
|
+
disconnectedCallback() {
|
|
119
|
+
clearBackgroundInert(this);
|
|
120
|
+
}
|
|
120
121
|
_prev() {
|
|
121
122
|
this._index = (this._index - 1 + this._items.length) % this._items.length;
|
|
122
123
|
this._render();
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
+
clearBackgroundInert,
|
|
3
|
+
setBackgroundInert,
|
|
2
4
|
trapFocus
|
|
3
5
|
} from "./chunk-42VVPW6N.js";
|
|
6
|
+
import {
|
|
7
|
+
announce
|
|
8
|
+
} from "./chunk-Z4JYQ2XX.js";
|
|
4
9
|
import {
|
|
5
10
|
escapeHTML,
|
|
6
11
|
sanitizeURL
|
|
7
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-L7ZGUU4D.js";
|
|
8
13
|
|
|
9
14
|
// components/velin-lightbox.js
|
|
10
15
|
var styles = `
|
|
@@ -67,7 +72,7 @@ var VelinLightbox = class extends HTMLElement {
|
|
|
67
72
|
<div class="content" part="content"></div>
|
|
68
73
|
<button class="nav nav--next" aria-label="Next">›</button>
|
|
69
74
|
<button class="close" aria-label="Close">×</button>
|
|
70
|
-
<div class="counter" part="counter"></div>
|
|
75
|
+
<div class="counter" part="counter" aria-live="polite" aria-atomic="true"></div>
|
|
71
76
|
</div>
|
|
72
77
|
`;
|
|
73
78
|
const slot = this.shadowRoot.querySelector("slot");
|
|
@@ -94,8 +99,8 @@ var VelinLightbox = class extends HTMLElement {
|
|
|
94
99
|
this._previousFocus = document.activeElement;
|
|
95
100
|
this._index = index;
|
|
96
101
|
this.setAttribute("open", "");
|
|
102
|
+
setBackgroundInert(this);
|
|
97
103
|
this._render();
|
|
98
|
-
document.body.style.overflow = "hidden";
|
|
99
104
|
const overlay = this.shadowRoot.querySelector(".overlay");
|
|
100
105
|
overlay.removeEventListener("keydown", this._onTrapKey);
|
|
101
106
|
overlay.addEventListener("keydown", this._onTrapKey);
|
|
@@ -103,13 +108,16 @@ var VelinLightbox = class extends HTMLElement {
|
|
|
103
108
|
}
|
|
104
109
|
close() {
|
|
105
110
|
this.removeAttribute("open");
|
|
106
|
-
|
|
111
|
+
clearBackgroundInert(this);
|
|
107
112
|
if (this._previousFocus) {
|
|
108
113
|
this._previousFocus.focus();
|
|
109
114
|
this._previousFocus = null;
|
|
110
115
|
}
|
|
111
116
|
this.dispatchEvent(new CustomEvent("velin-close", { bubbles: true }));
|
|
112
117
|
}
|
|
118
|
+
disconnectedCallback() {
|
|
119
|
+
clearBackgroundInert(this);
|
|
120
|
+
}
|
|
113
121
|
_prev() {
|
|
114
122
|
this._index = (this._index - 1 + this._items.length) % this._items.length;
|
|
115
123
|
this._render();
|
|
@@ -130,7 +138,9 @@ var VelinLightbox = class extends HTMLElement {
|
|
|
130
138
|
container.innerHTML = `<img src="${escapeHTML(src)}" alt="${alt}">`;
|
|
131
139
|
}
|
|
132
140
|
const counter = this.shadowRoot.querySelector(".counter");
|
|
133
|
-
|
|
141
|
+
const label = `${this._index + 1} / ${this._items.length}`;
|
|
142
|
+
counter.textContent = label;
|
|
143
|
+
announce(`Slide ${label}`, "polite");
|
|
134
144
|
}
|
|
135
145
|
};
|
|
136
146
|
customElements.define("velin-lightbox", VelinLightbox);
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-42VVPW6N.js";
|
|
9
9
|
import {
|
|
10
10
|
escapeHTML
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-L7ZGUU4D.js";
|
|
12
12
|
|
|
13
13
|
// components/velin-modal.js
|
|
14
14
|
var styles = `
|
|
@@ -105,14 +105,15 @@ var VelinModal = class extends HTMLElement {
|
|
|
105
105
|
this._onKeydown = this._onKeydown.bind(this);
|
|
106
106
|
}
|
|
107
107
|
connectedCallback() {
|
|
108
|
-
const title = this.getAttribute("title") || "";
|
|
108
|
+
const title = (this.getAttribute("title") || "").trim();
|
|
109
109
|
const safeTitle = escapeHTML(title);
|
|
110
|
+
const dialogLabel = title ? 'aria-labelledby="velin-modal-title"' : `aria-label="${escapeHTML(this.getAttribute("aria-label") || "Dialog")}"`;
|
|
110
111
|
this.shadowRoot.innerHTML = `
|
|
111
112
|
<style>${styles}</style>
|
|
112
113
|
<div class="overlay" part="overlay">
|
|
113
|
-
<div class="dialog" role="dialog" aria-modal="true"
|
|
114
|
+
<div class="dialog" role="dialog" aria-modal="true" ${dialogLabel} part="dialog">
|
|
114
115
|
<div class="header" part="header">
|
|
115
|
-
<h2 class="title" id="velin-modal-title">${safeTitle}</h2>
|
|
116
|
+
<h2 class="title" id="velin-modal-title"${title ? "" : ' class="velin-sr-only"'}>${safeTitle || "Dialog"}</h2>
|
|
116
117
|
<button class="close-btn" aria-label="Close" part="close">×</button>
|
|
117
118
|
</div>
|
|
118
119
|
<div class="body" part="body"><slot></slot></div>
|
|
@@ -145,7 +146,6 @@ var VelinModal = class extends HTMLElement {
|
|
|
145
146
|
this._previouslyFocused = saveFocus();
|
|
146
147
|
setBackgroundInert(this);
|
|
147
148
|
document.addEventListener("keydown", this._onKeydown);
|
|
148
|
-
document.body.style.overflow = "hidden";
|
|
149
149
|
requestAnimationFrame(() => {
|
|
150
150
|
const focusable = getFocusableElements(this.shadowRoot);
|
|
151
151
|
if (focusable.length > 0) focusable[0].focus();
|
|
@@ -153,8 +153,7 @@ var VelinModal = class extends HTMLElement {
|
|
|
153
153
|
}
|
|
154
154
|
_close() {
|
|
155
155
|
document.removeEventListener("keydown", this._onKeydown);
|
|
156
|
-
|
|
157
|
-
clearBackgroundInert();
|
|
156
|
+
clearBackgroundInert(this);
|
|
158
157
|
restoreFocus(this._previouslyFocused);
|
|
159
158
|
}
|
|
160
159
|
_onKeydown(event) {
|
|
@@ -166,7 +165,7 @@ var VelinModal = class extends HTMLElement {
|
|
|
166
165
|
}
|
|
167
166
|
disconnectedCallback() {
|
|
168
167
|
document.removeEventListener("keydown", this._onKeydown);
|
|
169
|
-
|
|
168
|
+
clearBackgroundInert(this);
|
|
170
169
|
}
|
|
171
170
|
};
|
|
172
171
|
customElements.define("velin-modal", VelinModal);
|