@bluevolt-tech/lumen 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +37 -23
  2. package/dist/components/_shared/adopt-styles.d.ts +8 -0
  3. package/dist/components/_shared/date-range-validator.d.ts +61 -0
  4. package/dist/components/_shared/dropdown-controller.d.ts +31 -0
  5. package/dist/components/_shared/selectable-card.d.ts +10 -0
  6. package/dist/components/bv-alert-banner/bv-alert-banner.js +19 -17
  7. package/dist/components/bv-avatar/bv-avatar.js +19 -11
  8. package/dist/components/bv-badge/bv-badge.js +19 -11
  9. package/dist/components/bv-button/bv-button.js +18 -16
  10. package/dist/components/bv-category-pill/bv-category-pill.js +18 -16
  11. package/dist/components/bv-checkbox/bv-checkbox.js +19 -11
  12. package/dist/components/bv-chip/bv-chip.js +19 -11
  13. package/dist/components/bv-course-detail/bv-course-detail.js +20 -34
  14. package/dist/components/bv-course-detail-modal/bv-course-detail-modal.js +24 -90
  15. package/dist/components/bv-course-grid-card/bv-course-grid-card.d.ts +2 -2
  16. package/dist/components/bv-course-grid-card/bv-course-grid-card.js +105 -55
  17. package/dist/components/bv-course-list-card/bv-course-list-card.d.ts +2 -2
  18. package/dist/components/bv-course-list-card/bv-course-list-card.js +105 -55
  19. package/dist/components/bv-course-outline/bv-course-outline.js +19 -11
  20. package/dist/components/bv-date-range-input/bv-date-range-input.d.ts +4 -6
  21. package/dist/components/bv-date-range-input/bv-date-range-input.js +460 -122
  22. package/dist/components/bv-date-range-picker/bv-date-range-picker.d.ts +17 -3
  23. package/dist/components/bv-date-range-picker/bv-date-range-picker.js +339 -73
  24. package/dist/components/bv-empty-state/bv-empty-state.js +20 -28
  25. package/dist/components/bv-filter-dropdown/bv-filter-dropdown.d.ts +1 -4
  26. package/dist/components/bv-filter-dropdown/bv-filter-dropdown.js +78 -53
  27. package/dist/components/bv-filter-panel/bv-filter-panel.js +20 -22
  28. package/dist/components/bv-icon/bv-icon.js +19 -11
  29. package/dist/components/bv-input/bv-input.js +18 -16
  30. package/dist/components/bv-input-dropdown/bv-input-dropdown.d.ts +1 -4
  31. package/dist/components/bv-input-dropdown/bv-input-dropdown.js +77 -78
  32. package/dist/components/bv-modal/bv-modal.js +19 -11
  33. package/dist/components/bv-page-title/bv-page-title.js +19 -6
  34. package/dist/components/bv-pagination/bv-pagination.d.ts +1 -0
  35. package/dist/components/bv-pagination/bv-pagination.js +23 -186
  36. package/dist/components/bv-portal-header/bv-portal-header.js +25 -83
  37. package/dist/components/bv-portal-layout/bv-portal-layout.js +19 -11
  38. package/dist/components/bv-progress-bar/bv-progress-bar.js +19 -11
  39. package/dist/components/bv-promo-banner/bv-promo-banner.js +19 -11
  40. package/dist/components/bv-radio/bv-radio.js +19 -11
  41. package/dist/components/bv-searchbox/bv-searchbox.js +19 -11
  42. package/dist/components/bv-segmented-control/bv-segmented-control.js +18 -16
  43. package/dist/components/bv-select/bv-select.d.ts +1 -4
  44. package/dist/components/bv-select/bv-select.js +76 -44
  45. package/dist/components/bv-split-button/bv-split-button.d.ts +1 -4
  46. package/dist/components/bv-split-button/bv-split-button.js +76 -42
  47. package/dist/components/bv-star-rating/bv-star-rating.js +19 -11
  48. package/dist/components/bv-table/bv-table.d.ts +5 -0
  49. package/dist/components/bv-table/bv-table.js +69 -19
  50. package/dist/components/bv-toggle/bv-toggle.js +19 -11
  51. package/dist/components/bv-tooltip/bv-tooltip.js +19 -17
  52. package/dist/components/bv-user-row/bv-user-row.js +22 -50
  53. package/dist/components/bv-view-switcher/bv-view-switcher.js +19 -11
  54. package/dist/themes/base.css +2 -1
  55. package/package.json +8 -1
  56. package/themes/base.css +2 -1
package/README.md CHANGED
@@ -22,26 +22,28 @@ npm run storybook
22
22
 
23
23
  ## Installation
24
24
 
25
- Install from GitHub Packages:
25
+ Install from the public npm registry:
26
26
 
27
27
  ```bash
28
28
  npm install @bluevolt-tech/lumen
29
29
  ```
30
30
 
31
- ### Prerequisites
31
+ No authentication or `.npmrc` configuration is required — the package is public.
32
32
 
33
- **1. Configure npm to authenticate with GitHub Packages**
33
+ > Legacy apps that still have a `@bluevolt-tech:registry=https://npm.pkg.github.com` entry in their `.npmrc` from the GitHub Packages era can safely remove it. See [docs/migration-guide-angular-apps.md](docs/migration-guide-angular-apps.md) for the step-by-step.
34
34
 
35
- Create or edit `~/.npmrc`:
35
+ ### CDN (no build step)
36
36
 
