@bluevolt-tech/lumen 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +86 -0
  4. package/dist/components/_shared/dropdown-controller.d.ts +60 -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 -6
  16. package/dist/components/bv-course-grid-card/bv-course-grid-card.js +98 -105
  17. package/dist/components/bv-course-list-card/bv-course-list-card.d.ts +2 -6
  18. package/dist/components/bv-course-list-card/bv-course-list-card.js +98 -105
  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 +10 -6
  21. package/dist/components/bv-date-range-input/bv-date-range-input.js +602 -125
  22. package/dist/components/bv-date-range-picker/bv-date-range-picker.d.ts +71 -4
  23. package/dist/components/bv-date-range-picker/bv-date-range-picker.js +441 -76
  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 +80 -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 +79 -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 +78 -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 +78 -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 +70 -19
  50. package/dist/components/bv-toggle/bv-toggle.js +19 -11
  51. package/dist/components/bv-tooltip/bv-tooltip.js +20 -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 +1 -1
  55. package/package.json +8 -1
  56. package/themes/base.css +1 -1
@@ -1,17 +1,78 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-select/bv-select.css
2
2
  var bv_select_default = ":host {\n display: block;\n position: relative;\n font-family: var(--bv-font-family-base);\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 */\n\n.field-label {\n display: block;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-800);\n margin-bottom: 6px;\n}\n\n.required-mark {\n color: var(--bv-color-danger);\n margin-left: 2px;\n}\n\n/* \u2500\u2500 Trigger \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.trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n height: 44px;\n padding: 0 var(--bv-spacing-md);\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-lg);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n color: var(--bv-color-neutral-800);\n text-align: left;\n transition:\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n.trigger:focus-visible {\n outline: none;\n border-color: var(--bv-color-primary);\n box-shadow:\n 0 0 0 3px var(--bv-color-primary-light),\n 0 1px 2px 0 rgba(16, 24, 40, 0.05);\n}\n\n:host([open]) .trigger {\n border-color: var(--bv-color-primary);\n box-shadow:\n 0 0 0 3px var(--bv-color-primary-light),\n 0 1px 2px 0 rgba(16, 24, 40, 0.05);\n}\n\n:host([error]:not([error=''])) .trigger {\n border-color: var(--bv-color-danger);\n}\n\n.trigger:disabled {\n background: var(--bv-color-neutral-100);\n color: var(--bv-color-neutral-400);\n cursor: not-allowed;\n}\n\n.trigger-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.trigger-label.is-placeholder {\n color: var(--bv-color-neutral-400);\n}\n\n.trigger-chevron {\n display: flex;\n color: var(--bv-color-neutral-500);\n flex-shrink: 0;\n margin-left: var(--bv-spacing-sm);\n transition: transform var(--bv-transition-fast);\n}\n\n:host([open]) .trigger-chevron {\n transform: rotate(180deg);\n}\n\n/* \u2500\u2500 Dropdown panel \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.panel {\n display: none;\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-200);\n border-radius: var(--bv-radius-lg);\n box-shadow: var(--bv-shadow-md);\n z-index: 200;\n overflow: hidden;\n padding: var(--bv-spacing-xs) 0;\n}\n\n:host([open]) .panel {\n display: block;\n}\n\n/* \u2500\u2500 Options \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.option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--bv-spacing-sm);\n padding: 10px var(--bv-spacing-md);\n font-size: var(--bv-font-size-sm);\n color: var(--bv-color-neutral-700);\n cursor: pointer;\n transition: background var(--bv-transition-fast);\n}\n\n.option:hover {\n background: var(--bv-color-secondary);\n}\n\n.option[aria-selected='true'] {\n font-weight: var(--bv-font-weight-medium);\n color: var(--bv-color-neutral-900);\n background: var(--bv-color-secondary-light);\n}\n\n.option-text {\n flex: 1;\n}\n\n.option-check {\n display: none;\n color: var(--bv-color-primary);\n flex-shrink: 0;\n}\n\n.option[aria-selected='true'] .option-check {\n display: flex;\n}\n\n/* \u2500\u2500 Error message \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.field-error {\n display: block;\n font-size: var(--bv-font-size-xs);\n color: var(--bv-color-danger);\n margin-top: var(--bv-spacing-xs);\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
