@byuhbll/components 5.0.1 → 5.0.3-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/button/button.mjs +95 -0
- package/esm2022/lib/button-group/button-group.component.mjs +63 -0
- package/esm2022/lib/header-with-impersonation/header-with-impersonation.component.mjs +81 -2
- package/esm2022/lib/subatomic-components/button-group-item/button-group-item.component.mjs +29 -0
- package/esm2022/public-api.mjs +3 -1
- package/fesm2022/byuhbll-components.mjs +258 -2
- package/fesm2022/byuhbll-components.mjs.map +1 -1
- package/lib/button/button.d.ts +139 -0
- package/lib/button-group/button-group.component.d.ts +17 -0
- package/lib/header-with-impersonation/header-with-impersonation.component.d.ts +19 -2
- package/lib/subatomic-components/button-group-item/button-group-item.component.d.ts +18 -0
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
|
@@ -3,7 +3,7 @@ import { CommonModule, DatePipe, DOCUMENT, LowerCasePipe, NgIf, NgClass } from '
|
|
|
3
3
|
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { HttpClient } from '@angular/common/http';
|
|
5
5
|
import * as i0 from '@angular/core';
|
|
6
|
-
import { Component, ChangeDetectionStrategy, ViewChild, Input, input, EventEmitter, Output, inject, computed, ViewChildren, Pipe, Renderer2, viewChild, HostListener, ElementRef, ViewEncapsulation, booleanAttribute, createComponent, Injectable } from '@angular/core';
|
|
6
|
+
import { Component, ChangeDetectionStrategy, ViewChild, Input, input, EventEmitter, Output, inject, computed, ViewChildren, Pipe, Renderer2, viewChild, HostListener, ElementRef, Injector, runInInjectionContext, effect, ViewEncapsulation, booleanAttribute, createComponent, Injectable, signal } from '@angular/core';
|
|
7
7
|
import { trigger, transition, group, style, query, animateChild, animate } from '@angular/animations';
|
|
8
8
|
import { map, of, switchMap, shareReplay, combineLatest, Subject, Subscription } from 'rxjs';
|
|
9
9
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
|
@@ -1302,6 +1302,85 @@ class HeaderWithImpersonationComponent {
|
|
|
1302
1302
|
: false);
|
|
1303
1303
|
this.showImpersonationModal = false;
|
|
1304
1304
|
this.isImpersonating = computed(() => !!this.parsedToken()?.['impersonator']);
|
|
1305
|
+
// Inactivity timeout for impersonation
|
|
1306
|
+
this.activityEvents = [
|
|
1307
|
+
'keydown',
|
|
1308
|
+
'pointerdown',
|
|
1309
|
+
'wheel',
|
|
1310
|
+
'scroll',
|
|
1311
|
+
];
|
|
1312
|
+
this.inactivityTimerId = null;
|
|
1313
|
+
this.inactivityTimeoutMs = 5 * 60 * 1000; // 5 minutes
|
|
1314
|
+
this.debounceTimerId = null;
|
|
1315
|
+
this.debounceDelayMs = 250;
|
|
1316
|
+
this.trackingActive = false;
|
|
1317
|
+
this.injector = inject(Injector);
|
|
1318
|
+
/** Reset the inactivity countdown (no-op if not impersonating) */
|
|
1319
|
+
this.resetInactivityTimer = () => {
|
|
1320
|
+
if (!this.isImpersonating())
|
|
1321
|
+
return;
|
|
1322
|
+
if (this.inactivityTimerId)
|
|
1323
|
+
clearTimeout(this.inactivityTimerId);
|
|
1324
|
+
this.inactivityTimerId = window.setTimeout(() => {
|
|
1325
|
+
this.endImpersonation.emit();
|
|
1326
|
+
this.stopInactivityTracking();
|
|
1327
|
+
}, this.inactivityTimeoutMs);
|
|
1328
|
+
};
|
|
1329
|
+
/** Debounce activity to avoid hammering resets during event storms */
|
|
1330
|
+
this.debouncedResetTimer = () => {
|
|
1331
|
+
if (this.debounceTimerId)
|
|
1332
|
+
clearTimeout(this.debounceTimerId);
|
|
1333
|
+
this.debounceTimerId = window.setTimeout(() => {
|
|
1334
|
+
this.resetInactivityTimer();
|
|
1335
|
+
}, this.debounceDelayMs);
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
ngOnInit() {
|
|
1339
|
+
// effect can only be used within an injection context (ex: constructor)
|
|
1340
|
+
runInInjectionContext(this.injector, () => {
|
|
1341
|
+
effect(() => {
|
|
1342
|
+
if (this.isImpersonating()) {
|
|
1343
|
+
if (!this.trackingActive)
|
|
1344
|
+
this.startInactivityTracking();
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
if (this.trackingActive)
|
|
1348
|
+
this.stopInactivityTracking();
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
ngOnDestroy() {
|
|
1354
|
+
this.stopInactivityTracking();
|
|
1355
|
+
}
|
|
1356
|
+
/** Begin listening and start countdown */
|
|
1357
|
+
startInactivityTracking() {
|
|
1358
|
+
this.activityEvents.forEach((event) => {
|
|
1359
|
+
// 'scroll' on document doesn't bubble; use window + capture to catch nested scrolls
|
|
1360
|
+
const target = event === 'scroll' ? window : document;
|
|
1361
|
+
const options = event === 'scroll'
|
|
1362
|
+
? { passive: true, capture: true }
|
|
1363
|
+
: { passive: true };
|
|
1364
|
+
target.addEventListener(event, this.debouncedResetTimer, options);
|
|
1365
|
+
});
|
|
1366
|
+
this.trackingActive = true;
|
|
1367
|
+
this.resetInactivityTimer();
|
|
1368
|
+
}
|
|
1369
|
+
/** Remove listeners and clear timers */
|
|
1370
|
+
stopInactivityTracking() {
|
|
1371
|
+
this.activityEvents.forEach((event) => {
|
|
1372
|
+
const target = event === 'scroll' ? window : document;
|
|
1373
|
+
target.removeEventListener(event, this.debouncedResetTimer);
|
|
1374
|
+
});
|
|
1375
|
+
if (this.inactivityTimerId) {
|
|
1376
|
+
clearTimeout(this.inactivityTimerId);
|
|
1377
|
+
this.inactivityTimerId = null;
|
|
1378
|
+
}
|
|
1379
|
+
if (this.debounceTimerId) {
|
|
1380
|
+
clearTimeout(this.debounceTimerId);
|
|
1381
|
+
this.debounceTimerId = null;
|
|
1382
|
+
}
|
|
1383
|
+
this.trackingActive = false;
|
|
1305
1384
|
}
|
|
1306
1385
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: HeaderWithImpersonationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1307
1386
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.1.0", type: HeaderWithImpersonationComponent, isStandalone: true, selector: "lib-header-with-impersonation", inputs: { accessTokenPayload: { classPropertyName: "accessTokenPayload", publicName: "accessTokenPayload", isSignal: true, isRequired: true, transformFunction: null }, oidcBaseUri: { classPropertyName: "oidcBaseUri", publicName: "oidcBaseUri", isSignal: true, isRequired: false, transformFunction: null }, oidcDefaultIdp: { classPropertyName: "oidcDefaultIdp", publicName: "oidcDefaultIdp", isSignal: true, isRequired: false, transformFunction: null }, mainSiteBaseUrl: { classPropertyName: "mainSiteBaseUrl", publicName: "mainSiteBaseUrl", isSignal: true, isRequired: false, transformFunction: null }, personBaseUri: { classPropertyName: "personBaseUri", publicName: "personBaseUri", isSignal: true, isRequired: false, transformFunction: null }, myAccountApiBaseUri: { classPropertyName: "myAccountApiBaseUri", publicName: "myAccountApiBaseUri", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { login: "login", logout: "logout", endImpersonation: "endImpersonation" }, ngImport: i0, template: "<lib-impersonation-banner\n [accessTokenPayload]=\"accessTokenPayload()\"\n (endImpersonation)=\"endImpersonation.emit()\"\n [personBaseUri]=\"personBaseUri()\"\n [myAccountApiBaseUri]=\"myAccountApiBaseUri()\"\n></lib-impersonation-banner>\n<lib-hbll-header\n [name]=\"name()\"\n [showImpersonateButton]=\"showImpersonateButton()\"\n (openImpersonationModal)=\"showImpersonationModal = true\"\n (login)=\"login.emit()\"\n (logout)=\"logout.emit()\"\n [mainsitebaseurl]=\"mainSiteBaseUrl()\"\n/>\n<lib-impersonate-modal\n [showModal]=\"showImpersonationModal\"\n [oidcBaseUri]=\"oidcBaseUri()\"\n [oidcDefaultIdp]=\"oidcDefaultIdp()\"\n [accessTokenPayload]=\"accessTokenPayload()\"\n (dismiss)=\"showImpersonationModal = false\"\n (init)=\"showImpersonationModal = true\"\n></lib-impersonate-modal>\n", styles: [""], dependencies: [{ kind: "component", type: HbllHeaderComponent, selector: "lib-hbll-header", inputs: ["name", "mainsitebaseurl", "showImpersonateButton"], outputs: ["openImpersonationModal", "login", "logout"] }, { kind: "component", type: ImpersonationBannerComponent, selector: "lib-impersonation-banner", inputs: ["accessTokenPayload", "personBaseUri", "myAccountApiBaseUri"], outputs: ["endImpersonation"] }, { kind: "component", type: ImpersonateModalComponent, selector: "lib-impersonate-modal", inputs: ["showModal", "oidcBaseUri", "oidcDefaultIdp", "accessTokenPayload"], outputs: ["dismiss", "init"] }] }); }
|
|
@@ -2502,6 +2581,183 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImpor
|
|
|
2502
2581
|
args: [{ providedIn: 'root' }]
|
|
2503
2582
|
}], ctorParameters: () => [{ type: i0.ApplicationRef }, { type: i0.EnvironmentInjector }] });
|
|
2504
2583
|
|
|
2584
|
+
/**
|
|
2585
|
+
* A flexible, reusable button component that supports multiple button types
|
|
2586
|
+
* and various content combinations (icon before, title, icon after).
|
|
2587
|
+
*
|
|
2588
|
+
* @example
|
|
2589
|
+
* ```html
|
|
2590
|
+
* <!-- Primary button with icon and title -->
|
|
2591
|
+
* <app-hbll-button-custom-component
|
|
2592
|
+
* buttonType="primary"
|
|
2593
|
+
* title="Copy Citation"
|
|
2594
|
+
* iconBefore="../../assets/copy.svg"
|
|
2595
|
+
* iconAlt="Copy icon"
|
|
2596
|
+
* (buttonClick)="copyCitation()">
|
|
2597
|
+
* </app-hbll-button-custom-component>
|
|
2598
|
+
*
|
|
2599
|
+
* <!-- Secondary button with title only -->
|
|
2600
|
+
* <app-hbll-button-custom-component
|
|
2601
|
+
* buttonType="secondary"
|
|
2602
|
+
* title="Cancel"
|
|
2603
|
+
* (buttonClick)="cancelAction()">
|
|
2604
|
+
* </app-hbll-button-custom-component>
|
|
2605
|
+
*
|
|
2606
|
+
* <!-- Transparent button with icon after title -->
|
|
2607
|
+
* <app-hbll-button-custom-component
|
|
2608
|
+
* buttonType="transparent"
|
|
2609
|
+
* title="Download"
|
|
2610
|
+
* iconAfter="../../assets/download.svg"
|
|
2611
|
+
* iconAlt="Download icon"
|
|
2612
|
+
* (buttonClick)="downloadFile()">
|
|
2613
|
+
* </app-hbll-button-custom-component>
|
|
2614
|
+
*
|
|
2615
|
+
* <!-- Thin button with reduced padding -->
|
|
2616
|
+
* <app-hbll-button-custom-component
|
|
2617
|
+
* buttonType="primary"
|
|
2618
|
+
* title="Submit"
|
|
2619
|
+
* [isThin]="true"
|
|
2620
|
+
* (buttonClick)="submitForm()">
|
|
2621
|
+
* </app-hbll-button-custom-component>
|
|
2622
|
+
* ```
|
|
2623
|
+
*/
|
|
2624
|
+
class ButtonComponent {
|
|
2625
|
+
constructor() {
|
|
2626
|
+
/**
|
|
2627
|
+
* The visual style of the button
|
|
2628
|
+
* @default 'primary'
|
|
2629
|
+
*/
|
|
2630
|
+
this.buttonType = 'primary';
|
|
2631
|
+
/**
|
|
2632
|
+
* Whether the button is disabled
|
|
2633
|
+
* @default false
|
|
2634
|
+
*/
|
|
2635
|
+
this.disabled = false;
|
|
2636
|
+
/**
|
|
2637
|
+
* Whether the button should have thin padding
|
|
2638
|
+
* @default false
|
|
2639
|
+
*/
|
|
2640
|
+
this.isThin = false;
|
|
2641
|
+
/**
|
|
2642
|
+
* Event emitted when the button is clicked or activated via keyboard
|
|
2643
|
+
*/
|
|
2644
|
+
this.buttonClick = new EventEmitter();
|
|
2645
|
+
}
|
|
2646
|
+
onButtonClick() {
|
|
2647
|
+
if (!this.disabled) {
|
|
2648
|
+
this.buttonClick.emit();
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2652
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.0", type: ButtonComponent, isStandalone: true, selector: "lib-button", inputs: { buttonType: "buttonType", title: "title", iconBefore: "iconBefore", iconAfter: "iconAfter", iconAlt: "iconAlt", disabled: "disabled", isThin: "isThin" }, outputs: { buttonClick: "buttonClick" }, ngImport: i0, template: "<button\n type=\"button\"\n [class]=\"'btn btn-' + buttonType + (isThin ? ' btn-thin' : '')\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"title || iconAlt\"\n (click)=\"onButtonClick()\"\n [attr.tabindex]=\"disabled ? -1 : 0\"\n>\n <!-- Icon before title -->\n <span class=\"icon icon-before\" *ngIf=\"iconBefore\">\n <img\n [src]=\"iconBefore\"\n [class]=\"\n disabled\n ? 'disabled-icon'\n : buttonType === 'primary'\n ? 'white-icon'\n : 'dark-icon'\n \"\n [alt]=\"iconAlt || ''\"\n />\n </span>\n\n <!-- Title text -->\n <span class=\"button-title\" *ngIf=\"title\">{{ title }}</span>\n\n <!-- Icon after title -->\n <span class=\"icon icon-after\" *ngIf=\"iconAfter\">\n <img\n [src]=\"iconAfter\"\n [class]=\"\n disabled\n ? 'disabled-icon'\n : buttonType === 'primary'\n ? 'white-icon'\n : 'dark-icon'\n \"\n [alt]=\"iconAlt || ''\"\n />\n </span>\n</button>\n", styles: [".btn{padding:.8rem 2rem;border-radius:.25rem;font-size:1rem;font-weight:600;cursor:pointer;transition:all .2s ease;border:none;display:inline-flex;align-items:center;outline:none}.btn.btn-thin{padding:.4rem 2rem;border-radius:.5rem}.btn:disabled{cursor:not-allowed;color:#767676}.btn:disabled:not(.btn-transparent){background-color:#e7e7e7;border:1px solid #767676}.btn:focus{outline:.125rem solid #b967c7;outline-offset:.125rem}.btn .icon{display:flex;align-items:center;justify-content:center}.btn .icon img{height:1.5rem;width:auto}.btn .icon.icon-before{margin-right:.25rem}.btn .icon.icon-after{margin-left:.25rem}.btn .button-title{flex-shrink:0}.btn-primary{background-color:#0047ba;color:#fff;border:1px solid #0047ba}.btn-primary:hover:not(:disabled){background-color:#003995}.btn-secondary{background-color:#fff;color:#00245d;border:1px solid #0047ba}.btn-secondary:hover:not(:disabled){background-color:#e5edf8}.btn-transparent{background-color:transparent;color:#00245d}.btn-transparent:hover:not(:disabled){background-color:#e5edf8}.white-icon{filter:invert(100%) sepia(0%) saturate(7489%) hue-rotate(149deg) brightness(104%) contrast(100%)}.dark-icon{filter:invert(12%) sepia(44%) saturate(3863%) hue-rotate(207deg) brightness(90%) contrast(105%)}.disabled-icon{filter:invert(48%) sepia(0%) saturate(0%) hue-rotate(146deg) brightness(96%) contrast(88%)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
|
|
2653
|
+
}
|
|
2654
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ButtonComponent, decorators: [{
|
|
2655
|
+
type: Component,
|
|
2656
|
+
args: [{ selector: 'lib-button', standalone: true, imports: [CommonModule], template: "<button\n type=\"button\"\n [class]=\"'btn btn-' + buttonType + (isThin ? ' btn-thin' : '')\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"title || iconAlt\"\n (click)=\"onButtonClick()\"\n [attr.tabindex]=\"disabled ? -1 : 0\"\n>\n <!-- Icon before title -->\n <span class=\"icon icon-before\" *ngIf=\"iconBefore\">\n <img\n [src]=\"iconBefore\"\n [class]=\"\n disabled\n ? 'disabled-icon'\n : buttonType === 'primary'\n ? 'white-icon'\n : 'dark-icon'\n \"\n [alt]=\"iconAlt || ''\"\n />\n </span>\n\n <!-- Title text -->\n <span class=\"button-title\" *ngIf=\"title\">{{ title }}</span>\n\n <!-- Icon after title -->\n <span class=\"icon icon-after\" *ngIf=\"iconAfter\">\n <img\n [src]=\"iconAfter\"\n [class]=\"\n disabled\n ? 'disabled-icon'\n : buttonType === 'primary'\n ? 'white-icon'\n : 'dark-icon'\n \"\n [alt]=\"iconAlt || ''\"\n />\n </span>\n</button>\n", styles: [".btn{padding:.8rem 2rem;border-radius:.25rem;font-size:1rem;font-weight:600;cursor:pointer;transition:all .2s ease;border:none;display:inline-flex;align-items:center;outline:none}.btn.btn-thin{padding:.4rem 2rem;border-radius:.5rem}.btn:disabled{cursor:not-allowed;color:#767676}.btn:disabled:not(.btn-transparent){background-color:#e7e7e7;border:1px solid #767676}.btn:focus{outline:.125rem solid #b967c7;outline-offset:.125rem}.btn .icon{display:flex;align-items:center;justify-content:center}.btn .icon img{height:1.5rem;width:auto}.btn .icon.icon-before{margin-right:.25rem}.btn .icon.icon-after{margin-left:.25rem}.btn .button-title{flex-shrink:0}.btn-primary{background-color:#0047ba;color:#fff;border:1px solid #0047ba}.btn-primary:hover:not(:disabled){background-color:#003995}.btn-secondary{background-color:#fff;color:#00245d;border:1px solid #0047ba}.btn-secondary:hover:not(:disabled){background-color:#e5edf8}.btn-transparent{background-color:transparent;color:#00245d}.btn-transparent:hover:not(:disabled){background-color:#e5edf8}.white-icon{filter:invert(100%) sepia(0%) saturate(7489%) hue-rotate(149deg) brightness(104%) contrast(100%)}.dark-icon{filter:invert(12%) sepia(44%) saturate(3863%) hue-rotate(207deg) brightness(90%) contrast(105%)}.disabled-icon{filter:invert(48%) sepia(0%) saturate(0%) hue-rotate(146deg) brightness(96%) contrast(88%)}\n"] }]
|
|
2657
|
+
}], propDecorators: { buttonType: [{
|
|
2658
|
+
type: Input
|
|
2659
|
+
}], title: [{
|
|
2660
|
+
type: Input
|
|
2661
|
+
}], iconBefore: [{
|
|
2662
|
+
type: Input
|
|
2663
|
+
}], iconAfter: [{
|
|
2664
|
+
type: Input
|
|
2665
|
+
}], iconAlt: [{
|
|
2666
|
+
type: Input
|
|
2667
|
+
}], disabled: [{
|
|
2668
|
+
type: Input
|
|
2669
|
+
}], isThin: [{
|
|
2670
|
+
type: Input
|
|
2671
|
+
}], buttonClick: [{
|
|
2672
|
+
type: Output
|
|
2673
|
+
}] } });
|
|
2674
|
+
|
|
2675
|
+
class ButtonGroupItemComponent {
|
|
2676
|
+
constructor() {
|
|
2677
|
+
this.isActive = false;
|
|
2678
|
+
this.disabled = false;
|
|
2679
|
+
this.buttonClick = new EventEmitter();
|
|
2680
|
+
}
|
|
2681
|
+
onClick() {
|
|
2682
|
+
this.buttonClick.emit(this.button.id);
|
|
2683
|
+
}
|
|
2684
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ButtonGroupItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2685
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.0", type: ButtonGroupItemComponent, isStandalone: true, selector: "lib-button-group-item", inputs: { button: "button", isActive: "isActive", disabled: "disabled" }, outputs: { buttonClick: "buttonClick" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"isActive\"\n [disabled]=\"disabled\"\n (click)=\"onClick()\"\n role=\"tab\"\n [attr.aria-pressed]=\"isActive\"\n [attr.aria-label]=\"button.title\"\n [attr.tabindex]=\"disabled ? -1 : 0\"\n>\n @if (button.icon) {\n <span class=\"icon\">\n <img\n [src]=\"button.icon\"\n [class]=\"disabled ? 'disabled-icon' : isActive ? 'white-icon' : 'dark-icon'\"\n [alt]=\"button.iconAlt || ''\"\n />\n </span>\n }\n\n @if (button.title) {\n <span class=\"button-title\">{{ button.title }}</span>\n }\n</button>\n", styles: [".tab-btn{padding:.5rem;font-size:1rem;font-weight:400;color:#00245d;background-color:#fff;cursor:pointer;position:relative;border:1px solid #d0d0d0;border-left:none;border-radius:0;transition:all .2s ease;display:inline-flex;align-items:center;justify-content:center;gap:.25rem;height:2.3rem}.tab-btn:not(:disabled):hover{background-color:#e5edf8}.tab-btn.active{background-color:#0047ba;color:#fff;z-index:10}.tab-btn.active:not(:disabled):hover{background-color:#003995}.tab-btn:focus{outline:.125rem solid #b967c7;outline-offset:.125rem}.tab-btn:disabled{cursor:not-allowed;color:#767676;background-color:#e7e7e7;border:1px solid #767676}.tab-btn .icon{display:flex;align-items:center;justify-content:center}.tab-btn .icon img{height:1rem;width:auto}.tab-btn .button-title{flex-shrink:0}.white-icon{filter:invert(100%) sepia(0%) saturate(7489%) hue-rotate(149deg) brightness(104%) contrast(100%)}.dark-icon{filter:invert(12%) sepia(44%) saturate(3863%) hue-rotate(207deg) brightness(90%) contrast(105%)}.disabled-icon{filter:invert(48%) sepia(0%) saturate(0%) hue-rotate(146deg) brightness(96%) contrast(88%)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
|
|
2686
|
+
}
|
|
2687
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ButtonGroupItemComponent, decorators: [{
|
|
2688
|
+
type: Component,
|
|
2689
|
+
args: [{ selector: 'lib-button-group-item', standalone: true, imports: [CommonModule], template: "<button\n type=\"button\"\n class=\"tab-btn\"\n [class.active]=\"isActive\"\n [disabled]=\"disabled\"\n (click)=\"onClick()\"\n role=\"tab\"\n [attr.aria-pressed]=\"isActive\"\n [attr.aria-label]=\"button.title\"\n [attr.tabindex]=\"disabled ? -1 : 0\"\n>\n @if (button.icon) {\n <span class=\"icon\">\n <img\n [src]=\"button.icon\"\n [class]=\"disabled ? 'disabled-icon' : isActive ? 'white-icon' : 'dark-icon'\"\n [alt]=\"button.iconAlt || ''\"\n />\n </span>\n }\n\n @if (button.title) {\n <span class=\"button-title\">{{ button.title }}</span>\n }\n</button>\n", styles: [".tab-btn{padding:.5rem;font-size:1rem;font-weight:400;color:#00245d;background-color:#fff;cursor:pointer;position:relative;border:1px solid #d0d0d0;border-left:none;border-radius:0;transition:all .2s ease;display:inline-flex;align-items:center;justify-content:center;gap:.25rem;height:2.3rem}.tab-btn:not(:disabled):hover{background-color:#e5edf8}.tab-btn.active{background-color:#0047ba;color:#fff;z-index:10}.tab-btn.active:not(:disabled):hover{background-color:#003995}.tab-btn:focus{outline:.125rem solid #b967c7;outline-offset:.125rem}.tab-btn:disabled{cursor:not-allowed;color:#767676;background-color:#e7e7e7;border:1px solid #767676}.tab-btn .icon{display:flex;align-items:center;justify-content:center}.tab-btn .icon img{height:1rem;width:auto}.tab-btn .button-title{flex-shrink:0}.white-icon{filter:invert(100%) sepia(0%) saturate(7489%) hue-rotate(149deg) brightness(104%) contrast(100%)}.dark-icon{filter:invert(12%) sepia(44%) saturate(3863%) hue-rotate(207deg) brightness(90%) contrast(105%)}.disabled-icon{filter:invert(48%) sepia(0%) saturate(0%) hue-rotate(146deg) brightness(96%) contrast(88%)}\n"] }]
|
|
2690
|
+
}], propDecorators: { button: [{
|
|
2691
|
+
type: Input,
|
|
2692
|
+
args: [{ required: true }]
|
|
2693
|
+
}], isActive: [{
|
|
2694
|
+
type: Input
|
|
2695
|
+
}], disabled: [{
|
|
2696
|
+
type: Input
|
|
2697
|
+
}], buttonClick: [{
|
|
2698
|
+
type: Output
|
|
2699
|
+
}] } });
|
|
2700
|
+
|
|
2701
|
+
class ButtonGroupComponent {
|
|
2702
|
+
constructor() {
|
|
2703
|
+
// Input: Whether the button group is disabled
|
|
2704
|
+
this.disabled = false;
|
|
2705
|
+
// Output: Emits the id of the active button when changed
|
|
2706
|
+
this.activeButtonChange = new EventEmitter();
|
|
2707
|
+
// Internal state: Currently active button id
|
|
2708
|
+
this.activeButtonId = signal('');
|
|
2709
|
+
}
|
|
2710
|
+
ngOnInit() {
|
|
2711
|
+
this.initializeActiveButton();
|
|
2712
|
+
}
|
|
2713
|
+
ngOnChanges(changes) {
|
|
2714
|
+
// When buttons or initialActiveId change (important for web components)
|
|
2715
|
+
if (changes['buttons'] || changes['initialActiveId']) {
|
|
2716
|
+
this.initializeActiveButton();
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
initializeActiveButton() {
|
|
2720
|
+
// Set initial active button
|
|
2721
|
+
const initial = this.initialActiveId;
|
|
2722
|
+
if (initial) {
|
|
2723
|
+
this.activeButtonId.set(initial);
|
|
2724
|
+
}
|
|
2725
|
+
else {
|
|
2726
|
+
// Find the first active button from the config
|
|
2727
|
+
const activeButton = this.buttons?.find((btn) => btn.isActive);
|
|
2728
|
+
if (activeButton) {
|
|
2729
|
+
this.activeButtonId.set(activeButton.id);
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
// Handle button click
|
|
2734
|
+
onButtonClick(buttonId) {
|
|
2735
|
+
if (!this.disabled) {
|
|
2736
|
+
this.activeButtonId.set(buttonId);
|
|
2737
|
+
this.activeButtonChange.emit(buttonId);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
// Check if a button is active
|
|
2741
|
+
isButtonActive(buttonId) {
|
|
2742
|
+
return this.activeButtonId() === buttonId;
|
|
2743
|
+
}
|
|
2744
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ButtonGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2745
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.0", type: ButtonGroupComponent, isStandalone: true, selector: "lib-button-group", inputs: { buttons: "buttons", initialActiveId: "initialActiveId", disabled: "disabled" }, outputs: { activeButtonChange: "activeButtonChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"button-group\" role=\"tablist\">\n @for (button of buttons; track button.id) {\n <lib-button-group-item\n [button]=\"button\"\n [isActive]=\"isButtonActive(button.id)\"\n [disabled]=\"disabled\"\n (buttonClick)=\"onButtonClick($event)\"\n class=\"button-group-item\"\n />\n }\n</div>\n", styles: [".button-group{display:flex}.button-group lib-button-group-item:first-child::ng-deep .tab-btn{border-radius:.25rem 0 0 .25rem;border-left:1px solid #d0d0d0}.button-group lib-button-group-item:first-child::ng-deep .tab-btn:disabled{border-left:1px solid #767676}.button-group lib-button-group-item:last-child::ng-deep .tab-btn{border-radius:0 .25rem .25rem 0}@media (max-width: 640px){.button-group{overflow-x:auto;-webkit-overflow-scrolling:touch}}\n"], dependencies: [{ kind: "component", type: ButtonGroupItemComponent, selector: "lib-button-group-item", inputs: ["button", "isActive", "disabled"], outputs: ["buttonClick"] }] }); }
|
|
2746
|
+
}
|
|
2747
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: ButtonGroupComponent, decorators: [{
|
|
2748
|
+
type: Component,
|
|
2749
|
+
args: [{ selector: 'lib-button-group', standalone: true, imports: [ButtonGroupItemComponent], template: "<div class=\"button-group\" role=\"tablist\">\n @for (button of buttons; track button.id) {\n <lib-button-group-item\n [button]=\"button\"\n [isActive]=\"isButtonActive(button.id)\"\n [disabled]=\"disabled\"\n (buttonClick)=\"onButtonClick($event)\"\n class=\"button-group-item\"\n />\n }\n</div>\n", styles: [".button-group{display:flex}.button-group lib-button-group-item:first-child::ng-deep .tab-btn{border-radius:.25rem 0 0 .25rem;border-left:1px solid #d0d0d0}.button-group lib-button-group-item:first-child::ng-deep .tab-btn:disabled{border-left:1px solid #767676}.button-group lib-button-group-item:last-child::ng-deep .tab-btn{border-radius:0 .25rem .25rem 0}@media (max-width: 640px){.button-group{overflow-x:auto;-webkit-overflow-scrolling:touch}}\n"] }]
|
|
2750
|
+
}], propDecorators: { buttons: [{
|
|
2751
|
+
type: Input,
|
|
2752
|
+
args: [{ required: true }]
|
|
2753
|
+
}], initialActiveId: [{
|
|
2754
|
+
type: Input
|
|
2755
|
+
}], disabled: [{
|
|
2756
|
+
type: Input
|
|
2757
|
+
}], activeButtonChange: [{
|
|
2758
|
+
type: Output
|
|
2759
|
+
}] } });
|
|
2760
|
+
|
|
2505
2761
|
/*
|
|
2506
2762
|
* Public API Surface of components
|
|
2507
2763
|
*/
|
|
@@ -2510,5 +2766,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImpor
|
|
|
2510
2766
|
* Generated bundle index. Do not edit.
|
|
2511
2767
|
*/
|
|
2512
2768
|
|
|
2513
|
-
export { ADVANCED_SEARCH_FIELD_MAP, ADVANCED_SEARCH_OPTIONS, ADVANCED_SEARCH_QUALIFIER_MAP, HbllFooterComponent, HbllHeaderComponent, HbllItemTypeIconPipe, HeaderWithImpersonationComponent, ImpersonateModalComponent, ImpersonateUserPipe, ImpersonationBannerComponent, LIBRARY_HOURS_API_URL, SnackbarComponent, SnackbarService, SsSearchBarComponent, StatusButtonComponent, defaultOidcBaseUri, defaultOidcDefaultIdp, getUserStatusFromRoles, isAdvancedSearchExternalFieldOption, isAdvancedSearchFieldOption, isAdvancedSearchLocalFieldOption, isSearchScope };
|
|
2769
|
+
export { ADVANCED_SEARCH_FIELD_MAP, ADVANCED_SEARCH_OPTIONS, ADVANCED_SEARCH_QUALIFIER_MAP, ButtonComponent, ButtonGroupComponent, HbllFooterComponent, HbllHeaderComponent, HbllItemTypeIconPipe, HeaderWithImpersonationComponent, ImpersonateModalComponent, ImpersonateUserPipe, ImpersonationBannerComponent, LIBRARY_HOURS_API_URL, SnackbarComponent, SnackbarService, SsSearchBarComponent, StatusButtonComponent, defaultOidcBaseUri, defaultOidcDefaultIdp, getUserStatusFromRoles, isAdvancedSearchExternalFieldOption, isAdvancedSearchFieldOption, isAdvancedSearchLocalFieldOption, isSearchScope };
|
|
2514
2770
|
//# sourceMappingURL=byuhbll-components.mjs.map
|