37
- ```
38
- @bluevolt-tech:registry=https://npm.pkg.github.com
39
- //npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN
37
+ Apps without a build pipeline (ASPX / MVC / plain HTML) can load Lumen directly via unpkg or jsdelivr:
38
+
39
+ ```html
40
+ <link rel="stylesheet" href="https://unpkg.com/@bluevolt-tech/lumen@latest/themes/base.css" />
41
+ <script type="module" src="https://unpkg.com/@bluevolt-tech/lumen@latest/dist/components/bv-button/bv-button.js"></script>
40
42
  ```
41
43
 
42
- Replace `YOUR_GITHUB_TOKEN` with a [GitHub personal access token](https://github.com/settings/tokens) that has `read:packages` scope.
44
+ Pin to a specific version (`@2.0.1` instead of `@latest`) in production. See [docs/aspx-integration-guide.md](docs/aspx-integration-guide.md) for a full ASPX integration walk-through.
43
45
 
44
- **2. Load the Roboto font**
46
+ ### Prerequisite: load the Roboto font
45
47
 
46
48
  Lumen uses Roboto (weights 400, 500, 600, 700). Add to your app's `<head>`:
47
49
 
@@ -206,9 +208,11 @@ document.addEventListener('bv-page-change', (e) => {
206
208
 
207
209
  ## Deployment
208
210
 
209
- ### Library — GitHub Packages
211
+ ### Library — public npm registry
210
212
 
211
- The npm package (`@bluevolt-tech/lumen`) is published to GitHub Packages. Publishing is triggered automatically by creating a GitHub release, which runs the `deploy.yml` workflow.
213
+ The npm package (`@bluevolt-tech/lumen`) is published to the public npm registry. Publishing is triggered automatically by creating a GitHub release, which runs the `publish-npm.yml` workflow.
214
+
215
+ The DevOps setup (npm org, automation token, GitHub secret) is documented once at [docs/npm-public-publishing-guide.md](docs/npm-public-publishing-guide.md).
212
216
 
213
217
  **Build pipeline:**
214
218
 
@@ -235,20 +239,25 @@ npm install /path/to/lumen/@bluevolt-tech-lumen-1.0.0.tgz
235
239
 
236
240
  ### Storybook — Azure Blob Storage
237
241
 
238
- The Storybook is hosted on Azure Blob Storage as a static website. Two deployments are maintained:
242
+ The Storybook is hosted on Azure Blob Storage as a static website. Two deployments are maintained per release:
243
+
244
+ | URL path | Contents | Updated when |
245
+ | ------------- | -------------------------------------------- | -------------------------------------------- |
246
+ | `/latest/` | Always the newest release build | Every release (moving pointer) |
247
+ | `/vX.Y.Z/` | Frozen snapshot for that exact semver tag | On release — one snapshot per released tag |
239
248
 
240
- | URL path | Contents | Updated when |
241
- |----------|----------|--------------|
242
- | `/latest/` | Always the newest build | Every release |
243
- | `/v{major}/` | Stable major version | On major/minor releases |
249
+ The full semver path (`/v2.0.1/`, not `/v2/` or `/v2.0/`) matches the npm-installed version 1:1, so a consumer on `@bluevolt-tech/lumen@2.0.1` has a durable docs URL that will not change when v2.0.2 ships.
244
250
 
245
251
  Deploying is triggered automatically by **publishing a GitHub release**. The `deploy.yml` workflow runs:
246
252
 
247
- 1. `npm run build` — generates tokens, compiles components, generates types
248
- 2. `npm run build-storybook -- --output-dir storybook-latest` with `STORYBOOK_BASE_PATH=/latest/` bakes the base path into asset URLs for the blob subdirectory
249
- 3. Copies `dist/` into `storybook-latest/dist/` so component JS files are co-located with the Storybook
250
- 4. Uploads the output to Azure Blob Storage under `$web/latest`
251
- 5. Repeats steps 2–4 with `STORYBOOK_BASE_PATH=/v{major}/` for the versioned deployment
253
+ 1. `npm run build` — generates tokens, compiles components, generates types.
254
+ 2. Regenerates `storybook/.storybook/versions.ts` from the current git tag list via the `prebuild-storybook` npm hook (`scripts/generate-versions.js`).
255
+ 3. `npm run build-storybook -- --output-dir storybook-versioned` with `STORYBOOK_BASE_PATH=/vX.Y.Z/` bakes the base path into asset URLs for the versioned blob subdirectory.
256
+ 4. Copies `dist/` into `storybook-versioned/dist/` so component JS files are co-located with the Storybook.
257
+ 5. Uploads the output to Azure Blob Storage under `$web/vX.Y.Z`.
258
+ 6. Repeats steps 3–5 with `STORYBOOK_BASE_PATH=/latest/` and uploads to `$web/latest`.
259
+
260
+ A version switcher dropdown in the Storybook toolbar auto-discovers all supported releases from git tags — cutting a new release is the only action needed for a new entry to appear.
252
261
 
253
262
  ### Simulating the pipeline locally
254
263
 
@@ -282,5 +291,10 @@ npx http-server temp-serve -p 8080
282
291
 
283
292
  ## Resources
284
293
 
285
- - **Storybook** — component playground, API reference, and framework integration snippets (`npm run storybook`)
286
- - Questions or issues reach out to the Lumen team or open an issue in this repository
294
+ - **Storybook** — component playground, API reference, and framework integration snippets (`npm run storybook`).
295
+ - [docs/aspx-integration-guide.md](docs/aspx-integration-guide.md) end-to-end walkthrough for adding a Lumen screen to a legacy ASPX page via unpkg.
296
+ - [docs/migration-guide-angular-apps.md](docs/migration-guide-angular-apps.md) — one-time steps for Angular apps still on the old GitHub Packages registry.
297
+ - [docs/npm-public-publishing-guide.md](docs/npm-public-publishing-guide.md) — DevOps setup for the public-npm publish pipeline (org, automation token, GitHub secret).
298
+ - [docs/component-usage-scanner.md](docs/component-usage-scanner.md) — GitHub Action that scans the org for `bv-*` usage and writes a report.
299
+ - **Contributing to Lumen?** — start at [CLAUDE.md](CLAUDE.md). It is the write-time rulebook: file structure, TypeScript rules, component/story patterns, the 9 pre-push gates, the review-process skills, and the semver policy.
300
+ - Questions or issues → reach out to the Lumen team or open an issue in this repository.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Attach a CSS string to a shadow root.
3
+ *
4
+ * Uses constructable stylesheets (`adoptedStyleSheets`) when available, falling
5
+ * back to a `<style>` element appended to the shadow root otherwise. Both paths
6
+ * scope the CSS to the shadow root — consumers do not need to know which was used.
7
+ */
8
+ export declare function adoptStyles(shadow: ShadowRoot, css: string): void;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Pure validator for a Start / End date range.
3
+ *
4
+ * The picker and the input both consume this. All rules are **opt-in** —
5
+ * without config, the validator returns "no errors, apply enabled once both
6
+ * dates are set" (the historical behavior of `bv-date-range-picker` before
7
+ * BTN-10191).
8
+ *
9
+ * The validator returns AT MOST ONE error per field, plus at most one
10
+ * form-level error, in the precedence order documented on BTN-10191:
11
+ *
12
+ * 1. Format guard (V8) — per field.
13
+ * 2. Bounds (V1–V4) — per field: `min` first, then `max`.
14
+ * 3. Cross-field (V5) — only when both fields are individually valid.
15
+ * 4. Feature limit (V6) — last, only when both fields are individually
16
+ * valid AND the cross-field check passes.
17
+ *
18
+ * The consumer decides how to render each error — this module has no strings
19
+ * and no DOM.
20
+ */
21
+ export interface DateRangeConfig {
22
+ /** ISO date string (YYYY-MM-DD). Enables V1/V2 (lower bound) when set. */
23
+ min?: string;
24
+ /** ISO date string (YYYY-MM-DD) or the literal `'today'`. Enables V3/V4 (upper bound) when set. */
25
+ max?: string;
26
+ /** Number of years. Enables V6 (span limit) when set. */
27
+ maxRangeYears?: number;
28
+ /** Opt-in to V5 (Start ≤ End cross-field check). */
29
+ validateOrder?: boolean;
30
+ /** Opt-in to V8 (format guard — non-parseable strings become field errors). */
31
+ validateFormat?: boolean;
32
+ }
33
+ export interface DateRangeInput {
34
+ /** Raw ISO string (YYYY-MM-DD) or empty. Non-empty non-parseable strings become format errors when V8 is on. */
35
+ start?: string;
36
+ end?: string;
37
+ }
38
+ /**
39
+ * Field-level errors — render next to the input with `aria-invalid="true"`.
40
+ * Form-level `order` renders as a message below the picker (no `aria-invalid`).
41
+ * Form-level `range-limit` renders as a banner above the picker (no `aria-invalid`)
42
+ * — it's a feature limit, not an input error.
43
+ */
44
+ export type FieldError = 'format' | 'min' | 'max';
45
+ export type FormError = 'order' | 'range-limit';
46
+ export interface DateRangeValidity {
47
+ /** True when NO rule fires (field-level or form-level). Does not consider whether both dates are set. */
48
+ valid: boolean;
49
+ /** True when `valid` AND both `start` and `end` parse to real dates. Consumer disables Apply when this is false. */
50
+ applyEnabled: boolean;
51
+ startError?: FieldError;
52
+ endError?: FieldError;
53
+ formError?: FormError;
54
+ }
55
+ export declare function validateDateRange(input: DateRangeInput, config?: DateRangeConfig): DateRangeValidity;
56
+ /**
57
+ * Strict ISO parser. Accepts `YYYY-MM-DD` where every field is a real calendar
58
+ * date. Rejects `2026-02-30` (JS Date would auto-correct it to 2026-03-02).
59
+ */
60
+ export declare function parseIso(s: string): Date | null;
61
+ export declare function isoToday(): string;
@@ -0,0 +1,31 @@
1
+ export interface DropdownControllerOptions {
2
+ /** Element whose `aria-expanded` should mirror the open state — usually the trigger. */
3
+ trigger?: HTMLElement | null;
4
+ /** Called just before opening. Use for lazy state seeding (e.g. syncing an internal picker). */
5
+ onBeforeOpen?: () => void;
6
+ }
7
+ export interface DropdownController {
8
+ open: () => void;
9
+ close: () => void;
10
+ toggle: () => void;
11
+ cleanup: () => void;
12
+ }
13
+ /**
14
+ * Wire the standard dropdown-dismissal contract on `host`:
15
+ *
16
+ * - Click outside the host (light or shadow tree) closes the dropdown.
17
+ * - Escape key closes the dropdown.
18
+ * - `aria-expanded` on the trigger mirrors the `open` attribute — including
19
+ * changes made externally (`host.setAttribute('open', '')`), which is how
20
+ * Storybook Controls, tests, and framework `[attr.open]` bindings drive
21
+ * the state. The controller is the single source of truth; consuming
22
+ * components must NOT duplicate this sync in their own attributeChangedCallback.
23
+ * - `bv-open` is dispatched from the host when opening via `open()`/`toggle()`.
24
+ *
25
+ * The controller mutates the `open` attribute on the host. Consumers should
26
+ * drive their visual state (`.panel` visibility, focus management) from that
27
+ * attribute — do not fork the source of truth.
28
+ *
29
+ * Call the returned `cleanup()` from `disconnectedCallback`.
30
+ */
31
+ export declare function bindDropdownController(host: HTMLElement, options?: DropdownControllerOptions): DropdownController;
@@ -0,0 +1,10 @@
1
+ export interface SelectableHost extends HTMLElement {
2
+ selected: boolean;
3
+ disableSelection: boolean;
4
+ loading: boolean;
5
+ }
6
+ export declare function buildAriaLabel(host: HTMLElement): string;
7
+ export declare function setPrefixedStrong(el: HTMLElement | null, prefix: string, val: string | null): void;
8
+ export declare function syncSelectionA11y(host: SelectableHost, card: HTMLElement): void;
9
+ export declare function syncCardLoading(host: SelectableHost, card: HTMLElement): void;
10
+ export declare function bindCardSelection(host: SelectableHost, card: HTMLElement, actions: HTMLElement): void;
@@ -1,6 +1,24 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-alert-banner/bv-alert-banner.css
2
2
  var bv_alert_banner_default = ":host {\n --banner-bg: var(--bv-color-neutral-0);\n --banner-border: var(--bv-color-neutral-200);\n --banner-color: var(--bv-color-neutral-700);\n --banner-color-subtle: var(--bv-color-neutral-500);\n --banner-icon-color: var(--bv-color-neutral-500);\n\n display: block;\n font-family: var(--bv-font-family-base);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n:host([variant='primary']) {\n --banner-bg: var(--bv-color-primary-50);\n --banner-border: color-mix(in srgb, var(--bv-color-primary) 40%, var(--bv-color-neutral-0));\n --banner-color: var(--bv-color-primary);\n --banner-color-subtle: var(--bv-color-primary);\n --banner-icon-color: var(--bv-color-primary);\n}\n\n:host([variant='error']) {\n --banner-bg: color-mix(in srgb, var(--bv-color-danger-light) 35%, var(--bv-color-neutral-0));\n --banner-border: color-mix(in srgb, var(--bv-color-danger) 45%, var(--bv-color-neutral-0));\n --banner-color: var(--bv-color-danger-dark);\n --banner-color-subtle: var(--bv-color-danger-dark);\n --banner-icon-color: var(--bv-color-danger);\n}\n\n:host([variant='warning']) {\n --banner-bg: color-mix(in srgb, var(--bv-color-warning-light) 35%, var(--bv-color-neutral-0));\n --banner-border: color-mix(in srgb, var(--bv-color-warning) 50%, var(--bv-color-neutral-0));\n --banner-color: var(--bv-color-warning-dark);\n --banner-color-subtle: var(--bv-color-warning-dark);\n --banner-icon-color: var(--bv-color-warning);\n}\n\n:host([variant='success']) {\n --banner-bg: color-mix(in srgb, var(--bv-color-success-light) 30%, var(--bv-color-neutral-0));\n --banner-border: color-mix(in srgb, var(--bv-color-success) 40%, var(--bv-color-neutral-0));\n --banner-color: var(--bv-color-success-dark);\n --banner-color-subtle: var(--bv-color-success-dark);\n --banner-icon-color: var(--bv-color-success);\n}\n\n/* \u2500\u2500 Banner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner {\n display: flex;\n align-items: flex-start;\n gap: var(--bv-spacing-md);\n padding: var(--bv-spacing-md);\n background: var(--banner-bg);\n border: 1px solid var(--banner-border);\n border-radius: var(--bv-radius-xl);\n}\n\n/* \u2500\u2500 Icon \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner-icon {\n display: flex;\n flex-shrink: 0;\n margin-top: 0;\n color: var(--banner-icon-color);\n line-height: 0;\n}\n\n/* \u2500\u2500 Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-xs);\n min-width: 0;\n}\n\n.banner-heading {\n margin: 0;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n line-height: 20px;\n color: var(--banner-color);\n}\n\n.banner-heading[hidden] {\n display: none;\n}\n\n.banner-message {\n margin: 0;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n line-height: 20px;\n color: var(--banner-color);\n}\n\n.banner-message[hidden] {\n display: none;\n}\n\n/* \u2500\u2500 Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.banner-actions {\n display: flex;\n align-items: center;\n gap: var(--bv-spacing-md);\n margin-top: var(--bv-spacing-xs);\n}\n\n.banner-actions[hidden] {\n display: none;\n}\n\n.action-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n line-height: 20px;\n color: var(--banner-color);\n transition: opacity 0.15s;\n}\n\n.action-btn[hidden] {\n display: none;\n}\n\n.action-btn:hover {\n opacity: 0.75;\n}\n\n.action-btn:focus-visible {\n outline: 2px solid var(--banner-color);\n outline-offset: 2px;\n border-radius: var(--bv-radius-sm);\n}\n\n/* \u2500\u2500 Close button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.close-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n margin: -6px -6px 0 0;\n background: none;\n border: none;\n border-radius: var(--bv-radius-lg);\n cursor: pointer;\n color: var(--banner-icon-color);\n transition: background 0.15s;\n padding: 0;\n}\n\n.close-btn[hidden] {\n display: none;\n}\n\n.close-btn:hover {\n background: color-mix(in srgb, var(--banner-border) 60%, var(--banner-bg));\n}\n\n.close-btn:focus-visible {\n outline: 2px solid var(--banner-color);\n outline-offset: 2px;\n}\n";
3
3
 
4
+ // components/_shared/adopt-styles.ts
5
+ var _sheetCache = /* @__PURE__ */ new Map();
6
+ function adoptStyles(shadow, css) {
7
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
8
+ let sheet = _sheetCache.get(css);
9
+ if (!sheet) {
10
+ sheet = new CSSStyleSheet();
11
+ sheet.replaceSync(css);
12
+ _sheetCache.set(css, sheet);
13
+ }
14
+ shadow.adoptedStyleSheets = [sheet];
15
+ return;
16
+ }
17
+ const style = document.createElement("style");
18
+ style.textContent = css;
19
+ shadow.appendChild(style);
20
+ }
21
+
4
22
  // components/bv-alert-banner/bv-alert-banner.ts
5
23
  var ICON_INFO = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>`;