+
22
+ // components/_shared/dropdown-controller.ts
23
+ function bindDropdownController(host, options = {}) {
24
+ if (!host.shadowRoot) {
25
+ throw new Error("bindDropdownController requires the host to have an open shadow root.");
26
+ }
27
+ const syncExpanded = () => {
28
+ var _a;
29
+ (_a = options.trigger) == null ? void 0 : _a.setAttribute("aria-expanded", host.hasAttribute("open") ? "true" : "false");
30
+ };
31
+ const open = () => {
32
+ var _a;
33
+ if (host.hasAttribute("open")) return;
34
+ (_a = options.onBeforeOpen) == null ? void 0 : _a.call(options);
35
+ host.setAttribute("open", "");
36
+ syncExpanded();
37
+ host.dispatchEvent(new CustomEvent("bv-open", { bubbles: true, composed: true }));
38
+ };
39
+ const closeWithReason = (reason) => {
40
+ var _a;
41
+ if (!host.hasAttribute("open")) return;
42
+ if (options.canClose && !options.canClose(reason)) return;
43
+ host.removeAttribute("open");
44
+ syncExpanded();
45
+ (_a = options.onAfterClose) == null ? void 0 : _a.call(options);
46
+ };
47
+ const close = () => closeWithReason("programmatic");
48
+ const toggle = () => {
49
+ if (host.hasAttribute("open")) closeWithReason("programmatic");
50
+ else open();
51
+ };
52
+ const offClick = (e) => {
53
+ if (!host.hasAttribute("open")) return;
54
+ if (e.composedPath().includes(host)) return;
55
+ closeWithReason("outside-click");
56
+ };
57
+ document.addEventListener("click", offClick);
58
+ const onKeyDown = (e) => {
59
+ if (e.key === "Escape") closeWithReason("escape");
60
+ };
61
+ host.addEventListener("keydown", onKeyDown);
62
+ const observer = new MutationObserver(syncExpanded);
63
+ observer.observe(host, { attributes: true, attributeFilter: ["open"] });
64
+ const cleanup = () => {
65
+ observer.disconnect();
66
+ document.removeEventListener("click", offClick);
67
+ host.removeEventListener("keydown", onKeyDown);
68
+ };
69
+ syncExpanded();
70
+ return { open, close, toggle, cleanup };
71
+ }
72
+
4
73
  // components/bv-select/bv-select.ts
5
74
  var CHEVRON_DOWN = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>`;
6
75
  var CHECK = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>`;
