@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
@@ -1,17 +1,76 @@
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
+ const shadow = host.shadowRoot;
25
+ if (!shadow) {
26
+ throw new Error("bindDropdownController requires the host to have an open shadow root.");
27
+ }
28
+ const syncExpanded = () => {
29
+ var _a;
30
+ (_a = options.trigger) == null ? void 0 : _a.setAttribute("aria-expanded", host.hasAttribute("open") ? "true" : "false");
31
+ };
32
+ const open = () => {
33
+ var _a;
34
+ if (host.hasAttribute("open")) return;
35
+ (_a = options.onBeforeOpen) == null ? void 0 : _a.call(options);
36
+ host.setAttribute("open", "");
37
+ syncExpanded();
38
+ host.dispatchEvent(new CustomEvent("bv-open", { bubbles: true, composed: true }));
39
+ };
40
+ const close = () => {
41
+ if (!host.hasAttribute("open")) return;
42
+ host.removeAttribute("open");
43
+ syncExpanded();
44
+ };
45
+ const toggle = () => {
46
+ if (host.hasAttribute("open")) close();
47
+ else open();
48
+ };
49
+ const offClick = (e) => {
50
+ if (!host.hasAttribute("open")) return;
51
+ const target = e.target;
52
+ if (host.contains(target) || shadow.contains(target)) return;
53
+ close();
54
+ };
55
+ document.addEventListener("click", offClick);
56
+ const onKeyDown = (e) => {
57
+ if (e.key === "Escape") close();
58
+ };
59
+ host.addEventListener("keydown", onKeyDown);
60
+ const observer = new MutationObserver(syncExpanded);
61
+ observer.observe(host, { attributes: true, attributeFilter: ["open"] });
62
+ const cleanup = () => {
63
+ observer.disconnect();
64
+ document.removeEventListener("click", offClick);
65
+ host.removeEventListener("keydown", onKeyDown);
66
+ };
67
+ syncExpanded();
68
+ return { open, close, toggle, cleanup };
69
+ }
70
+
4
71
  // components/bv-select/bv-select.ts
5
72
  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
73
  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