6
24
  var ICON_ALERT_CIRCLE = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`;
@@ -15,14 +33,6 @@ var VARIANT_ICONS = {
15
33
  warning: ICON_ALERT_TRIANGLE,
16
34
  success: ICON_CHECK_CIRCLE
17
35
  };
18
- var _sheet = null;
19
- function getSheet() {
20
- if (!_sheet) {
21
- _sheet = new CSSStyleSheet();
22
- _sheet.replaceSync(bv_alert_banner_default);
23
- }
24
- return _sheet;
25
- }
26
36
  var BvAlertBanner = class extends HTMLElement {
27
37
  static observedAttributes = [
28
38
  "variant",
@@ -42,10 +52,7 @@ var BvAlertBanner = class extends HTMLElement {
42
52
  _closeBtn = null;
43
53
  constructor() {
44
54
  super();
45
- const shadow = this.attachShadow({ mode: "open" });
46
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
47
- shadow.adoptedStyleSheets = [getSheet()];
48
- }
55
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_alert_banner_default);
49
56
  }
50
57
  get variant() {
51
58
  return this.getAttribute("variant") ?? "default";
@@ -99,11 +106,6 @@ var BvAlertBanner = class extends HTMLElement {
99
106
  if (this._rendered) return;
100
107
  this._rendered = true;
101
108
  const shadow = this.shadowRoot;
102
- if (!shadow.adoptedStyleSheets.length) {
103
- const s = document.createElement("style");
104
- s.textContent = bv_alert_banner_default;
105
- shadow.appendChild(s);
106
- }
107
109
  const banner = document.createElement("div");
108
110
  banner.className = "banner";
109
111
  banner.setAttribute("role", "alert");
@@ -1,6 +1,24 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-avatar/bv-avatar.css
2
2
  var bv_avatar_default = ":host {\n --bv-avatar-size: 40px;\n --bv-avatar-bg: var(--bv-color-primary-light);\n --bv-avatar-color: var(--bv-color-primary);\n --bv-avatar-radius: var(--bv-radius-full);\n display: inline-flex;\n}\n\n:host([size='xs']) {\n --bv-avatar-size: 24px;\n}\n:host([size='sm']) {\n --bv-avatar-size: 32px;\n}\n:host([size='md']) {\n --bv-avatar-size: 40px;\n}\n:host([size='lg']) {\n --bv-avatar-size: 48px;\n}\n:host([size='xl']) {\n --bv-avatar-size: 64px;\n}\n\n.avatar {\n width: var(--bv-avatar-size);\n height: var(--bv-avatar-size);\n border-radius: var(--bv-avatar-radius);\n background: var(--bv-avatar-bg);\n color: var(--bv-avatar-color);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n flex-shrink: 0;\n font-family: var(--bv-font-family-base);\n font-weight: var(--bv-font-weight-semibold);\n font-size: calc(var(--bv-avatar-size) * 0.38);\n user-select: none;\n}\n\n.avatar img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n";
3
3
 
4
+ // components/_shared/adopt-styles.ts
5
+ var _sheetCache = /* @__PURE__ */ new Map();
6
+ function adoptStyles(shadow, css) {
7
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
8
+ let sheet = _sheetCache.get(css);
9
+ if (!sheet) {
10
+ sheet = new CSSStyleSheet();
11
+ sheet.replaceSync(css);
12
+ _sheetCache.set(css, sheet);
13
+ }
14
+ shadow.adoptedStyleSheets = [sheet];
15
+ return;
16
+ }
17
+ const style = document.createElement("style");
18
+ style.textContent = css;
19
+ shadow.appendChild(style);
20
+ }
21
+
4
22
  // components/bv-avatar/bv-avatar.ts
5
23
  var BvAvatar = class extends HTMLElement {
6
24
  static observedAttributes = ["src", "alt", "initials", "size"];
@@ -9,12 +27,7 @@ var BvAvatar = class extends HTMLElement {
9
27
  _label = null;
10
28
  constructor() {
11
29
  super();
12
- this.attachShadow({ mode: "open" });
13
- if ("replaceSync" in CSSStyleSheet.prototype) {
14
- const sheet = new CSSStyleSheet();
15
- sheet.replaceSync(bv_avatar_default);
16
- this.shadowRoot.adoptedStyleSheets = [sheet];
17
- }
30
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_avatar_default);
18
31
  }
19
32
  connectedCallback() {
20
33
  this._render();
@@ -29,11 +42,6 @@ var BvAvatar = class extends HTMLElement {
29
42
  if (this._rendered) return;
30
43
  this._rendered = true;
31
44
  const shadow = this.shadowRoot;
32
- if (!shadow.adoptedStyleSheets.length) {
33
- const s = document.createElement("style");
34
- s.textContent = bv_avatar_default;
35
- shadow.appendChild(s);
36
- }
37
45
  const div = document.createElement("div");
38
46
  div.className = "avatar";
39
47
  const src = this.getAttribute("src");
@@ -1,18 +1,31 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-badge/bv-badge.css
2
2
  var bv_badge_default = ":host {\n --bv-badge-bg: var(--bv-color-neutral-200);\n --bv-badge-color: var(--bv-color-neutral-700);\n display: inline-flex;\n}\n\n:host([variant='primary']) {\n --bv-badge-bg: var(--bv-color-primary-light);\n --bv-badge-color: var(--bv-color-primary);\n}\n:host([variant='success']) {\n --bv-badge-bg: var(--bv-color-success-light);\n --bv-badge-color: var(--bv-color-success);\n}\n:host([variant='warning']) {\n --bv-badge-bg: var(--bv-color-warning-light);\n --bv-badge-color: var(--bv-color-warning);\n}\n:host([variant='danger']) {\n --bv-badge-bg: var(--bv-color-danger-light);\n --bv-badge-color: var(--bv-color-danger);\n}\n:host([variant='info']) {\n --bv-badge-bg: var(--bv-color-primary-50);\n --bv-badge-color: var(--bv-color-primary);\n}\n:host([variant='purple']) {\n --bv-badge-bg: var(--bv-color-purple-light);\n --bv-badge-color: var(--bv-color-purple);\n}\n:host([variant='orange']) {\n --bv-badge-bg: var(--bv-color-orange-light);\n --bv-badge-color: var(--bv-color-orange);\n}\n:host([variant='indigo']) {\n --bv-badge-bg: var(--bv-color-neutral-100);\n --bv-badge-color: var(--bv-color-indigo);\n}\n\n.badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n border-radius: var(--bv-radius-full);\n background: var(--bv-badge-bg);\n color: var(--bv-badge-color);\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-medium);\n line-height: 18px;\n white-space: nowrap;\n}\n\n:host([size='sm']) .badge {\n padding: 1px 6px;\n font-size: 11px;\n line-height: 16px;\n}\n\n:host([size='lg']) .badge {\n padding: 3px 10px;\n font-size: var(--bv-font-size-sm);\n line-height: 20px;\n}\n\n.dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentColor;\n flex-shrink: 0;\n}\n";
3
3
 
4
+ // components/_shared/adopt-styles.ts
5
+ var _sheetCache = /* @__PURE__ */ new Map();
6
+ function adoptStyles(shadow, css) {
7
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
8
+ let sheet = _sheetCache.get(css);
9
+ if (!sheet) {
10
+ sheet = new CSSStyleSheet();
11
+ sheet.replaceSync(css);
12
+ _sheetCache.set(css, sheet);
13
+ }
14
+ shadow.adoptedStyleSheets = [sheet];
15
+ return;
16
+ }
17
+ const style = document.createElement("style");
18
+ style.textContent = css;
19
+ shadow.appendChild(style);
20
+ }
21
+
4
22
  // components/bv-badge/bv-badge.ts
5
23
  var BvBadge = class extends HTMLElement {
6
24
  static observedAttributes = ["variant", "size", "dot"];
7
25
  _rendered = false;
8
26
  constructor() {
9
27
  super();
10
- this.attachShadow({ mode: "open" });
11
- if ("replaceSync" in CSSStyleSheet.prototype) {
12
- const sheet = new CSSStyleSheet();
13
- sheet.replaceSync(bv_badge_default);
14
- this.shadowRoot.adoptedStyleSheets = [sheet];
15
- }
28
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_badge_default);
16
29
  }
17
30
  connectedCallback() {
18
31
  this._render();
@@ -24,11 +37,6 @@ var BvBadge = class extends HTMLElement {
24
37
  if (this._rendered) return;
25
38
  this._rendered = true;
26
39
  const shadow = this.shadowRoot;
27
- if (!shadow.adoptedStyleSheets.length) {
28
- const s = document.createElement("style");
29
- s.textContent = bv_badge_default;
30
- shadow.appendChild(s);
31
- }
32
40
  const badge = document.createElement("span");
33
41
  badge.className = "badge";
34
42
  if (this.hasAttribute("dot")) {
@@ -74,15 +74,25 @@ var rawIcons = {
74
74
  "module-upload": `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 14.75 16.13" fill="none" stroke="currentColor" stroke-width="1.125" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13.39 4.3125V4.1625C13.39 2.90238 13.39 2.27232 13.1278 1.79102C12.8972 1.36766 12.5293 1.02345 12.0767 0.807736C11.5622 0.5625 10.8887 0.5625 9.54172 0.5625H4.41074C3.06373 0.5625 2.39022 0.5625 1.87573 0.807736C1.42318 1.02345 1.05524 1.36766 0.824646 1.79102C0.5625 2.27232 0.5625 2.90238 0.5625 4.1625V11.9625C0.5625 13.2226 0.5625 13.8527 0.824646 14.334C1.05524 14.7573 1.42318 15.1015 1.87573 15.3173C2.39022 15.5625 3.06373 15.5625 4.41074 15.5625H7.37709M7.37709 7.3125H3.76936M6.57537 10.3125H3.76936M10.1831 4.3125H3.76936M11.7865 12.5625V8.4375C11.7865 7.81618 12.3249 7.3125 12.9891 7.3125C13.6533 7.3125 14.1917 7.81618 14.1917 8.4375V12.5625C14.1917 13.8051 13.1149 14.8125 11.7865 14.8125C10.4582 14.8125 9.38138 13.8051 9.38138 12.5625V9.5625"/></svg>`
75
75
  };
76
76
 
77
- // components/bv-button/bv-button.ts
78
- var _sheet = null;
79
- function getSheet() {
80
- if (!_sheet) {
81
- _sheet = new CSSStyleSheet();
82
- _sheet.replaceSync(bv_button_default);
77
+ // components/_shared/adopt-styles.ts
78
+ var _sheetCache = /* @__PURE__ */ new Map();
79
+ function adoptStyles(shadow, css) {
80
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
81
+ let sheet = _sheetCache.get(css);
82
+ if (!sheet) {
83
+ sheet = new CSSStyleSheet();
84
+ sheet.replaceSync(css);
85
+ _sheetCache.set(css, sheet);
86
+ }
87
+ shadow.adoptedStyleSheets = [sheet];
88
+ return;
83
89
  }
84
- return _sheet;
90
+ const style = document.createElement("style");
91
+ style.textContent = css;
92
+ shadow.appendChild(style);
85
93
  }
94
+
95
+ // components/bv-button/bv-button.ts
86
96
  var BvButton = class extends HTMLElement {
87
97
  static observedAttributes = [
88
98
  "variant",
@@ -98,10 +108,7 @@ var BvButton = class extends HTMLElement {
98
108
  ];
99
109
  constructor() {
100
110
  super();
101
- const shadow = this.attachShadow({ mode: "open" });
102
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
103
- shadow.adoptedStyleSheets = [getSheet()];
104
- }
111
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_button_default);
105
112
  }
106
113
  // ── Accessors ────────────────────────────────────────────────
107
114
  get variant() {
@@ -158,11 +165,6 @@ var BvButton = class extends HTMLElement {
158
165
  const shadow = this.shadowRoot;
159
166
  let btn = shadow.querySelector("button");
160
167
  if (!btn) {
161
- if (!shadow.adoptedStyleSheets.length) {
162
- const styleEl = document.createElement("style");
163
- styleEl.textContent = bv_button_default;
164
- shadow.appendChild(styleEl);
165
- }
166
168
  btn = document.createElement("button");
167
169
  btn.setAttribute("part", "button");
168
170
  btn.appendChild(document.createElement("slot"));
@@ -1,25 +1,32 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-category-pill/bv-category-pill.css
2
2
  var bv_category_pill_default = ":host {\n display: inline-flex;\n font-family: var(--bv-font-family-base);\n}\n\n.pill {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 4px 6px;\n border: 0.5px solid var(--bv-color-neutral-900);\n border-radius: var(--bv-radius-sm);\n font-size: 10px;\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n line-height: 1;\n white-space: nowrap;\n}\n";
3
3
 
4
- // components/bv-category-pill/bv-category-pill.ts
5
- var _sheet = null;
6
- function getSheet() {
7
- if (!_sheet) {
8
- _sheet = new CSSStyleSheet();
9
- _sheet.replaceSync(bv_category_pill_default);
4
+ // components/_shared/adopt-styles.ts
5
+ var _sheetCache = /* @__PURE__ */ new Map();
6
+ function adoptStyles(shadow, css) {
7
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
8
+ let sheet = _sheetCache.get(css);
9
+ if (!sheet) {
10
+ sheet = new CSSStyleSheet();
11
+ sheet.replaceSync(css);
12
+ _sheetCache.set(css, sheet);
13
+ }
14
+ shadow.adoptedStyleSheets = [sheet];
15
+ return;
10
16
  }
11
- return _sheet;
17
+ const style = document.createElement("style");
18
+ style.textContent = css;
19
+ shadow.appendChild(style);
12
20
  }
21
+
22
+ // components/bv-category-pill/bv-category-pill.ts
13
23
  var BvCategoryPill = class extends HTMLElement {
14
24
  static observedAttributes = ["label"];
15
25
  _rendered = false;
16
26
  _labelEl = null;
17
27
  constructor() {
18
28
  super();
19
- const shadow = this.attachShadow({ mode: "open" });
20
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
21
- shadow.adoptedStyleSheets = [getSheet()];
22
- }
29
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_category_pill_default);
23
30
  }
24
31
  get label() {
25
32
  return this.getAttribute("label") ?? "";
@@ -38,11 +45,6 @@ var BvCategoryPill = class extends HTMLElement {
38
45
  if (this._rendered) return;
39
46
  this._rendered = true;
40
47
  const shadow = this.shadowRoot;
41
- if (!shadow.adoptedStyleSheets.length) {
42
- const s = document.createElement("style");
43
- s.textContent = bv_category_pill_default;
44
- shadow.appendChild(s);
45
- }
46
48
  const pill = document.createElement("span");
47
49
  pill.className = "pill";
48
50
  pill.setAttribute("part", "pill");
@@ -1,6 +1,24 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-checkbox/bv-checkbox.css
2
2
  var bv_checkbox_default = ":host {\n display: inline-flex;\n font-family: var(--bv-font-family-base);\n}\n\n/* \u2500\u2500 Size tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n:host,\n:host([size='sm']) {\n --_box-size: 16px;\n --_box-radius: 4px;\n --_gap: 8px;\n --_check-size: 10px;\n --_label-size: var(--bv-font-size-sm);\n --_label-lh: 20px;\n}\n\n:host([size='md']) {\n --_box-size: 20px;\n --_box-radius: 6px;\n --_gap: 12px;\n --_check-size: 12px;\n --_label-size: var(--bv-font-size-md);\n --_label-lh: 24px;\n}\n\n/* \u2500\u2500 Layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.wrap {\n display: inline-flex;\n align-items: flex-start;\n gap: var(--_gap);\n cursor: pointer;\n user-select: none;\n}\n\n:host([disabled]) .wrap {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* \u2500\u2500 Box \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.box {\n width: var(--_box-size);\n height: var(--_box-size);\n border-radius: var(--_box-radius);\n border: 1px solid var(--bv-color-neutral-300);\n background: var(--bv-color-neutral-0);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n margin-top: 2px;\n transition:\n background var(--bv-transition-fast),\n border-color var(--bv-transition-fast);\n}\n\n:host([checked]) .box,\n:host([indeterminate]) .box {\n background: var(--bv-color-primary-50);\n border-color: var(--bv-color-primary);\n}\n\n/* \u2500\u2500 Check icon \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.check {\n display: none;\n width: var(--_check-size);\n height: var(--_check-size);\n color: var(--bv-color-primary);\n line-height: 0;\n}\n\n.check svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n:host([checked]) .check,\n:host([indeterminate]) .check {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* \u2500\u2500 Label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.label {\n font-size: var(--_label-size);\n font-weight: var(--bv-font-weight-medium);\n color: var(--bv-color-secondary-foreground);\n line-height: var(--_label-lh);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n";
3
3
 
4
+ // components/_shared/adopt-styles.ts
5
+ var _sheetCache = /* @__PURE__ */ new Map();
6
+ function adoptStyles(shadow, css) {
7
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
8
+ let sheet = _sheetCache.get(css);
9
+ if (!sheet) {
10
+ sheet = new CSSStyleSheet();
11
+ sheet.replaceSync(css);
12
+ _sheetCache.set(css, sheet);
13
+ }
14
+ shadow.adoptedStyleSheets = [sheet];
15
+ return;
16
+ }
17
+ const style = document.createElement("style");
18
+ style.textContent = css;
19
+ shadow.appendChild(style);
20
+ }
21
+
4
22
  // components/bv-checkbox/bv-checkbox.ts
5
23
  var CHECK_ICON = `<svg viewBox="0 0 11.3333 8.41667" fill="none" xmlns="http://www.w3.org/2000/svg">