7
- var _sheet = null;
8
- function getSheet() {
9
- if (!_sheet) {
10
- _sheet = new CSSStyleSheet();
11
- _sheet.replaceSync(bv_select_default);
12
- }
13
- return _sheet;
14
- }
15
76
  var BvSelect = class extends HTMLElement {
16
77
  static observedAttributes = [
17
78
  "label",
@@ -30,13 +91,10 @@ var BvSelect = class extends HTMLElement {
30
91
  _errorEl = null;
31
92
  _options = [];
32
93
  _value = "";
33
- _offClick = null;
94
+ _dropdown = null;
34
95
  constructor() {
35
96
  super();
36
- const shadow = this.attachShadow({ mode: "open" });
37
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
38
- shadow.adoptedStyleSheets = [getSheet()];
39
- }
97
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_select_default);
40
98
  }
41
99
  get value() {
42
100
  return this._value;
@@ -62,7 +120,8 @@ var BvSelect = class extends HTMLElement {
62
120
  this._render();
63
121
  }
64
122
  disconnectedCallback() {
65
- if (this._offClick) document.removeEventListener("click", this._offClick);
123
+ var _a;
124
+ (_a = this._dropdown) == null ? void 0 : _a.cleanup();
66
125
  }
67
126
  attributeChangedCallback(name, _old, val) {
68
127
  if (name === "options") {
@@ -96,11 +155,6 @@ var BvSelect = class extends HTMLElement {
96
155
  }
97
156
  this._value = this.getAttribute("value") ?? "";
98
157
  const shadow = this.shadowRoot;
99
- if (!shadow.adoptedStyleSheets.length) {
100
- const s = document.createElement("style");
101
- s.textContent = bv_select_default;
102
- shadow.appendChild(s);
103
- }
104
158
  const label = document.createElement("label");
105
159
  label.className = "field-label";
106
160
  this._labelEl = label;
@@ -119,8 +173,9 @@ var BvSelect = class extends HTMLElement {
119
173
  chevron.innerHTML = CHEVRON_DOWN;
120
174
  trigger.append(triggerLabel, chevron);
121
175
  trigger.addEventListener("click", (e) => {
176
+ var _a;
122
177
  e.stopPropagation();
123
- if (!this.hasAttribute("disabled")) this._toggle();
178
+ if (!this.hasAttribute("disabled")) (_a = this._dropdown) == null ? void 0 : _a.toggle();
124
179
  });
125
180
  const panel = document.createElement("div");
126
181
  panel.className = "panel";
@@ -136,29 +191,7 @@ var BvSelect = class extends HTMLElement {
136
191
  this._renderOptions();
137
192
  this._syncError();
138
193
  this._syncDisabled();
139
- this._offClick = (e) => {
140
- if (!this.contains(e.target) && !shadow.contains(e.target)) {
141
- this._close();
142
- }
143
- };
144
- document.addEventListener("click", this._offClick);
145
- this.addEventListener("keydown", (e) => {
146
- if (e.key === "Escape") this._close();
147
- });
148
- }
149
- _toggle() {
150
- if (this.hasAttribute("open")) this._close();
151
- else this._open();
152
- }
153
- _open() {
154
- var _a;
155
- this.setAttribute("open", "");
156
- (_a = this._triggerEl) == null ? void 0 : _a.setAttribute("aria-expanded", "true");
157
- }
158
- _close() {
159
- var _a;
160
- this.removeAttribute("open");
161
- (_a = this._triggerEl) == null ? void 0 : _a.setAttribute("aria-expanded", "false");
194
+ this._dropdown = bindDropdownController(this, { trigger });
162
195
  }
163
196
  _syncLabel() {
164
197
  if (!this._labelEl) return;
@@ -218,11 +251,12 @@ var BvSelect = class extends HTMLElement {
218
251
  this._triggerEl.disabled = this.hasAttribute("disabled");
219
252
  }
220
253
  _select(opt) {
254
+ var _a;
221
255
  this._value = opt.value;
222
256
  this.setAttribute("value", opt.value);
223
257
  this._syncTriggerLabel();
224
258
  this._syncSelected();
225
- this._close();
259
+ (_a = this._dropdown) == null ? void 0 : _a.close();
226
260
  this.dispatchEvent(
227
261
  new CustomEvent("bv-change", {
228
262
  bubbles: true,
@@ -15,7 +15,7 @@ export declare class BvSplitButton extends HTMLElement {
15
15
  private _toggleBtn;
16
16
  private _labelEl;
17
17
  private _panelEl;
18
- private _offClick;
18
+ private _dropdown;
19
19
  constructor();
20
20
  get label(): string;
21
21
  set label(v: string);
@@ -29,9 +29,6 @@ export declare class BvSplitButton extends HTMLElement {
29
29
  disconnectedCallback(): void;
30
30
  attributeChangedCallback(name: string, _old: string | null, _val: string | null): void;
31
31
  private _render;
32
- private _toggle;
33
- private _open;
34
- private _close;
35
32
  private _syncDisabled;
36
33
  private _syncPanel;
37
34
  }
@@ -1,6 +1,75 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-split-button/bv-split-button.css
2
2
  var bv_split_button_default = ":host {\n --bv-button-bg: var(--bv-color-primary);\n --bv-button-bg-hover: color-mix(in srgb, var(--bv-color-primary) 82%, black);\n --bv-button-color: var(--bv-color-neutral-0);\n --bv-button-border-color: var(--bv-color-primary);\n\n display: inline-flex;\n position: relative;\n font-family: var(--bv-font-family-base);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n/* \u2500\u2500 Variants \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([variant='secondary']) {\n --bv-button-bg: var(--bv-color-secondary);\n --bv-button-bg-hover: color-mix(in srgb, var(--bv-color-secondary) 82%, black);\n --bv-button-color: var(--bv-color-neutral-0);\n --bv-button-border-color: var(--bv-color-secondary);\n}\n\n:host([variant='outline']) {\n --bv-button-bg: transparent;\n --bv-button-bg-hover: var(--bv-color-neutral-100);\n --bv-button-color: var(--bv-color-primary);\n --bv-button-border-color: var(--bv-color-primary);\n}\n\n:host([variant='outline-secondary']) {\n --bv-button-bg: transparent;\n --bv-button-bg-hover: var(--bv-color-neutral-100);\n --bv-button-color: var(--bv-color-neutral-700);\n --bv-button-border-color: var(--bv-color-neutral-300);\n}\n\n/* \u2500\u2500 Wrap \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: flex;\n align-items: stretch;\n width: 100%;\n}\n\n/* \u2500\u2500 Main 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 */\n\n.main-btn {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0 var(--bv-spacing-md);\n min-height: 44px;\n background: var(--bv-button-bg);\n color: var(--bv-button-color);\n border: none;\n border-radius: var(--bv-radius-lg) 0 0 var(--bv-radius-lg);\n font-family: inherit;\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n cursor: pointer;\n white-space: nowrap;\n transition: background var(--bv-transition-fast);\n}\n\n.main-btn:hover:not(:disabled) {\n background: var(--bv-button-bg-hover);\n}\n\n/* \u2500\u2500 Divider \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.divider {\n width: 1px;\n background: var(--bv-button-color);\n opacity: 0.25;\n flex-shrink: 0;\n}\n\n/* \u2500\u2500 Toggle 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 */\n\n.toggle-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n flex-shrink: 0;\n background: var(--bv-button-bg);\n color: var(--bv-button-color);\n border: none;\n border-radius: 0 var(--bv-radius-lg) var(--bv-radius-lg) 0;\n cursor: pointer;\n transition: background var(--bv-transition-fast);\n}\n\n.toggle-btn:hover:not(:disabled) {\n background: var(--bv-button-bg-hover);\n}\n\n.main-btn:disabled,\n.toggle-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* \u2500\u2500 Panel \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.panel {\n display: none;\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n min-width: 220px;\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-200);\n border-radius: var(--bv-radius-lg);\n box-shadow: var(--bv-shadow-md);\n z-index: 200;\n padding: var(--bv-spacing-xs) 0;\n}\n\n:host([open]) .panel {\n display: block;\n}\n\n/* \u2500\u2500 Option \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.option {\n display: flex;\n align-items: center;\n gap: var(--bv-spacing-sm);\n width: 100%;\n padding: var(--bv-spacing-sm) var(--bv-spacing-md);\n background: transparent;\n border: none;\n text-align: left;\n cursor: pointer;\n font-family: inherit;\n transition: background var(--bv-transition-fast);\n}\n\n.option:hover {\n background: var(--bv-color-neutral-100);\n}\n\n.option-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--bv-color-neutral-600);\n flex-shrink: 0;\n}\n\n.option-content {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.option-label {\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-700);\n line-height: 1.4;\n}\n\n.option-desc {\n font-size: var(--bv-font-size-xs);\n color: var(--bv-color-neutral-500);\n line-height: 1.4;\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
+
22
+ // components/_shared/dropdown-controller.ts
23
+ function bindDropdownController(host, options = {}) {
24
+ if (!host.shadowRoot) {
25
+ throw new Error("bindDropdownController requires the host to have an open shadow root.");
26
+ }
27
+ const syncExpanded = () => {
28
+ var _a;
29
+ (_a = options.trigger) == null ? void 0 : _a.setAttribute("aria-expanded", host.hasAttribute("open") ? "true" : "false");
30
+ };
31
+ const open = () => {
32
+ var _a;
33
+ if (host.hasAttribute("open")) return;
34
+ (_a = options.onBeforeOpen) == null ? void 0 : _a.call(options);
35
+ host.setAttribute("open", "");
36
+ syncExpanded();
37
+ host.dispatchEvent(new CustomEvent("bv-open", { bubbles: true, composed: true }));
38
+ };
39
+ const closeWithReason = (reason) => {
40
+ var _a;
41
+ if (!host.hasAttribute("open")) return;
42
+ if (options.canClose && !options.canClose(reason)) return;
43
+ host.removeAttribute("open");
44
+ syncExpanded();
45
+ (_a = options.onAfterClose) == null ? void 0 : _a.call(options);
46
+ };
47
+ const close = () => closeWithReason("programmatic");
48
+ const toggle = () => {
49
+ if (host.hasAttribute("open")) closeWithReason("programmatic");
50
+ else open();
51
+ };
52
+ const offClick = (e) => {
53
+ if (!host.hasAttribute("open")) return;
54
+ if (e.composedPath().includes(host)) return;
55
+ closeWithReason("outside-click");
56
+ };
57
+ document.addEventListener("click", offClick);
58
+ const onKeyDown = (e) => {
59
+ if (e.key === "Escape") closeWithReason("escape");
60
+ };
61
+ host.addEventListener("keydown", onKeyDown);
62
+ const observer = new MutationObserver(syncExpanded);
63
+ observer.observe(host, { attributes: true, attributeFilter: ["open"] });
64
+ const cleanup = () => {
65
+ observer.disconnect();
66
+ document.removeEventListener("click", offClick);
67
+ host.removeEventListener("keydown", onKeyDown);
68
+ };
69
+ syncExpanded();
70
+ return { open, close, toggle, cleanup };
71
+ }
72
+
4
73
  // components/bv-icon/icons.ts
5
74
  var icons = {
6
75
  // Navigation
@@ -76,14 +145,6 @@ var rawIcons = {
76
145
 
77
146
  // components/bv-split-button/bv-split-button.ts
78
147
  var CHEVRON_DOWN = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>`;
79
- var _sheet = null;
80
- function getSheet() {
81
- if (!_sheet) {
82
- _sheet = new CSSStyleSheet();
83
- _sheet.replaceSync(bv_split_button_default);
84
- }
85
- return _sheet;
86
- }
87
148
  var BvSplitButton = class extends HTMLElement {
88
149
  static observedAttributes = ["label", "variant", "disabled", "options", "open"];
89
150
  _rendered = false;
@@ -91,13 +152,10 @@ var BvSplitButton = class extends HTMLElement {
91
152
  _toggleBtn = null;
92
153
  _labelEl = null;
93
154
  _panelEl = null;
94
- _offClick = null;
155
+ _dropdown = null;
95
156
  constructor() {
96
157
  super();
97
- const shadow = this.attachShadow({ mode: "open" });
98
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
99
- shadow.adoptedStyleSheets = [getSheet()];
100
- }
158
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_split_button_default);
101
159
  }
102
160
  get label() {
103
161
  return this.getAttribute("label") ?? "";
@@ -132,7 +190,8 @@ var BvSplitButton = class extends HTMLElement {
132
190
  this._render();
133
191
  }
134
192
  disconnectedCallback() {
135
- if (this._offClick) document.removeEventListener("click", this._offClick);
193
+ var _a;
194
+ (_a = this._dropdown) == null ? void 0 : _a.cleanup();
136
195
  }
137
196
  attributeChangedCallback(name, _old, _val) {
138
197
  if (!this._rendered) return;
@@ -144,11 +203,6 @@ var BvSplitButton = class extends HTMLElement {
144
203
  if (this._rendered) return;
145
204
  this._rendered = true;
146
205
  const shadow = this.shadowRoot;
147
- if (!shadow.adoptedStyleSheets.length) {
148
- const s = document.createElement("style");
149
- s.textContent = bv_split_button_default;
150
- shadow.appendChild(s);
151
- }
152
206
  const wrap = document.createElement("div");
153
207
  wrap.className = "wrap";
154
208
  const mainBtn = document.createElement("button");
@@ -176,8 +230,9 @@ var BvSplitButton = class extends HTMLElement {
176
230
  toggleBtn.setAttribute("aria-label", "More options");
177
231
  toggleBtn.innerHTML = CHEVRON_DOWN;
178
232
  toggleBtn.addEventListener("click", (e) => {
233
+ var _a;
179
234
  e.stopPropagation();
180
- if (!this.disabled) this._toggle();
235
+ if (!this.disabled) (_a = this._dropdown) == null ? void 0 : _a.toggle();
181
236
  });
182
237
  this._toggleBtn = toggleBtn;
183
238
  wrap.append(mainBtn, divider, toggleBtn);
@@ -188,27 +243,7 @@ var BvSplitButton = class extends HTMLElement {
188
243
  shadow.append(wrap, panel);
189
244
  this._syncDisabled();
190
245
  this._syncPanel();
191
- this._offClick = (e) => {
192
- if (!this.contains(e.target) && !shadow.contains(e.target)) {
193
- this._close();
194
- }
195
- };
196
- document.addEventListener("click", this._offClick);
197
- }
198
- _toggle() {
199
- if (this.hasAttribute("open")) this._close();
200
- else this._open();
201
- }
202
- _open() {
203
- var _a;
204
- this.setAttribute("open", "");
205
- (_a = this._toggleBtn) == null ? void 0 : _a.setAttribute("aria-expanded", "true");
206
- this.dispatchEvent(new CustomEvent("bv-open", { bubbles: true, composed: true }));
207
- }
208
- _close() {
209
- var _a;
210
- this.removeAttribute("open");
211
- (_a = this._toggleBtn) == null ? void 0 : _a.setAttribute("aria-expanded", "false");
246
+ this._dropdown = bindDropdownController(this, { trigger: toggleBtn });
212
247
  }
213
248
  _syncDisabled() {
214
249
  const d = this.disabled;
@@ -243,7 +278,8 @@ var BvSplitButton = class extends HTMLElement {
243
278
  }
244
279
  btn.appendChild(content);
245
280
  btn.addEventListener("click", () => {
246
- this._close();
281
+ var _a;
282
+ (_a = this._dropdown) == null ? void 0 : _a.close();
247
283
  this.dispatchEvent(
248
284
  new CustomEvent("bv-option-select", {
249
285
  bubbles: true,
@@ -1,6 +1,24 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-star-rating/bv-star-rating.css
2
2
  var bv_star_rating_default = ":host {\n --bv-star-color: #f59e0b;\n --bv-star-empty-color: var(--bv-color-neutral-200);\n --bv-star-size: 20px;\n display: inline-flex;\n}\n\n:host([size='sm']) {\n --bv-star-size: 14px;\n}\n:host([size='lg']) {\n --bv-star-size: 28px;\n}\n\n.stars {\n display: inline-flex;\n gap: 2px;\n align-items: center;\n}\n\n.star {\n width: var(--bv-star-size);\n height: var(--bv-star-size);\n color: var(--bv-star-empty-color);\n flex-shrink: 0;\n transition: color var(--bv-transition-fast);\n}\n\n.star.filled {\n color: var(--bv-star-color);\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-star-rating/bv-star-rating.ts
5
23
  var STAR_PATH = "M10 1.5L12.39 6.26L17.72 7.07L13.86 10.83L14.78 16.14L10 13.63L5.22 16.14L6.14 10.83L2.28 7.07L7.61 6.26L10 1.5Z";
6
24
  var BvStarRating = class extends HTMLElement {
@@ -8,12 +26,7 @@ var BvStarRating = class extends HTMLElement {
8
26
  _rendered = false;
9
27
  constructor() {
10
28
  super();
11
- this.attachShadow({ mode: "open" });
12
- if ("replaceSync" in CSSStyleSheet.prototype) {
13
- const sheet = new CSSStyleSheet();
14
- sheet.replaceSync(bv_star_rating_default);
15
- this.shadowRoot.adoptedStyleSheets = [sheet];
16
- }
29
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_star_rating_default);
17
30
  }
18
31
  connectedCallback() {
19
32
  this._render();
@@ -31,11 +44,6 @@ var BvStarRating = class extends HTMLElement {
31
44
  if (this._rendered) return;
32
45
  this._rendered = true;
33
46
  const shadow = this.shadowRoot;
34
- if (!shadow.adoptedStyleSheets.length) {
35
- const s = document.createElement("style");
36
- s.textContent = bv_star_rating_default;
37
- shadow.appendChild(s);
38
- }
39
47
  const wrap = document.createElement("div");
40
48
  wrap.className = "stars";
41
49
  wrap.setAttribute("role", "img");
@@ -7,8 +7,13 @@ export declare class BvTable extends HTMLElement {
7
7
  private _boundMouseDown;
8
8
  private _boundMouseMove;
9
9
  private _boundMouseUp;
10
+ get loading(): boolean;
11
+ set loading(v: boolean);
10
12
  connectedCallback(): void;
11
13
  disconnectedCallback(): void;
14
+ attributeChangedCallback(name: string, _old: string | null, val: string | null): void;
15
+ private _renderSkeleton;
16
+ private _removeSkeleton;
12
17
  private _setupDrag;
13
18
  private _teardownDrag;
14
19
  private _enhanceBodyCells;
@@ -1,19 +1,29 @@
1
1
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-table/bv-table.css
2
- var bv_table_default = "bv-table {\n display: block;\n width: 100%;\n box-sizing: border-box;\n overflow-x: auto;\n border: 1px solid var(--bv-color-neutral-200);\n font-family: var(--bv-font-family-base);\n cursor: grab;\n}\n\n/* During drag: override cursor for the entire table and all children */\nbv-table[data-dragging],\nbv-table[data-dragging] * {\n cursor: grabbing !important;\n user-select: none !important;\n}\n\nbv-table tbody td {\n cursor: grab;\n}\n\n/* Only the rendered glyphs are text-selectable \u2014 see _enhanceBodyCells in bv-table.ts */\nbv-table tbody td .bv-cell-text {\n cursor: text;\n}\n\nbv-table table {\n width: 100%;\n border-collapse: collapse;\n min-width: 0;\n}\n\n/* \u2500\u2500 Header \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\nbv-table thead tr {\n background: var(--bv-color-neutral-100);\n}\n\nbv-table thead th {\n padding: 12px 16px;\n text-align: left;\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-800);\n border-bottom: 1px solid var(--bv-color-neutral-200);\n white-space: nowrap;\n user-select: none;\n vertical-align: middle;\n}\n\nbv-table thead th[data-sortable] {\n cursor: pointer;\n}\n\nbv-table thead th[data-sortable]:hover {\n color: var(--bv-color-neutral-700);\n}\n\nbv-table thead th[data-sort='asc'] .bv-sort-icon,\nbv-table thead th[data-sort='desc'] .bv-sort-icon {\n color: var(--bv-color-primary);\n}\n\n.bv-th-content {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.bv-sort-icon {\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n}\n\n/* \u2500\u2500 Body \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\u2500*/\n\nbv-table tbody td {\n padding: 12px 16px;\n font-size: var(--bv-font-size-sm);\n color: var(--bv-color-neutral-700);\n border-bottom: 1px solid var(--bv-color-neutral-100);\n vertical-align: middle;\n white-space: nowrap;\n}\n\nbv-table tbody tr:last-child td {\n border-bottom: none;\n}\n\nbv-table tbody tr:hover td {\n background: var(--bv-color-neutral-100);\n}\n\n/* \u2500\u2500 Column alignment \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\nbv-table thead th[data-align='right'],\nbv-table tbody td[data-align='right'] {\n text-align: right;\n}\n\nbv-table thead th[data-align='center'],\nbv-table tbody td[data-align='center'] {\n text-align: center;\n}\n\n/* \u2500\u2500 Cell modifier classes \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/* Muted: em dash or empty placeholder */\nbv-table td.bv-cell-muted {\n color: var(--bv-color-neutral-400);\n}\n\n/* Hidden: redacted identity for external users */\nbv-table td.bv-cell-hidden {\n color: var(--bv-color-neutral-400);\n font-style: italic;\n}\n\n/* Colored text for numeric highlight states */\nbv-table td.bv-cell-success {\n color: var(--bv-color-success);\n font-weight: var(--bv-font-weight-medium);\n}\n\nbv-table td.bv-cell-warning {\n color: var(--bv-color-warning);\n font-weight: var(--bv-font-weight-medium);\n}\n\nbv-table td.bv-cell-danger {\n color: var(--bv-color-danger);\n font-weight: var(--bv-font-weight-medium);\n}\n\nbv-table td.bv-cell-info {\n color: var(--bv-color-primary);\n font-weight: var(--bv-font-weight-medium);\n}\n\n/* \u2500\u2500 Modifiers \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\nbv-table[striped] tbody tr:nth-child(even) td {\n background: var(--bv-color-neutral-100);\n}\n\nbv-table[bordered] thead th,\nbv-table[bordered] tbody td {\n border-right: 1px solid var(--bv-color-neutral-100);\n}\n\nbv-table[bordered] thead th:last-child,\nbv-table[bordered] tbody td:last-child {\n border-right: none;\n}\n\n/* \u2500\u2500 Column divider \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\nbv-table thead th[data-divider],\nbv-table tbody td[data-divider] {\n border-left: 1px solid var(--bv-color-neutral-200);\n}\n\n/* \u2500\u2500 Checkbox column \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\nbv-table thead th.bv-col-check,\nbv-table tbody td.bv-col-check {\n width: 48px;\n padding: 12px 16px;\n}\n";
2
+ var bv_table_default = "bv-table {\n display: block;\n width: 100%;\n box-sizing: border-box;\n overflow-x: auto;\n border: 1px solid var(--bv-color-neutral-200);\n font-family: var(--bv-font-family-base);\n cursor: grab;\n}\n\n/* During drag: override cursor for the entire table and all children */\nbv-table[data-dragging],\nbv-table[data-dragging] * {\n cursor: grabbing !important;\n user-select: none !important;\n}\n\nbv-table tbody td {\n cursor: grab;\n}\n\n/* Only the rendered glyphs are text-selectable \u2014 see _enhanceBodyCells in bv-table.ts */\nbv-table tbody td .bv-cell-text {\n cursor: text;\n}\n\nbv-table table {\n width: 100%;\n border-collapse: collapse;\n min-width: 0;\n}\n\n/* \u2500\u2500 Header \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\nbv-table thead tr {\n background: var(--bv-color-neutral-100);\n}\n\nbv-table thead th {\n padding: 12px 16px;\n text-align: left;\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-800);\n border-bottom: 1px solid var(--bv-color-neutral-200);\n white-space: nowrap;\n user-select: none;\n vertical-align: middle;\n}\n\nbv-table thead th[data-sortable] {\n cursor: pointer;\n}\n\nbv-table thead th[data-sortable]:hover {\n color: var(--bv-color-neutral-700);\n}\n\nbv-table thead th[data-sort='asc'] .bv-sort-icon,\nbv-table thead th[data-sort='desc'] .bv-sort-icon {\n color: var(--bv-color-primary);\n}\n\n.bv-th-content {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.bv-sort-icon {\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n}\n\n/* \u2500\u2500 Body \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\u2500*/\n\nbv-table tbody td {\n padding: 12px 16px;\n font-size: var(--bv-font-size-sm);\n color: var(--bv-color-neutral-700);\n border-bottom: 1px solid var(--bv-color-neutral-100);\n vertical-align: middle;\n white-space: nowrap;\n}\n\nbv-table tbody tr:last-child td {\n border-bottom: none;\n}\n\nbv-table tbody tr:hover td {\n background: var(--bv-color-neutral-100);\n}\n\n/* \u2500\u2500 Column alignment \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\nbv-table thead th[data-align='right'],\nbv-table tbody td[data-align='right'] {\n text-align: right;\n}\n\nbv-table thead th[data-align='center'],\nbv-table tbody td[data-align='center'] {\n text-align: center;\n}\n\n/* \u2500\u2500 Cell modifier classes \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/* Muted: em dash or empty placeholder */\nbv-table td.bv-cell-muted {\n color: var(--bv-color-neutral-400);\n}\n\n/* Hidden: redacted identity for external users */\nbv-table td.bv-cell-hidden {\n color: var(--bv-color-neutral-400);\n font-style: italic;\n}\n\n/* Colored text for numeric highlight states */\nbv-table td.bv-cell-success {\n color: var(--bv-color-success);\n font-weight: var(--bv-font-weight-medium);\n}\n\nbv-table td.bv-cell-warning {\n color: var(--bv-color-warning);\n font-weight: var(--bv-font-weight-medium);\n}\n\nbv-table td.bv-cell-danger {\n color: var(--bv-color-danger);\n font-weight: var(--bv-font-weight-medium);\n}\n\nbv-table td.bv-cell-info {\n color: var(--bv-color-primary);\n font-weight: var(--bv-font-weight-medium);\n}\n\n/* \u2500\u2500 Modifiers \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\nbv-table[striped] tbody tr:nth-child(even) td {\n background: var(--bv-color-neutral-100);\n}\n\nbv-table[bordered] thead th,\nbv-table[bordered] tbody td {\n border-right: 1px solid var(--bv-color-neutral-100);\n}\n\nbv-table[bordered] thead th:last-child,\nbv-table[bordered] tbody td:last-child {\n border-right: none;\n}\n\n/* \u2500\u2500 Column divider \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\nbv-table thead th[data-divider],\nbv-table tbody td[data-divider] {\n border-left: 1px solid var(--bv-color-neutral-200);\n}\n\n/* \u2500\u2500 Checkbox column \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\nbv-table thead th.bv-col-check,\nbv-table tbody td.bv-col-check {\n width: 48px;\n padding: 12px 16px;\n}\n\n/* \u2500\u2500 BTN-10191 \xB7 loading skeleton \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/* Hide the real table while the skeleton is showing. Consumers keep\n their <thead>/<tbody> markup in place so no re-hydration is needed\n when data lands \u2014 only display switches. */\nbv-table[loading] > table {\n display: none;\n}\n\n.bv-table-skeleton {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-md-minus);\n padding: var(--bv-spacing-md);\n}\n\n.bv-table-skeleton-row {\n display: flex;\n gap: var(--bv-spacing-lg);\n}\n\n.bv-table-skeleton-bar {\n flex: 1;\n height: 14px;\n border-radius: var(--bv-radius-sm);\n background: var(--bv-color-neutral-200);\n animation: bv-table-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n/* Slight offset per row so the shimmer isn't in lockstep \u2014 reads more\n like real content loading than a pulsing wall. */\n.bv-table-skeleton-row:nth-child(even) .bv-table-skeleton-bar {\n animation-delay: 0.2s;\n}\n\n@keyframes bv-table-skeleton-shimmer {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n";
3
3
 
4
4
  // css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-tooltip/bv-tooltip.css
5
5
  var bv_tooltip_default = ":host {\n display: inline-flex;\n align-items: center;\n position: relative;\n font-family: var(--bv-font-family-base);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n.wrap {\n display: inline-flex;\n align-items: center;\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n cursor: help;\n color: var(--bv-color-neutral-400);\n outline: none;\n border: none;\n background: none;\n padding: 0;\n line-height: 0;\n}\n\n.trigger:hover,\n.trigger:focus {\n color: var(--bv-color-neutral-600);\n}\n\n.trigger svg {\n display: block;\n width: 14px;\n height: 14px;\n}\n\n/* \u2500\u2500 Popup \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.popup {\n /* Shown/positioned entirely via JS \u2014 display starts as none */\n display: none;\n position: absolute;\n z-index: 500;\n min-width: 200px;\n max-width: 300px;\n background: var(--bv-color-neutral-900);\n color: var(--bv-color-neutral-0);\n border-radius: var(--bv-radius-md);\n padding: 10px 12px;\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-regular);\n line-height: 1.6;\n box-shadow: var(--bv-shadow-lg);\n pointer-events: none;\n white-space: normal;\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\u2500 */\n\n.text-content {\n display: none;\n}\n\n:host([text]) .text-content {\n display: block;\n}\n\n:host([text]) slot {\n display: none;\n}\n\n::slotted(p) {\n margin: 0 0 4px;\n}\n\n::slotted(p:last-child) {\n margin-bottom: 0;\n}\n";
6
6
 
7
- // components/bv-tooltip/bv-tooltip.ts
8
- var QUESTION_MARK_SVG = `<svg width="14" height="14" 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"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`;
9
- var _sheet = null;
10
- function getSheet() {
11
- if (!_sheet) {
12
- _sheet = new CSSStyleSheet();
13
- _sheet.replaceSync(bv_tooltip_default);
7
+ // components/_shared/adopt-styles.ts
8
+ var _sheetCache = /* @__PURE__ */ new Map();
9
+ function adoptStyles(shadow, css) {
10
+ if ("adoptedStyleSheets" in ShadowRoot.prototype && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
11
+ let sheet = _sheetCache.get(css);
12
+ if (!sheet) {
13
+ sheet = new CSSStyleSheet();
14
+ sheet.replaceSync(css);
15
+ _sheetCache.set(css, sheet);
16
+ }
17
+ shadow.adoptedStyleSheets = [sheet];
18
+ return;
14
19
  }
15
- return _sheet;
20
+ const style = document.createElement("style");
21
+ style.textContent = css;
22
+ shadow.appendChild(style);
16
23
  }
24
+
25
+ // components/bv-tooltip/bv-tooltip.ts
26
+ var QUESTION_MARK_SVG = `<svg width="14" height="14" 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"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`;
17
27
  var _uid = 0;
18
28
  var BvTooltip = class extends HTMLElement {
19
29
  static observedAttributes = ["text", "position"];
@@ -23,10 +33,7 @@ var BvTooltip = class extends HTMLElement {
23
33
  _popupEl = null;
24
34
  constructor() {
25
35
  super();
26
- const shadow = this.attachShadow({ mode: "open" });
27
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
28
- shadow.adoptedStyleSheets = [getSheet()];
29
- }
36
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_tooltip_default);
30
37
  }
31
38
  get text() {
32
39
  return this.getAttribute("text") ?? "";
@@ -53,11 +60,6 @@ var BvTooltip = class extends HTMLElement {
53
60
  if (this._rendered) return;
54
61
  this._rendered = true;
55
62
  const shadow = this.shadowRoot;
56
- if (!shadow.adoptedStyleSheets.length) {
57
- const s = document.createElement("style");
58
- s.textContent = bv_tooltip_default;
59
- shadow.appendChild(s);
60
- }
61
63
  const wrap = document.createElement("span");
62
64
  wrap.className = "wrap";
63
65
  const popupId = `bv-tooltip-popup-${++_uid}`;
@@ -98,6 +100,7 @@ var BvTooltip = class extends HTMLElement {
98
100
  popup.style.transform = "none";
99
101
  popup.style.display = "block";
100
102
  popup.style.visibility = "hidden";
103
+ void popup.offsetHeight;
101
104
  const tRect = trigger.getBoundingClientRect();
102
105
  const pRect = popup.getBoundingClientRect();
103
106
  const vw = window.innerWidth;
@@ -164,22 +167,70 @@ function injectTableStyles() {
164
167
  document.head.appendChild(style);
165
168
  }
166
169
  var BvTable = class extends HTMLElement {
167
- static observedAttributes = ["striped", "bordered"];
170
+ static observedAttributes = ["striped", "bordered", "loading", "loading-rows"];
168
171
  _isDragging = false;
169
172
  _dragStartX = 0;
170
173
  _dragScrollLeft = 0;
171
174
  _boundMouseDown = (e) => this._onMouseDown(e);
172
175
  _boundMouseMove = (e) => this._onMouseMove(e);
173
176
  _boundMouseUp = () => this._onMouseUp();
177
+ get loading() {
178
+ return this.hasAttribute("loading");
179
+ }
180
+ set loading(v) {
181
+ if (v) this.setAttribute("loading", "");
182
+ else this.removeAttribute("loading");
183
+ }
174
184
  connectedCallback() {
175
185
  injectTableStyles();
176
186
  this._enhanceHeaders();
177
187
  this._enhanceBodyCells();
178
188
  this._setupDrag();
189
+ if (this.hasAttribute("loading")) this._renderSkeleton();
179
190
  }
180
191
  disconnectedCallback() {
181
192
  this._teardownDrag();
182
193
  }
194
+ attributeChangedCallback(name, _old, val) {
195
+ if (!this.isConnected) return;
196
+ if (name === "loading") {
197
+ if (val === null) this._removeSkeleton();
198
+ else this._renderSkeleton();
199
+ }
200
+ if (name === "loading-rows" && this.hasAttribute("loading")) {
201
+ this._removeSkeleton();
202
+ this._renderSkeleton();
203
+ }
204
+ }
205
+ // Renders skeleton rows as a light-DOM sibling to the user's <table>.
206
+ // The `loading` attribute on the host + CSS in bv-table.css handles the
207
+ // "hide the real table" side of the swap.
208
+ _renderSkeleton() {
209
+ if (this.querySelector(":scope > .bv-table-skeleton")) return;
210
+ const rowsAttr = this.getAttribute("loading-rows");
211
+ const rows = Math.max(1, Number(rowsAttr) || 10);
212
+ const columns = Math.max(1, this.querySelectorAll("thead th").length || 4);
213
+ const skeleton = document.createElement("div");
214
+ skeleton.className = "bv-table-skeleton";
215
+ skeleton.setAttribute("role", "status");
216
+ skeleton.setAttribute("aria-live", "polite");
217
+ skeleton.setAttribute("aria-label", this.getAttribute("loading-label") ?? "Loading");
218
+ for (let r = 0; r < rows; r++) {
219
+ const row = document.createElement("div");
220
+ row.className = "bv-table-skeleton-row";
221
+ for (let c = 0; c < columns; c++) {
222
+ const bar = document.createElement("div");
223
+ bar.className = "bv-table-skeleton-bar";
224
+ row.appendChild(bar);
225
+ }
226
+ skeleton.appendChild(row);
227
+ }
228
+ this.appendChild(skeleton);
229
+ }
230
+ _removeSkeleton() {
231
+ var _a;
232
+ (_a = this.querySelector(":scope > .bv-table-skeleton")) == null ? void 0 : _a.remove();
233
+ }
183
234
  _setupDrag() {
184
235
  this.addEventListener("mousedown", this._boundMouseDown);
185
236
  window.addEventListener("mousemove", this._boundMouseMove);