@bluevolt-tech/lumen 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/bv-course-grid-card/bv-course-grid-card.d.ts +4 -0
- package/dist/components/bv-course-grid-card/bv-course-grid-card.js +74 -17
- package/dist/components/bv-course-list-card/bv-course-list-card.d.ts +4 -0
- package/dist/components/bv-course-list-card/bv-course-list-card.js +74 -17
- package/dist/themes/base.css +2 -1
- package/package.json +1 -1
- package/themes/base.css +2 -1
|
@@ -19,5 +19,9 @@ export declare class BvCourseGridCard extends HTMLElement {
|
|
|
19
19
|
connectedCallback(): void;
|
|
20
20
|
attributeChangedCallback(name: string, _old: string | null, val: string | null): void;
|
|
21
21
|
private _render;
|
|
22
|
+
private _toggleSelected;
|
|
23
|
+
private _buildAriaLabel;
|
|
24
|
+
private _setPrefixedStrong;
|
|
25
|
+
private _syncSelectionA11y;
|
|
22
26
|
private _syncLoading;
|
|
23
27
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-course-grid-card/bv-course-grid-card.css
|
|
2
|
-
var bv_course_grid_card_default = ":host {\n display: block;\n}\n\n.card {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n padding: 12px;\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-xl);\n box-shadow: var(--bv-shadow-sm);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n:host([selected]) .card {\n background: var(--bv-color-primary-50);\n border-color: var(--bv-color-primary);\n box-shadow: none;\n}\n\n/* Thumbnail */\n.thumbnail-wrap {\n position: relative;\n width: 100%;\n height: 153px;\n border-radius: var(--bv-radius-lg);\n overflow: hidden;\n background: var(--bv-color-neutral-200);\n flex-shrink: 0;\n}\n\n.thumbnail {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n\n.status-badge {\n position: absolute;\n top: 6px;\n right: 6px;\n padding: 0 var(--bv-spacing-sm);\n border-radius: var(--bv-radius-sm);\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-medium);\n line-height: 18px;\n background: var(--bv-color-success-light);\n color: var(--bv-color-success-dark);\n}\n\n/* Content */\n.content {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n min-width: 0;\n}\n\n.title {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-md);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-900);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.publisher {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-500);\n}\n\n.publisher strong {\n font-weight: var(--bv-font-weight-medium);\n color: var(--bv-color-neutral-900);\n}\n\n.description {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n line-height: 20px;\n min-height: 40px; /* Reserve space for 2 lines so cards align at the bottom in the grid, matching the list card's .description-wrap. */\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n margin: 0;\n}\n\n.info-row {\n display: flex;\n gap: var(--bv-spacing-md);\n margin-top: 2px;\n}\n\n.info-item {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-900);\n}\n\n.info-item strong {\n font-weight: var(--bv-font-weight-bold);\n}\n\n/* Actions */\n.actions {\n display: flex;\n gap: var(--bv-spacing-sm);\n align-items: stretch;\n}\n\n.primary-action {\n display: block;\n flex: 1;\n min-width: 0;\n}\n\n.secondary-action {\n display: block;\n width: 48px;\n flex-shrink: 0;\n}\n\n/* Disabled selection: card body is no longer interactive */\n:host([disable-selection]) .card {\n cursor: default;\n}\n\n/* Loading skeleton */\n.skeleton {\n display: none;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n flex: 1;\n min-width: 0;\n}\n\n:host([loading]) .card {\n cursor: default;\n}\n\n:host([loading]) .thumbnail-wrap,\n:host([loading]) .content,\n:host([loading]) .actions {\n display: none;\n}\n\n:host([loading]) .skeleton {\n display: flex;\n}\n\n.skeleton-thumb {\n width: 100%;\n height: 153px;\n border-radius: var(--bv-radius-lg);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-line {\n height: 12px;\n border-radius: var(--bv-radius-sm);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-title-line {\n height: 20px;\n width: 70%;\n}\n\n.skeleton-short-line {\n width: 45%;\n}\n\n.skeleton-actions {\n display: flex;\n gap: var(--bv-spacing-sm);\n align-items: stretch;\n margin-top: 2px;\n}\n\n.skeleton-button {\n height: 44px;\n border-radius: var(--bv-radius-md);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-primary {\n flex: 1;\n min-width: 0;\n}\n\n.skeleton-icon-btn {\n width: 48px;\n flex-shrink: 0;\n}\n\n@keyframes bv-skeleton-shimmer {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n";
|
|
2
|
+
var bv_course_grid_card_default = ":host {\n display: block;\n}\n\n.card {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n padding: 12px;\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-xl);\n box-shadow: var(--bv-shadow-sm);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n:host([selected]) .card {\n background: var(--bv-color-primary-50);\n border-color: var(--bv-color-primary);\n box-shadow: none;\n}\n\n.card:focus-visible {\n outline: var(--bv-focus-ring);\n outline-offset: 2px;\n}\n\n/* Thumbnail */\n.thumbnail-wrap {\n position: relative;\n width: 100%;\n height: 153px;\n border-radius: var(--bv-radius-lg);\n overflow: hidden;\n background: var(--bv-color-neutral-200);\n flex-shrink: 0;\n}\n\n.thumbnail {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n\n.status-badge {\n position: absolute;\n top: 6px;\n right: 6px;\n padding: 0 var(--bv-spacing-sm);\n border-radius: var(--bv-radius-sm);\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-medium);\n line-height: 18px;\n background: var(--bv-color-success-light);\n color: var(--bv-color-success-dark);\n}\n\n/* Content */\n.content {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n min-width: 0;\n}\n\n.title {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-md);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-900);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.publisher {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-500);\n}\n\n.publisher strong {\n font-weight: var(--bv-font-weight-medium);\n color: var(--bv-color-neutral-900);\n}\n\n.description {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n line-height: 20px;\n min-height: 40px; /* Reserve space for 2 lines so cards align at the bottom in the grid, matching the list card's .description-wrap. */\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n margin: 0;\n}\n\n.info-row {\n display: flex;\n gap: var(--bv-spacing-md);\n margin-top: 2px;\n}\n\n.info-item {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-900);\n}\n\n.info-item strong {\n font-weight: var(--bv-font-weight-bold);\n}\n\n/* Actions */\n.actions {\n display: flex;\n gap: var(--bv-spacing-sm);\n align-items: stretch;\n}\n\n.primary-action {\n display: block;\n flex: 1;\n min-width: 0;\n}\n\n.secondary-action {\n display: block;\n width: 48px;\n flex-shrink: 0;\n}\n\n/* Disabled selection: card body is no longer interactive */\n:host([disable-selection]) .card {\n cursor: default;\n}\n\n/* Loading skeleton */\n.skeleton {\n display: none;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n flex: 1;\n min-width: 0;\n}\n\n:host([loading]) .card {\n cursor: default;\n}\n\n:host([loading]) .thumbnail-wrap,\n:host([loading]) .content,\n:host([loading]) .actions {\n display: none;\n}\n\n:host([loading]) .skeleton {\n display: flex;\n}\n\n.skeleton-thumb {\n width: 100%;\n height: 153px;\n border-radius: var(--bv-radius-lg);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-line {\n height: 12px;\n border-radius: var(--bv-radius-sm);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-title-line {\n height: 20px;\n width: 70%;\n}\n\n.skeleton-short-line {\n width: 45%;\n}\n\n.skeleton-actions {\n display: flex;\n gap: var(--bv-spacing-sm);\n align-items: stretch;\n margin-top: 2px;\n}\n\n.skeleton-button {\n height: 44px;\n border-radius: var(--bv-radius-md);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-primary {\n flex: 1;\n min-width: 0;\n}\n\n.skeleton-icon-btn {\n width: 48px;\n flex-shrink: 0;\n}\n\n@keyframes bv-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-button/bv-button.css
|
|
5
5
|
var bv_button_default = "/* \u2500\u2500 Public theming API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 Apps can override these three properties per-instance:\n\n HTML: <bv-button style=\"--bv-button-bg: #e53e3e;\">\n Angular: <bv-button [style.--bv-button-bg]=\"brandColor\">\n JavaScript: btn.style.setProperty('--bv-button-bg', value)\n\n Hover is auto-computed from the base colors via color-mix().\n Override --bv-button-bg-hover / --bv-button-border-color-hover\n if you need explicit control over the hovered state.\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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:host {\n /* Default values \u2014 map to global tokens (primary variant) */\n --bv-button-bg: var(--bv-color-primary);\n --bv-button-bg-hover: color-mix(in srgb, var(--bv-button-bg) 82%, black);\n --bv-button-color: var(--bv-color-neutral-0);\n --bv-button-border-color: var(--bv-color-primary);\n --bv-button-border-color-hover: color-mix(in srgb, var(--bv-button-border-color) 82%, black);\n --bv-button-font-family: var(--bv-font-family-base);\n --bv-button-font-size: var(--bv-font-size-sm);\n\n display: inline-block;\n font-family: var(--bv-font-family-base);\n}\n\n:host([hidden]) {\n /* `!important` so `hidden` always wins over :host([full-width]) / other display rules. */\n display: none !important;\n}\n\n:host([full-width]) {\n display: block;\n width: 100%;\n}\n\n/* \u2500\u2500 Variant tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Each variant re-points the component tokens to different global\n tokens. Apps can still override with inline styles \u2014 inline style\n always wins due to higher specificity.\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-border-color: var(--bv-color-secondary);\n --bv-button-color: var(--bv-color-secondary-foreground);\n}\n\n:host([variant='tertiary']) {\n --bv-button-bg: var(--bv-color-tertiary);\n --bv-button-border-color: var(--bv-color-tertiary);\n}\n\n:host([variant='danger']) {\n --bv-button-bg: var(--bv-color-danger);\n --bv-button-border-color: var(--bv-color-danger);\n}\n\n:host([variant='ghost']) {\n --bv-button-bg: transparent;\n --bv-button-bg-hover: var(--bv-color-primary-light);\n --bv-button-color: var(--bv-color-primary);\n --bv-button-border-color: transparent;\n --bv-button-border-color-hover: transparent;\n}\n\n:host([variant='outline']) {\n --bv-button-bg: transparent;\n --bv-button-bg-hover: var(--bv-color-primary-light);\n --bv-button-color: var(--bv-color-primary);\n --bv-button-border-color: var(--bv-color-primary);\n --bv-button-border-color-hover: 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 --bv-button-border-color-hover: var(--bv-color-neutral-300);\n}\n\n/* \u2500\u2500 Base button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n All visual properties read from the component-level tokens above.\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--bv-spacing-xs);\n padding: 10px var(--bv-spacing-md);\n min-height: 40px;\n width: auto;\n font-family: var(--bv-button-font-family);\n font-size: var(--bv-button-font-size);\n font-style: normal;\n font-weight: var(--bv-font-weight-semibold);\n line-height: 20px;\n border: 1px solid var(--bv-button-border-color);\n border-radius: var(--bv-radius-lg);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n color var(--bv-transition-fast),\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast),\n opacity var(--bv-transition-fast);\n white-space: nowrap;\n user-select: none;\n text-decoration: none;\n outline: none;\n box-sizing: border-box;\n background: var(--bv-button-bg);\n color: var(--bv-button-color);\n box-shadow: var(--bv-shadow-sm);\n}\n\n:host([full-width]) .btn {\n width: 100%;\n}\n\n.btn:hover:not(:disabled) {\n background: var(--bv-button-bg-hover);\n border-color: var(--bv-button-border-color-hover);\n}\n\n.btn:active:not(:disabled) {\n transform: translateY(1px);\n}\n\n.btn:focus-visible {\n outline: 2px solid var(--bv-button-border-color);\n outline-offset: 2px;\n}\n\n.btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Sizes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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([size='sm']) .btn {\n padding: 6px 12px;\n min-height: 32px;\n font-size: var(--bv-font-size-sm);\n}\n\n:host([size='lg']) .btn {\n padding: 12px var(--bv-spacing-lg);\n min-height: 48px;\n font-size: var(--bv-font-size-md);\n}\n\n/* \u2500\u2500 Icon \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500*/\n\n.btn-icon {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n line-height: 0;\n}\n\n/* \u2500\u2500 Count badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.btn-count {\n opacity: 0.8;\n font-weight: var(--bv-font-weight-regular);\n}\n\n/* \u2500\u2500 Loading spinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.spinner {\n display: inline-block;\n width: 1em;\n height: 1em;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n flex-shrink: 0;\n animation: bv-spin 700ms linear infinite;\n}\n\n@keyframes bv-spin {\n to {\n transform: rotate(360deg);\n }\n}\n";
|
|
@@ -311,20 +311,23 @@ var BvCourseGridCard = class extends HTMLElement {
|
|
|
311
311
|
}
|
|
312
312
|
},
|
|
313
313
|
title: () => {
|
|
314
|
+
var _a2;
|
|
314
315
|
if (this._els.title) this._els.title.textContent = val ?? "";
|
|
316
|
+
(_a2 = this._els.card) == null ? void 0 : _a2.setAttribute("aria-label", this._buildAriaLabel());
|
|
315
317
|
},
|
|
316
318
|
publisher: () => {
|
|
317
|
-
|
|
319
|
+
var _a2;
|
|
320
|
+
this._setPrefixedStrong(this._els.publisher, "By ", val);
|
|
321
|
+
(_a2 = this._els.card) == null ? void 0 : _a2.setAttribute("aria-label", this._buildAriaLabel());
|
|
318
322
|
},
|
|
319
323
|
description: () => {
|
|
320
324
|
if (this._els.description) this._els.description.textContent = val ?? "";
|
|
321
325
|
},
|
|
322
326
|
duration: () => {
|
|
323
|
-
|
|
327
|
+
this._setPrefixedStrong(this._els.duration, "", val);
|
|
324
328
|
},
|
|
325
329
|
"last-updated": () => {
|
|
326
|
-
|
|
327
|
-
this._els.lastUpdated.innerHTML = `Last Updated: <strong>${val ?? ""}</strong>`;
|
|
330
|
+
this._setPrefixedStrong(this._els.lastUpdated, "Last Updated: ", val);
|
|
328
331
|
},
|
|
329
332
|
"primary-label": () => {
|
|
330
333
|
if (this._els.primaryBtn) this._els.primaryBtn.textContent = val ?? "";
|
|
@@ -354,8 +357,16 @@ var BvCourseGridCard = class extends HTMLElement {
|
|
|
354
357
|
"hide-secondary": () => {
|
|
355
358
|
if (this._els.secondaryBtn) this._els.secondaryBtn.hidden = this.hideSecondary;
|
|
356
359
|
},
|
|
360
|
+
selected: () => {
|
|
361
|
+
var _a2;
|
|
362
|
+
(_a2 = this._els.card) == null ? void 0 : _a2.setAttribute("aria-checked", this.selected ? "true" : "false");
|
|
363
|
+
},
|
|
364
|
+
"disable-selection": () => {
|
|
365
|
+
this._syncSelectionA11y();
|
|
366
|
+
},
|
|
357
367
|
loading: () => {
|
|
358
368
|
this._syncLoading();
|
|
369
|
+
this._syncSelectionA11y();
|
|
359
370
|
}
|
|
360
371
|
};
|
|
361
372
|
(_a = map[name]) == null ? void 0 : _a.call(map);
|
|
@@ -371,6 +382,10 @@ var BvCourseGridCard = class extends HTMLElement {
|
|
|
371
382
|
}
|
|
372
383
|
const card = document.createElement("div");
|
|
373
384
|
card.className = "card";
|
|
385
|
+
card.setAttribute("role", "checkbox");
|
|
386
|
+
card.setAttribute("tabindex", "0");
|
|
387
|
+
card.setAttribute("aria-checked", this.selected ? "true" : "false");
|
|
388
|
+
card.setAttribute("aria-label", this._buildAriaLabel());
|
|
374
389
|
this._els.card = card;
|
|
375
390
|
const thumbWrap = document.createElement("div");
|
|
376
391
|
thumbWrap.className = "thumbnail-wrap";
|
|
@@ -394,8 +409,8 @@ var BvCourseGridCard = class extends HTMLElement {
|
|
|
394
409
|
this._els.title = title;
|
|
395
410
|
const publisher = document.createElement("span");
|
|
396
411
|
publisher.className = "publisher";
|
|
397
|
-
publisher.innerHTML = `By <strong>${this.getAttribute("publisher") ?? ""}</strong>`;
|
|
398
412
|
this._els.publisher = publisher;
|
|
413
|
+
this._setPrefixedStrong(publisher, "By ", this.getAttribute("publisher"));
|
|
399
414
|
const description = document.createElement("p");
|
|
400
415
|
description.className = "description";
|
|
401
416
|
description.textContent = this.getAttribute("description") ?? "";
|
|
@@ -404,12 +419,12 @@ var BvCourseGridCard = class extends HTMLElement {
|
|
|
404
419
|
infoRow.className = "info-row";
|
|
405
420
|
const duration = document.createElement("span");
|
|
406
421
|
duration.className = "info-item";
|
|
407
|
-
duration.innerHTML = `<strong>${this.getAttribute("duration") ?? ""}</strong>`;
|
|
408
422
|
this._els.duration = duration;
|
|
423
|
+
this._setPrefixedStrong(duration, "", this.getAttribute("duration"));
|
|
409
424
|
const lastUpdated = document.createElement("span");
|
|
410
425
|
lastUpdated.className = "info-item";
|
|
411
|
-
lastUpdated.innerHTML = `Last Updated: <strong>${this.getAttribute("last-updated") ?? ""}</strong>`;
|
|
412
426
|
this._els.lastUpdated = lastUpdated;
|
|
427
|
+
this._setPrefixedStrong(lastUpdated, "Last Updated: ", this.getAttribute("last-updated"));
|
|
413
428
|
infoRow.append(duration, lastUpdated);
|
|
414
429
|
content.append(title, publisher, description, infoRow);
|
|
415
430
|
const actions = document.createElement("div");
|
|
@@ -449,18 +464,60 @@ var BvCourseGridCard = class extends HTMLElement {
|
|
|
449
464
|
card.append(thumbWrap, content, actions, skeleton);
|
|
450
465
|
shadow.appendChild(card);
|
|
451
466
|
this._syncLoading();
|
|
467
|
+
this._syncSelectionA11y();
|
|
452
468
|
card.addEventListener("click", (e) => {
|
|
453
469
|
if (this.disableSelection || this.loading) return;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
this.
|
|
457
|
-
new CustomEvent("bv-select", {
|
|
458
|
-
bubbles: true,
|
|
459
|
-
composed: true,
|
|
460
|
-
detail: { selected: this.selected }
|
|
461
|
-
})
|
|
462
|
-
);
|
|
470
|
+
const actionsEl = this._els.actions;
|
|
471
|
+
if (actionsEl && e.composedPath().includes(actionsEl)) return;
|
|
472
|
+
this._toggleSelected();
|
|
463
473
|
});
|
|
474
|
+
card.addEventListener("keydown", (e) => {
|
|
475
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
476
|
+
if (this.disableSelection || this.loading) return;
|
|
477
|
+
const actionsEl = this._els.actions;
|
|
478
|
+
if (actionsEl && e.composedPath().includes(actionsEl)) return;
|
|
479
|
+
e.preventDefault();
|
|
480
|
+
this._toggleSelected();
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
_toggleSelected() {
|
|
484
|
+
this.selected = !this.selected;
|
|
485
|
+
this.dispatchEvent(
|
|
486
|
+
new CustomEvent("bv-select", {
|
|
487
|
+
bubbles: true,
|
|
488
|
+
composed: true,
|
|
489
|
+
detail: { selected: this.selected }
|
|
490
|
+
})
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
_buildAriaLabel() {
|
|
494
|
+
const title = this.getAttribute("title") ?? "";
|
|
495
|
+
const publisher = this.getAttribute("publisher") ?? "";
|
|
496
|
+
const parts = [];
|
|
497
|
+
if (title) parts.push(title);
|
|
498
|
+
if (publisher) parts.push(`by ${publisher}`);
|
|
499
|
+
return parts.join(", ");
|
|
500
|
+
}
|
|
501
|
+
_setPrefixedStrong(el, prefix, val) {
|
|
502
|
+
if (!el) return;
|
|
503
|
+
const value = val ?? "";
|
|
504
|
+
if (!value) {
|
|
505
|
+
el.hidden = true;
|
|
506
|
+
el.replaceChildren();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
el.hidden = false;
|
|
510
|
+
const strong = document.createElement("strong");
|
|
511
|
+
strong.textContent = value;
|
|
512
|
+
if (prefix) el.replaceChildren(document.createTextNode(prefix), strong);
|
|
513
|
+
else el.replaceChildren(strong);
|
|
514
|
+
}
|
|
515
|
+
_syncSelectionA11y() {
|
|
516
|
+
var _a, _b, _c;
|
|
517
|
+
const inert = this.disableSelection || this.loading;
|
|
518
|
+
(_a = this._els.card) == null ? void 0 : _a.setAttribute("tabindex", inert ? "-1" : "0");
|
|
519
|
+
if (inert) (_b = this._els.card) == null ? void 0 : _b.setAttribute("aria-disabled", "true");
|
|
520
|
+
else (_c = this._els.card) == null ? void 0 : _c.removeAttribute("aria-disabled");
|
|
464
521
|
}
|
|
465
522
|
_syncLoading() {
|
|
466
523
|
var _a;
|
|
@@ -19,5 +19,9 @@ export declare class BvCourseListCard extends HTMLElement {
|
|
|
19
19
|
connectedCallback(): void;
|
|
20
20
|
attributeChangedCallback(name: string, _old: string | null, val: string | null): void;
|
|
21
21
|
private _render;
|
|
22
|
+
private _toggleSelected;
|
|
23
|
+
private _buildAriaLabel;
|
|
24
|
+
private _setPrefixedStrong;
|
|
25
|
+
private _syncSelectionA11y;
|
|
22
26
|
private _syncLoading;
|
|
23
27
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// css-inline:/home/runner/work/BV_lumen/BV_lumen/components/bv-course-list-card/bv-course-list-card.css
|
|
2
|
-
var bv_course_list_card_default = ":host {\n display: block;\n}\n\n.card {\n display: flex;\n align-items: stretch;\n gap: var(--bv-spacing-md);\n padding: 12px;\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-xl);\n box-shadow: var(--bv-shadow-sm);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n:host([selected]) .card {\n background: var(--bv-color-primary-50);\n border-color: var(--bv-color-primary);\n box-shadow: none;\n}\n\n/* Thumbnail */\n.thumbnail-wrap {\n position: relative;\n flex-shrink: 0;\n width: 200px;\n min-height: 123px;\n border-radius: var(--bv-radius-lg);\n overflow: hidden;\n background: var(--bv-color-neutral-200);\n}\n\n.thumbnail {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n\n.status-badge {\n position: absolute;\n top: 6px;\n right: 6px;\n padding: 0 var(--bv-spacing-sm);\n border-radius: var(--bv-radius-sm);\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-medium);\n line-height: 18px;\n background: var(--bv-color-success-light);\n color: var(--bv-color-success-dark);\n}\n\n/* Content */\n.content {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n justify-content: space-between;\n}\n\n.meta {\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n\n.title {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-md);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-900);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.publisher {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-500);\n}\n\n.publisher strong {\n font-weight: var(--bv-font-weight-medium);\n color: var(--bv-color-neutral-900);\n}\n\n.description-wrap {\n height: 40px;\n display: flex;\n align-items: center;\n overflow: hidden;\n}\n\n.description {\n margin: 0;\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n line-height: 20px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.info-row {\n display: flex;\n gap: var(--bv-spacing-md);\n}\n\n.info-item {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-900);\n}\n\n.info-item strong {\n font-weight: var(--bv-font-weight-bold);\n}\n\n/* Actions */\n.actions {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n flex-shrink: 0;\n width: 209px;\n justify-content: center;\n}\n\n/* Disabled selection: card body is no longer interactive */\n:host([disable-selection]) .card {\n cursor: default;\n}\n\n/* Loading skeleton */\n.skeleton {\n display: none;\n align-items: stretch;\n gap: var(--bv-spacing-md);\n flex: 1;\n min-width: 0;\n}\n\n:host([loading]) .card {\n cursor: default;\n}\n\n:host([loading]) .thumbnail-wrap,\n:host([loading]) .content,\n:host([loading]) .actions {\n display: none;\n}\n\n:host([loading]) .skeleton {\n display: flex;\n}\n\n.skeleton-thumb {\n flex-shrink: 0;\n width: 200px;\n min-height: 123px;\n border-radius: var(--bv-radius-lg);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-content {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n flex: 1;\n min-width: 0;\n justify-content: center;\n}\n\n.skeleton-line {\n height: 12px;\n border-radius: var(--bv-radius-sm);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-title-line {\n height: 20px;\n width: 60%;\n}\n\n.skeleton-short-line {\n width: 40%;\n}\n\n.skeleton-actions {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n flex-shrink: 0;\n width: 209px;\n justify-content: center;\n}\n\n.skeleton-button {\n height: 40px;\n border-radius: var(--bv-radius-md);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n@keyframes bv-skeleton-shimmer {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n";
|
|
2
|
+
var bv_course_list_card_default = ":host {\n display: block;\n}\n\n.card {\n display: flex;\n align-items: stretch;\n gap: var(--bv-spacing-md);\n padding: 12px;\n background: var(--bv-color-neutral-0);\n border: 1px solid var(--bv-color-neutral-300);\n border-radius: var(--bv-radius-xl);\n box-shadow: var(--bv-shadow-sm);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast);\n}\n\n:host([selected]) .card {\n background: var(--bv-color-primary-50);\n border-color: var(--bv-color-primary);\n box-shadow: none;\n}\n\n.card:focus-visible {\n outline: var(--bv-focus-ring);\n outline-offset: 2px;\n}\n\n/* Thumbnail */\n.thumbnail-wrap {\n position: relative;\n flex-shrink: 0;\n width: 200px;\n min-height: 123px;\n border-radius: var(--bv-radius-lg);\n overflow: hidden;\n background: var(--bv-color-neutral-200);\n}\n\n.thumbnail {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n\n.status-badge {\n position: absolute;\n top: 6px;\n right: 6px;\n padding: 0 var(--bv-spacing-sm);\n border-radius: var(--bv-radius-sm);\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-medium);\n line-height: 18px;\n background: var(--bv-color-success-light);\n color: var(--bv-color-success-dark);\n}\n\n/* Content */\n.content {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n justify-content: space-between;\n}\n\n.meta {\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n\n.title {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-md);\n font-weight: var(--bv-font-weight-semibold);\n color: var(--bv-color-neutral-900);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.publisher {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-xs);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-500);\n}\n\n.publisher strong {\n font-weight: var(--bv-font-weight-medium);\n color: var(--bv-color-neutral-900);\n}\n\n.description-wrap {\n height: 40px;\n display: flex;\n align-items: center;\n overflow: hidden;\n}\n\n.description {\n margin: 0;\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-700);\n line-height: 20px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.info-row {\n display: flex;\n gap: var(--bv-spacing-md);\n}\n\n.info-item {\n font-family: var(--bv-font-family-base);\n font-size: var(--bv-font-size-sm);\n font-weight: var(--bv-font-weight-regular);\n color: var(--bv-color-neutral-900);\n}\n\n.info-item strong {\n font-weight: var(--bv-font-weight-bold);\n}\n\n/* Actions */\n.actions {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n flex-shrink: 0;\n width: 209px;\n justify-content: center;\n}\n\n/* Disabled selection: card body is no longer interactive */\n:host([disable-selection]) .card {\n cursor: default;\n}\n\n/* Loading skeleton */\n.skeleton {\n display: none;\n align-items: stretch;\n gap: var(--bv-spacing-md);\n flex: 1;\n min-width: 0;\n}\n\n:host([loading]) .card {\n cursor: default;\n}\n\n:host([loading]) .thumbnail-wrap,\n:host([loading]) .content,\n:host([loading]) .actions {\n display: none;\n}\n\n:host([loading]) .skeleton {\n display: flex;\n}\n\n.skeleton-thumb {\n flex-shrink: 0;\n width: 200px;\n min-height: 123px;\n border-radius: var(--bv-radius-lg);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-content {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n flex: 1;\n min-width: 0;\n justify-content: center;\n}\n\n.skeleton-line {\n height: 12px;\n border-radius: var(--bv-radius-sm);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-title-line {\n height: 20px;\n width: 60%;\n}\n\n.skeleton-short-line {\n width: 40%;\n}\n\n.skeleton-actions {\n display: flex;\n flex-direction: column;\n gap: var(--bv-spacing-sm);\n flex-shrink: 0;\n width: 209px;\n justify-content: center;\n}\n\n.skeleton-button {\n height: 40px;\n border-radius: var(--bv-radius-md);\n background: var(--bv-color-neutral-200);\n animation: bv-skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n@keyframes bv-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-button/bv-button.css
|
|
5
5
|
var bv_button_default = "/* \u2500\u2500 Public theming API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 Apps can override these three properties per-instance:\n\n HTML: <bv-button style=\"--bv-button-bg: #e53e3e;\">\n Angular: <bv-button [style.--bv-button-bg]=\"brandColor\">\n JavaScript: btn.style.setProperty('--bv-button-bg', value)\n\n Hover is auto-computed from the base colors via color-mix().\n Override --bv-button-bg-hover / --bv-button-border-color-hover\n if you need explicit control over the hovered state.\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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:host {\n /* Default values \u2014 map to global tokens (primary variant) */\n --bv-button-bg: var(--bv-color-primary);\n --bv-button-bg-hover: color-mix(in srgb, var(--bv-button-bg) 82%, black);\n --bv-button-color: var(--bv-color-neutral-0);\n --bv-button-border-color: var(--bv-color-primary);\n --bv-button-border-color-hover: color-mix(in srgb, var(--bv-button-border-color) 82%, black);\n --bv-button-font-family: var(--bv-font-family-base);\n --bv-button-font-size: var(--bv-font-size-sm);\n\n display: inline-block;\n font-family: var(--bv-font-family-base);\n}\n\n:host([hidden]) {\n /* `!important` so `hidden` always wins over :host([full-width]) / other display rules. */\n display: none !important;\n}\n\n:host([full-width]) {\n display: block;\n width: 100%;\n}\n\n/* \u2500\u2500 Variant tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Each variant re-points the component tokens to different global\n tokens. Apps can still override with inline styles \u2014 inline style\n always wins due to higher specificity.\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-border-color: var(--bv-color-secondary);\n --bv-button-color: var(--bv-color-secondary-foreground);\n}\n\n:host([variant='tertiary']) {\n --bv-button-bg: var(--bv-color-tertiary);\n --bv-button-border-color: var(--bv-color-tertiary);\n}\n\n:host([variant='danger']) {\n --bv-button-bg: var(--bv-color-danger);\n --bv-button-border-color: var(--bv-color-danger);\n}\n\n:host([variant='ghost']) {\n --bv-button-bg: transparent;\n --bv-button-bg-hover: var(--bv-color-primary-light);\n --bv-button-color: var(--bv-color-primary);\n --bv-button-border-color: transparent;\n --bv-button-border-color-hover: transparent;\n}\n\n:host([variant='outline']) {\n --bv-button-bg: transparent;\n --bv-button-bg-hover: var(--bv-color-primary-light);\n --bv-button-color: var(--bv-color-primary);\n --bv-button-border-color: var(--bv-color-primary);\n --bv-button-border-color-hover: 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 --bv-button-border-color-hover: var(--bv-color-neutral-300);\n}\n\n/* \u2500\u2500 Base button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n All visual properties read from the component-level tokens above.\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--bv-spacing-xs);\n padding: 10px var(--bv-spacing-md);\n min-height: 40px;\n width: auto;\n font-family: var(--bv-button-font-family);\n font-size: var(--bv-button-font-size);\n font-style: normal;\n font-weight: var(--bv-font-weight-semibold);\n line-height: 20px;\n border: 1px solid var(--bv-button-border-color);\n border-radius: var(--bv-radius-lg);\n cursor: pointer;\n transition:\n background var(--bv-transition-fast),\n color var(--bv-transition-fast),\n border-color var(--bv-transition-fast),\n box-shadow var(--bv-transition-fast),\n opacity var(--bv-transition-fast);\n white-space: nowrap;\n user-select: none;\n text-decoration: none;\n outline: none;\n box-sizing: border-box;\n background: var(--bv-button-bg);\n color: var(--bv-button-color);\n box-shadow: var(--bv-shadow-sm);\n}\n\n:host([full-width]) .btn {\n width: 100%;\n}\n\n.btn:hover:not(:disabled) {\n background: var(--bv-button-bg-hover);\n border-color: var(--bv-button-border-color-hover);\n}\n\n.btn:active:not(:disabled) {\n transform: translateY(1px);\n}\n\n.btn:focus-visible {\n outline: 2px solid var(--bv-button-border-color);\n outline-offset: 2px;\n}\n\n.btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Sizes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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([size='sm']) .btn {\n padding: 6px 12px;\n min-height: 32px;\n font-size: var(--bv-font-size-sm);\n}\n\n:host([size='lg']) .btn {\n padding: 12px var(--bv-spacing-lg);\n min-height: 48px;\n font-size: var(--bv-font-size-md);\n}\n\n/* \u2500\u2500 Icon \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500*/\n\n.btn-icon {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n line-height: 0;\n}\n\n/* \u2500\u2500 Count badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.btn-count {\n opacity: 0.8;\n font-weight: var(--bv-font-weight-regular);\n}\n\n/* \u2500\u2500 Loading spinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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.spinner {\n display: inline-block;\n width: 1em;\n height: 1em;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n flex-shrink: 0;\n animation: bv-spin 700ms linear infinite;\n}\n\n@keyframes bv-spin {\n to {\n transform: rotate(360deg);\n }\n}\n";
|
|
@@ -312,20 +312,23 @@ var BvCourseListCard = class extends HTMLElement {
|
|
|
312
312
|
}
|
|
313
313
|
},
|
|
314
314
|
title: () => {
|
|
315
|
+
var _a2;
|
|
315
316
|
if (this._els.title) this._els.title.textContent = val ?? "";
|
|
317
|
+
(_a2 = this._els.card) == null ? void 0 : _a2.setAttribute("aria-label", this._buildAriaLabel());
|
|
316
318
|
},
|
|
317
319
|
publisher: () => {
|
|
318
|
-
|
|
320
|
+
var _a2;
|
|
321
|
+
this._setPrefixedStrong(this._els.publisher, "By ", val);
|
|
322
|
+
(_a2 = this._els.card) == null ? void 0 : _a2.setAttribute("aria-label", this._buildAriaLabel());
|
|
319
323
|
},
|
|
320
324
|
description: () => {
|
|
321
325
|
if (this._els.description) this._els.description.textContent = val ?? "";
|
|
322
326
|
},
|
|
323
327
|
duration: () => {
|
|
324
|
-
|
|
328
|
+
this._setPrefixedStrong(this._els.duration, "", val);
|
|
325
329
|
},
|
|
326
330
|
"last-updated": () => {
|
|
327
|
-
|
|
328
|
-
this._els.lastUpdated.innerHTML = `Last Updated: <strong>${val ?? ""}</strong>`;
|
|
331
|
+
this._setPrefixedStrong(this._els.lastUpdated, "Last Updated: ", val);
|
|
329
332
|
},
|
|
330
333
|
"primary-label": () => {
|
|
331
334
|
if (this._els.primaryBtn) this._els.primaryBtn.textContent = val ?? "";
|
|
@@ -358,8 +361,16 @@ var BvCourseListCard = class extends HTMLElement {
|
|
|
358
361
|
"hide-secondary": () => {
|
|
359
362
|
if (this._els.secondaryBtn) this._els.secondaryBtn.hidden = this.hideSecondary;
|
|
360
363
|
},
|
|
364
|
+
selected: () => {
|
|
365
|
+
var _a2;
|
|
366
|
+
(_a2 = this._els.card) == null ? void 0 : _a2.setAttribute("aria-checked", this.selected ? "true" : "false");
|
|
367
|
+
},
|
|
368
|
+
"disable-selection": () => {
|
|
369
|
+
this._syncSelectionA11y();
|
|
370
|
+
},
|
|
361
371
|
loading: () => {
|
|
362
372
|
this._syncLoading();
|
|
373
|
+
this._syncSelectionA11y();
|
|
363
374
|
}
|
|
364
375
|
};
|
|
365
376
|
(_a = map[name]) == null ? void 0 : _a.call(map);
|
|
@@ -375,6 +386,10 @@ var BvCourseListCard = class extends HTMLElement {
|
|
|
375
386
|
}
|
|
376
387
|
const card = document.createElement("div");
|
|
377
388
|
card.className = "card";
|
|
389
|
+
card.setAttribute("role", "checkbox");
|
|
390
|
+
card.setAttribute("tabindex", "0");
|
|
391
|
+
card.setAttribute("aria-checked", this.selected ? "true" : "false");
|
|
392
|
+
card.setAttribute("aria-label", this._buildAriaLabel());
|
|
378
393
|
this._els.card = card;
|
|
379
394
|
const thumbWrap = document.createElement("div");
|
|
380
395
|
thumbWrap.className = "thumbnail-wrap";
|
|
@@ -400,8 +415,8 @@ var BvCourseListCard = class extends HTMLElement {
|
|
|
400
415
|
this._els.title = title;
|
|
401
416
|
const publisher = document.createElement("span");
|
|
402
417
|
publisher.className = "publisher";
|
|
403
|
-
publisher.innerHTML = `By <strong>${this.getAttribute("publisher") ?? ""}</strong>`;
|
|
404
418
|
this._els.publisher = publisher;
|
|
419
|
+
this._setPrefixedStrong(publisher, "By ", this.getAttribute("publisher"));
|
|
405
420
|
meta.append(title, publisher);
|
|
406
421
|
const descriptionWrap = document.createElement("div");
|
|
407
422
|
descriptionWrap.className = "description-wrap";
|
|
@@ -414,12 +429,12 @@ var BvCourseListCard = class extends HTMLElement {
|
|
|
414
429
|
infoRow.className = "info-row";
|
|
415
430
|
const duration = document.createElement("span");
|
|
416
431
|
duration.className = "info-item";
|
|
417
|
-
duration.innerHTML = `<strong>${this.getAttribute("duration") ?? ""}</strong>`;
|
|
418
432
|
this._els.duration = duration;
|
|
433
|
+
this._setPrefixedStrong(duration, "", this.getAttribute("duration"));
|
|
419
434
|
const lastUpdated = document.createElement("span");
|
|
420
435
|
lastUpdated.className = "info-item";
|
|
421
|
-
lastUpdated.innerHTML = `Last Updated: <strong>${this.getAttribute("last-updated") ?? ""}</strong>`;
|
|
422
436
|
this._els.lastUpdated = lastUpdated;
|
|
437
|
+
this._setPrefixedStrong(lastUpdated, "Last Updated: ", this.getAttribute("last-updated"));
|
|
423
438
|
infoRow.append(duration, lastUpdated);
|
|
424
439
|
content.append(meta, descriptionWrap, infoRow);
|
|
425
440
|
const actions = document.createElement("div");
|
|
@@ -455,18 +470,60 @@ var BvCourseListCard = class extends HTMLElement {
|
|
|
455
470
|
card.append(thumbWrap, content, actions, skeleton);
|
|
456
471
|
shadow.appendChild(card);
|
|
457
472
|
this._syncLoading();
|
|
473
|
+
this._syncSelectionA11y();
|
|
458
474
|
card.addEventListener("click", (e) => {
|
|
459
475
|
if (this.disableSelection || this.loading) return;
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
this.
|
|
463
|
-
new CustomEvent("bv-select", {
|
|
464
|
-
bubbles: true,
|
|
465
|
-
composed: true,
|
|
466
|
-
detail: { selected: this.selected }
|
|
467
|
-
})
|
|
468
|
-
);
|
|
476
|
+
const actionsEl = this._els.actions;
|
|
477
|
+
if (actionsEl && e.composedPath().includes(actionsEl)) return;
|
|
478
|
+
this._toggleSelected();
|
|
469
479
|
});
|
|
480
|
+
card.addEventListener("keydown", (e) => {
|
|
481
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
482
|
+
if (this.disableSelection || this.loading) return;
|
|
483
|
+
const actionsEl = this._els.actions;
|
|
484
|
+
if (actionsEl && e.composedPath().includes(actionsEl)) return;
|
|
485
|
+
e.preventDefault();
|
|
486
|
+
this._toggleSelected();
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
_toggleSelected() {
|
|
490
|
+
this.selected = !this.selected;
|
|
491
|
+
this.dispatchEvent(
|
|
492
|
+
new CustomEvent("bv-select", {
|
|
493
|
+
bubbles: true,
|
|
494
|
+
composed: true,
|
|
495
|
+
detail: { selected: this.selected }
|
|
496
|
+
})
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
_buildAriaLabel() {
|
|
500
|
+
const title = this.getAttribute("title") ?? "";
|
|
501
|
+
const publisher = this.getAttribute("publisher") ?? "";
|
|
502
|
+
const parts = [];
|
|
503
|
+
if (title) parts.push(title);
|
|
504
|
+
if (publisher) parts.push(`by ${publisher}`);
|
|
505
|
+
return parts.join(", ");
|
|
506
|
+
}
|
|
507
|
+
_setPrefixedStrong(el, prefix, val) {
|
|
508
|
+
if (!el) return;
|
|
509
|
+
const value = val ?? "";
|
|
510
|
+
if (!value) {
|
|
511
|
+
el.hidden = true;
|
|
512
|
+
el.replaceChildren();
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
el.hidden = false;
|
|
516
|
+
const strong = document.createElement("strong");
|
|
517
|
+
strong.textContent = value;
|
|
518
|
+
if (prefix) el.replaceChildren(document.createTextNode(prefix), strong);
|
|
519
|
+
else el.replaceChildren(strong);
|
|
520
|
+
}
|
|
521
|
+
_syncSelectionA11y() {
|
|
522
|
+
var _a, _b, _c;
|
|
523
|
+
const inert = this.disableSelection || this.loading;
|
|
524
|
+
(_a = this._els.card) == null ? void 0 : _a.setAttribute("tabindex", inert ? "-1" : "0");
|
|
525
|
+
if (inert) (_b = this._els.card) == null ? void 0 : _b.setAttribute("aria-disabled", "true");
|
|
526
|
+
else (_c = this._els.card) == null ? void 0 : _c.removeAttribute("aria-disabled");
|
|
470
527
|
}
|
|
471
528
|
_syncLoading() {
|
|
472
529
|
var _a;
|
package/dist/themes/base.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Do not edit directly
|
|
3
|
-
* Generated on
|
|
3
|
+
* Generated on Wed, 22 Jul 2026 02:07:11 GMT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
:root {
|
|
@@ -76,4 +76,5 @@
|
|
|
76
76
|
--bv-transition-fast: 100ms ease-in-out;
|
|
77
77
|
--bv-transition-normal: 200ms ease-in-out;
|
|
78
78
|
--bv-transition-slow: 300ms ease-in-out;
|
|
79
|
+
--bv-focus-ring: 2px solid #0277bd;
|
|
79
80
|
}
|
package/package.json
CHANGED
package/themes/base.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Do not edit directly
|
|
3
|
-
* Generated on
|
|
3
|
+
* Generated on Wed, 22 Jul 2026 02:07:11 GMT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
:root {
|
|
@@ -76,4 +76,5 @@
|
|
|
76
76
|
--bv-transition-fast: 100ms ease-in-out;
|
|
77
77
|
--bv-transition-normal: 200ms ease-in-out;
|
|
78
78
|
--bv-transition-slow: 300ms ease-in-out;
|
|
79
|
+
--bv-focus-ring: 2px solid #0277bd;
|
|
79
80
|
}
|