6
24
  <path d="M10.3333 1L3.91667 7.41667L1 4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
@@ -15,12 +33,7 @@ var BvCheckbox = class extends HTMLElement {
15
33
  _labelEl = null;
16
34
  constructor() {
17
35
  super();
18
- this.attachShadow({ mode: "open" });
19
- if ("replaceSync" in CSSStyleSheet.prototype) {
20
- const sheet = new CSSStyleSheet();
21
- sheet.replaceSync(bv_checkbox_default);
22
- this.shadowRoot.adoptedStyleSheets = [sheet];
23
- }
36
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_checkbox_default);
24
37
  }
25
38
  connectedCallback() {
26
39
  this._render();
@@ -44,11 +57,6 @@ var BvCheckbox = class extends HTMLElement {
44
57
  if (this._rendered) return;
45
58
  this._rendered = true;
46
59
  const shadow = this.shadowRoot;
47
- if (!shadow.adoptedStyleSheets.length) {
48
- const s = document.createElement("style");
49
- s.textContent = bv_checkbox_default;
50
- shadow.appendChild(s);
51
- }
52
60
  const wrap = document.createElement("label");
53
61
  wrap.className = "wrap";
54
62
  const box = document.createElement("span");
@@ -1,6 +1,24 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-chip/bv-chip.css
2
2
  var bv_chip_default = ":host {\n display: inline-flex;\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 28px;\n padding: 0 10px 0 12px;\n border-radius: var(--bv-radius-md, 6px);\n background: var(--bv-color-primary-50, #eff8ff);\n border: 1px solid #b9e6fe;\n color: var(--bv-color-primary-dark, #026aa2);\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-medium);\n line-height: 1;\n white-space: nowrap;\n user-select: none;\n}\n\n.label {\n flex: none;\n}\n\n.dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n padding: 0;\n background: none;\n border: none;\n cursor: pointer;\n color: inherit;\n flex-shrink: 0;\n opacity: 0.6;\n border-radius: 2px;\n transition: opacity var(--bv-transition-fast, 150ms);\n}\n\n.dismiss:hover {\n opacity: 1;\n}\n\n.dismiss:focus-visible {\n outline: 2px solid var(--bv-color-primary);\n outline-offset: 1px;\n}\n";
3
3
 
4
+ // components/_shared/adopt-styles.ts
5
+ var _sheetCache = /* @__PURE__ */ new Map();
6
+ function adoptStyles(shadow, css) {
7
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
8
+ let sheet = _sheetCache.get(css);
9
+ if (!sheet) {
10
+ sheet = new CSSStyleSheet();
11
+ sheet.replaceSync(css);
12
+ _sheetCache.set(css, sheet);
13
+ }
14
+ shadow.adoptedStyleSheets = [sheet];
15
+ return;
16
+ }
17
+ const style = document.createElement("style");
18
+ style.textContent = css;
19
+ shadow.appendChild(style);
20
+ }
21
+
4
22
  // components/bv-chip/bv-chip.ts
5
23
  var BvChip = class extends HTMLElement {
6
24
  static observedAttributes = ["label"];
@@ -14,12 +32,7 @@ var BvChip = class extends HTMLElement {
14
32
  _labelEl = null;
15
33
  constructor() {
16
34
  super();
17
- this.attachShadow({ mode: "open" });
18
- if ("replaceSync" in CSSStyleSheet.prototype) {
19
- const sheet = new CSSStyleSheet();
20
- sheet.replaceSync(bv_chip_default);
21
- this.shadowRoot.adoptedStyleSheets = [sheet];
22
- }
35
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_chip_default);
23
36
  }
24
37
  connectedCallback() {
25
38
  this._render();
@@ -34,11 +47,6 @@ var BvChip = class extends HTMLElement {
34
47
  if (this._rendered) return;
35
48
  this._rendered = true;
36
49
  const shadow = this.shadowRoot;
37
- if (!shadow.adoptedStyleSheets.length) {
38
- const s = document.createElement("style");
39
- s.textContent = bv_chip_default;
40
- shadow.appendChild(s);
41
- }
42
50
  const chip = document.createElement("span");
43
51
  chip.className = "chip";
44
52
  const labelEl = document.createElement("span");