74
  var BvSelect = class extends HTMLElement {
16
75
  static observedAttributes = [
17
76
  "label",
@@ -30,13 +89,10 @@ var BvSelect = class extends HTMLElement {
30
89
  _errorEl = null;
31
90
  _options = [];
32
91
  _value = "";
33
- _offClick = null;
92
+ _dropdown = null;
34
93
  constructor() {
35
94
  super();
36
- const shadow = this.attachShadow({ mode: "open" });
37
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
38
- shadow.adoptedStyleSheets = [getSheet()];
39
- }
95
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_select_default);
40
96
  }
41
97
  get value() {
42
98
  return this._value;
@@ -62,7 +118,8 @@ var BvSelect = class extends HTMLElement {
62
118
  this._render();
63
119
  }
64
120
  disconnectedCallback() {
65
- if (this._offClick) document.removeEventListener("click", this._offClick);
121
+ var _a;
122
+ (_a = this._dropdown) == null ? void 0 : _a.cleanup();
66
123
  }
67
124
  attributeChangedCallback(name, _old, val) {
68
125
  if (name === "options") {
@@ -96,11 +153,6 @@ var BvSelect = class extends HTMLElement {
96
153
  }
97
154
  this._value = this.getAttribute("value") ?? "";
98
155
  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
156
  const label = document.createElement("label");
105
157
  label.className = "field-label";
106
158
  this._labelEl = label;
@@ -119,8 +171,9 @@ var BvSelect = class extends HTMLElement {
119
171
  chevron.innerHTML = CHEVRON_DOWN;
120
172
  trigger.append(triggerLabel, chevron);
121
173
  trigger.addEventListener("click", (e) => {
174
+ var _a;
122
175
  e.stopPropagation();
123
- if (!this.hasAttribute("disabled")) this._toggle();
176
+ if (!this.hasAttribute("disabled")) (_a = this._dropdown) == null ? void 0 : _a.toggle();
124
177
  });
125
178
  const panel = document.createElement("div");
126
179
  panel.className = "panel";
@@ -136,29 +189,7 @@ var BvSelect = class extends HTMLElement {
136
189
  this._renderOptions();
137
190
  this._syncError();
138
191
  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");
192
+ this._dropdown = bindDropdownController(this, { trigger });
162
193
  }
163
194
  _syncLabel() {
164
195
  if (!this._labelEl) return;
@@ -218,11 +249,12 @@ var BvSelect = class extends HTMLElement {
218
249
  this._triggerEl.disabled = this.hasAttribute("disabled");
219
250
  }
220
251
  _select(opt) {
252
+ var _a;
221
253
  this._value = opt.value;
222
254
  this.setAttribute("value", opt.value);
223
255
  this._syncTriggerLabel();
224
256
  this._syncSelected();
225
- this._close();
257
+ (_a = this._dropdown) == null ? void 0 : _a.close();
226
258
  this.dispatchEvent(
227
259
  new CustomEvent("bv-change", {
228
260
  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,73 @@
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
+ const shadow = host.shadowRoot;
25
+ if (!shadow) {
26
+ throw new Error("bindDropdownController requires the host to have an open shadow root.");
27
+ }
28
+ const syncExpanded = () => {
29
+ var _a;
30
+ (_a = options.trigger) == null ? void 0 : _a.setAttribute("aria-expanded", host.hasAttribute("open") ? "true" : "false");
31
+ };
32
+ const open = () => {
33
+ var _a;
34
+ if (host.hasAttribute("open")) return;
35
+ (_a = options.onBeforeOpen) == null ? void 0 : _a.call(options);
36
+ host.setAttribute("open", "");
37
+ syncExpanded();
38
+ host.dispatchEvent(new CustomEvent("bv-open", { bubbles: true, composed: true }));
39
+ };
40
+ const close = () => {
41
+ if (!host.hasAttribute("open")) return;
42
+ host.removeAttribute("open");
43
+ syncExpanded();
44
+ };
45
+ const toggle = () => {
46
+ if (host.hasAttribute("open")) close();
47
+ else open();
48
+ };
49
+ const offClick = (e) => {
50
+ if (!host.hasAttribute("open")) return;
51
+ const target = e.target;
52
+ if (host.contains(target) || shadow.contains(target)) return;
53
+ close();
54
+ };
55
+ document.addEventListener("click", offClick);
56
+ const onKeyDown = (e) => {
57
+ if (e.key === "Escape") close();
58
+ };
59
+ host.addEventListener("keydown", onKeyDown);
60
+ const observer = new MutationObserver(syncExpanded);
61
+ observer.observe(host, { attributes: true, attributeFilter: ["open"] });
62
+ const cleanup = () => {
63
+ observer.disconnect();
64
+ document.removeEventListener("click", offClick);
65
+ host.removeEventListener("keydown", onKeyDown);
66
+ };
67
+ syncExpanded();
68
+ return { open, close, toggle, cleanup };
69
+ }
70
+
4
71
  // components/bv-icon/icons.ts
5
72
  var icons = {
6
73
  // Navigation
@@ -76,14 +143,6 @@ var rawIcons = {
76
143
 
77
144
  // components/bv-split-button/bv-split-button.ts
78
145
  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
146
  var BvSplitButton = class extends HTMLElement {
88
147
  static observedAttributes = ["label", "variant", "disabled", "options", "open"];
89
148
  _rendered = false;
@@ -91,13 +150,10 @@ var BvSplitButton = class extends HTMLElement {
91
150
  _toggleBtn = null;
92
151
  _labelEl = null;
93
152
  _panelEl = null;
94
- _offClick = null;
153
+ _dropdown = null;
95
154
  constructor() {
96
155
  super();
97
- const shadow = this.attachShadow({ mode: "open" });
98
- if ("adoptedStyleSheets" in ShadowRoot.prototype) {
99
- shadow.adoptedStyleSheets = [getSheet()];
100
- }
156
+ adoptStyles(this.attachShadow({ mode: "open" }), bv_split_button_default);
101
157
  }
102
158
  get label() {
103
159
  return this.getAttribute("label") ?? "";
@@ -132,7 +188,8 @@ var BvSplitButton = class extends HTMLElement {
132
188
  this._render();
133
189
  }
134
190
  disconnectedCallback() {
135
- if (this._offClick) document.removeEventListener("click", this._offClick);
191
+ var _a;
192
+ (_a = this._dropdown) == null ? void 0 : _a.cleanup();
136
193
  }
137
194
  attributeChangedCallback(name, _old, _val) {
138
195
  if (!this._rendered) return;
@@ -144,11 +201,6 @@ var BvSplitButton = class extends HTMLElement {
144
201
  if (this._rendered) return;
145
202
  this._rendered = true;
146
203
  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
204
  const wrap = document.createElement("div");
153
205
  wrap.className = "wrap";
154
206
  const mainBtn = document.createElement("button");
@@ -176,8 +228,9 @@ var BvSplitButton = class extends HTMLElement {
176
228
  toggleBtn.setAttribute("aria-label", "More options");
177
229
  toggleBtn.innerHTML = CHEVRON_DOWN;
178
230
  toggleBtn.addEventListener("click", (e) => {
231
+ var _a;
179
232
  e.stopPropagation();
180
- if (!this.disabled) this._toggle();
233
+ if (!this.disabled) (_a = this._dropdown) == null ? void 0 : _a.toggle();
181
234
  });
182
235
  this._toggleBtn = toggleBtn;
183
236
  wrap.append(mainBtn, divider, toggleBtn);
@@ -188,27 +241,7 @@ var BvSplitButton = class extends HTMLElement {
188
241
  shadow.append(wrap, panel);
189
242
  this._syncDisabled();
190
243
  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");
244
+ this._dropdown = bindDropdownController(this, { trigger: toggleBtn });
212
245
  }
213
246
  _syncDisabled() {
214
247
  const d = this.disabled;
@@ -243,7 +276,8 @@ var BvSplitButton = class extends HTMLElement {
243
276
  }
244
277
  btn.appendChild(content);
245
278
  btn.addEventListener("click", () => {
246
- this._close();
279
+ var _a;
280
+ (_a = this._dropdown) == null ? void 0 : _a.close();
247
281
  this.dispatchEvent(
248
282
  new CustomEvent("bv-option-select", {
249
283
  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}`;
@@ -164,22 +166,70 @@ function injectTableStyles() {
164
166
  document.head.appendChild(style);
165
167
  }
166
168
  var BvTable = class extends HTMLElement {
167
- static observedAttributes = ["striped", "bordered"];
169
+ static observedAttributes = ["striped", "bordered", "loading", "loading-rows"];
168
170
  _isDragging = false;
169
171
  _dragStartX = 0;
170
172
  _dragScrollLeft = 0;
171
173
  _boundMouseDown = (e) => this._onMouseDown(e);
172
174
  _boundMouseMove = (e) => this._onMouseMove(e);
173
175
  _boundMouseUp = () => this._onMouseUp();
176
+ get loading() {
177
+ return this.hasAttribute("loading");
178
+ }
179
+ set loading(v) {
180
+ if (v) this.setAttribute("loading", "");
181
+ else this.removeAttribute("loading");
182
+ }
174
183
  connectedCallback() {
175
184
  injectTableStyles();
176
185
  this._enhanceHeaders();
177
186
  this._enhanceBodyCells();
178
187
  this._setupDrag();
188
+ if (this.hasAttribute("loading")) this._renderSkeleton();
179
189
  }
180
190
  disconnectedCallback() {
181
191
  this._teardownDrag();
182
192
  }
193
+ attributeChangedCallback(name, _old, val) {
194
+ if (!this.isConnected) return;
195
+ if (name === "loading") {
196
+ if (val === null) this._removeSkeleton();
197
+ else this._renderSkeleton();
198
+ }
199
+ if (name === "loading-rows" && this.hasAttribute("loading")) {
200
+ this._removeSkeleton();
201
+ this._renderSkeleton();
202
+ }
203
+ }
204
+ // Renders skeleton rows as a light-DOM sibling to the user's <table>.
205
+ // The `loading` attribute on the host + CSS in bv-table.css handles the
206
+ // "hide the real table" side of the swap.
207
+ _renderSkeleton() {
208
+ if (this.querySelector(":scope > .bv-table-skeleton")) return;
209
+ const rowsAttr = this.getAttribute("loading-rows");
210
+ const rows = Math.max(1, Number(rowsAttr) || 10);
211
+ const columns = Math.max(1, this.querySelectorAll("thead th").length || 4);
212
+ const skeleton = document.createElement("div");
213
+ skeleton.className = "bv-table-skeleton";
214
+ skeleton.setAttribute("role", "status");
215
+ skeleton.setAttribute("aria-live", "polite");
216
+ skeleton.setAttribute("aria-label", this.getAttribute("loading-label") ?? "Loading");
217
+ for (let r = 0; r < rows; r++) {
218
+ const row = document.createElement("div");
219
+ row.className = "bv-table-skeleton-row";
220
+ for (let c = 0; c < columns; c++) {
221
+ const bar = document.createElement("div");
222
+ bar.className = "bv-table-skeleton-bar";
223
+ row.appendChild(bar);
224
+ }
225
+ skeleton.appendChild(row);
226
+ }
227
+ this.appendChild(skeleton);
228
+ }
229
+ _removeSkeleton() {
230
+ var _a;
231
+ (_a = this.querySelector(":scope > .bv-table-skeleton")) == null ? void 0 : _a.remove();
232
+ }
183
233
  _setupDrag() {
184
234
  this.addEventListener("mousedown", this._boundMouseDown);
185
235
  window.addEventListener("mousemove", this._boundMouseMove);