@byuhbll/components 4.3.1 → 4.4.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.
@@ -1,9 +1,9 @@
1
1
  import * as i1 from '@angular/common';
2
- import { CommonModule, DatePipe, DOCUMENT, LowerCasePipe } from '@angular/common';
2
+ import { CommonModule, DatePipe, DOCUMENT, LowerCasePipe, NgIf, NgClass } from '@angular/common';
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 } 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';
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';
@@ -2172,6 +2172,323 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImpor
2172
2172
  const searchScopeValues = ['local', 'external'];
2173
2173
  const isSearchScope = (scope) => searchScopeValues.includes(scope);
2174
2174
 
2175
+ /**
2176
+ * A button that indicates status with color and icon.
2177
+ * Status can be one of: 'success', 'info', 'warning', 'error', or 'disabled'.
2178
+ * Each status has a default left icon, but a custom left icon can be provided.
2179
+ * A right icon can also be provided, defaulting to 'chevron_right'.
2180
+ * The left and right icons can be hidden with the `hideLeftIcon` and `hideRightIcon` boolean inputs.
2181
+ * A label input is required.
2182
+ */
2183
+ class StatusButtonComponent {
2184
+ constructor() {
2185
+ this._status = 'info';
2186
+ this._leftIcon = '';
2187
+ this._rightIcon = '';
2188
+ this._hideLeftIcon = false;
2189
+ this._hideRightIcon = false;
2190
+ this._clearVariant = false;
2191
+ }
2192
+ set status(value) {
2193
+ this._status = value || 'info';
2194
+ }
2195
+ get status() {
2196
+ return this._status;
2197
+ }
2198
+ set leftIcon(value) {
2199
+ this._leftIcon = value;
2200
+ }
2201
+ get leftIcon() {
2202
+ if (this._hideLeftIcon)
2203
+ return '';
2204
+ return this._leftIcon || this.getDefaultLeftIcon(this._status);
2205
+ }
2206
+ set rightIcon(value) {
2207
+ this._rightIcon = value;
2208
+ }
2209
+ get rightIcon() {
2210
+ if (this._hideRightIcon)
2211
+ return '';
2212
+ return this._rightIcon || 'chevron_right';
2213
+ }
2214
+ set hideLeftIcon(value) {
2215
+ this._hideLeftIcon = value;
2216
+ }
2217
+ get hideLeftIcon() {
2218
+ return this._hideLeftIcon;
2219
+ }
2220
+ set hideRightIcon(value) {
2221
+ this._hideRightIcon = value;
2222
+ }
2223
+ get hideRightIcon() {
2224
+ return this._hideRightIcon;
2225
+ }
2226
+ set clearVariant(value) {
2227
+ this._clearVariant = value;
2228
+ }
2229
+ get clearVariant() {
2230
+ return this._clearVariant;
2231
+ }
2232
+ /** helper for default left icons */
2233
+ getDefaultLeftIcon(status) {
2234
+ switch (status) {
2235
+ case 'success':
2236
+ return 'check';
2237
+ case 'info':
2238
+ return 'info';
2239
+ case 'warning':
2240
+ return 'warning';
2241
+ case 'error':
2242
+ return 'error';
2243
+ case 'disabled':
2244
+ return 'block';
2245
+ default:
2246
+ return '';
2247
+ }
2248
+ }
2249
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: StatusButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2250
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "18.1.0", type: StatusButtonComponent, isStandalone: true, selector: "lib-status-button", inputs: { label: "label", status: "status", leftIcon: "leftIcon", rightIcon: "rightIcon", hideLeftIcon: ["hideLeftIcon", "hideLeftIcon", booleanAttribute], hideRightIcon: ["hideRightIcon", "hideRightIcon", booleanAttribute], clearVariant: ["clearVariant", "clearVariant", booleanAttribute] }, ngImport: i0, template: "<button\n type=\"button\"\n [ngClass]=\"[\n status,\n clearVariant ? 'strokeless transparent-bg' : ''\n ]\"\n>\n <span *ngIf=\"leftIcon\" class=\"icon material-symbols-outlined\">{{ leftIcon }}</span>\n {{ label }}\n <span *ngIf=\"rightIcon\" class=\"icon material-symbols-outlined\">{{ rightIcon }}</span>\n</button>\n", styles: ["button{align-items:center;background-color:#ecf2f6;border:1px solid #457fa6;border-radius:4px;color:#3e7295;display:inline-flex;flex-shrink:0;font-size:1em;font-weight:400;gap:.5em;height:2.25em;justify-content:center;padding:var(--status-button-padding, .25em .5em)}button .icon{font-size:1.25em}button:focus{outline:2px solid #b967c7;outline-offset:2px}button:hover{background-color:#a2bfd3;color:#26485f;cursor:pointer}button.success{background-color:#ebf6ee;border:1px solid #3ba35a;color:#1d562e}button.success:hover{background-color:#9dd1ac;color:#1d562e}button.info{background-color:#ecf2f6;border:1px solid #457fa6;color:#3e7295}button.info:hover{background-color:#a2bfd3;color:#26485f}button.warning{background-color:#faf9ec;border:1px solid #d1c844;color:#635f04}button.warning:hover{background-color:#e8e3a1;color:#635f04}button.error{background-color:#f9ecec;border:1px solid #c73e3d;color:#702121}button.error:hover{background-color:#e39e9e;color:#702121}button.disabled{background-color:#e7e7e7;border:1px solid #8f8f8f;color:#767676}button.disabled:hover{background-color:#d0d0d0;color:#141414}button.strokeless{border:0px}button.transparent-bg{background:transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
2251
+ }
2252
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: StatusButtonComponent, decorators: [{
2253
+ type: Component,
2254
+ args: [{ selector: 'lib-status-button', standalone: true, imports: [CommonModule, NgIf], template: "<button\n type=\"button\"\n [ngClass]=\"[\n status,\n clearVariant ? 'strokeless transparent-bg' : ''\n ]\"\n>\n <span *ngIf=\"leftIcon\" class=\"icon material-symbols-outlined\">{{ leftIcon }}</span>\n {{ label }}\n <span *ngIf=\"rightIcon\" class=\"icon material-symbols-outlined\">{{ rightIcon }}</span>\n</button>\n", styles: ["button{align-items:center;background-color:#ecf2f6;border:1px solid #457fa6;border-radius:4px;color:#3e7295;display:inline-flex;flex-shrink:0;font-size:1em;font-weight:400;gap:.5em;height:2.25em;justify-content:center;padding:var(--status-button-padding, .25em .5em)}button .icon{font-size:1.25em}button:focus{outline:2px solid #b967c7;outline-offset:2px}button:hover{background-color:#a2bfd3;color:#26485f;cursor:pointer}button.success{background-color:#ebf6ee;border:1px solid #3ba35a;color:#1d562e}button.success:hover{background-color:#9dd1ac;color:#1d562e}button.info{background-color:#ecf2f6;border:1px solid #457fa6;color:#3e7295}button.info:hover{background-color:#a2bfd3;color:#26485f}button.warning{background-color:#faf9ec;border:1px solid #d1c844;color:#635f04}button.warning:hover{background-color:#e8e3a1;color:#635f04}button.error{background-color:#f9ecec;border:1px solid #c73e3d;color:#702121}button.error:hover{background-color:#e39e9e;color:#702121}button.disabled{background-color:#e7e7e7;border:1px solid #8f8f8f;color:#767676}button.disabled:hover{background-color:#d0d0d0;color:#141414}button.strokeless{border:0px}button.transparent-bg{background:transparent}\n"] }]
2255
+ }], propDecorators: { label: [{
2256
+ type: Input,
2257
+ args: [{ required: true }]
2258
+ }], status: [{
2259
+ type: Input
2260
+ }], leftIcon: [{
2261
+ type: Input
2262
+ }], rightIcon: [{
2263
+ type: Input
2264
+ }], hideLeftIcon: [{
2265
+ type: Input,
2266
+ args: [{ transform: booleanAttribute }]
2267
+ }], hideRightIcon: [{
2268
+ type: Input,
2269
+ args: [{ transform: booleanAttribute }]
2270
+ }], clearVariant: [{
2271
+ type: Input,
2272
+ args: [{ transform: booleanAttribute }]
2273
+ }] } });
2274
+
2275
+ const purposeToStatusMap = {
2276
+ default: 'info',
2277
+ info: 'info',
2278
+ success: 'success',
2279
+ error: 'error',
2280
+ warning: 'warning',
2281
+ };
2282
+ class SnackbarComponent {
2283
+ constructor() {
2284
+ this.purpose = 'default';
2285
+ this.title = 'Snackbar Title';
2286
+ this.description = 'Lorem ipsum';
2287
+ this.fieldPlaceholder = 'Placeholder';
2288
+ this.titleIcon = 'info';
2289
+ this.buttonLabel = 'Done';
2290
+ this.showTitle = true;
2291
+ this.showTitleIcon = true;
2292
+ this.showClose = true;
2293
+ this.showProgressBar = true;
2294
+ this.showField = true;
2295
+ this.showButton = true;
2296
+ this.autoDismiss = true;
2297
+ this.visible = true;
2298
+ this.progressMs = 4000;
2299
+ this.y = 24;
2300
+ this.closed = new EventEmitter();
2301
+ this.visibleChange = new EventEmitter();
2302
+ this.isOpen = false;
2303
+ this.EXIT_MS = 300;
2304
+ }
2305
+ ngOnInit() {
2306
+ if (this.visible) {
2307
+ // enter on next tick
2308
+ setTimeout(() => (this.isOpen = true), 0);
2309
+ this.startDismissTimer();
2310
+ }
2311
+ }
2312
+ ngOnChanges(changes) {
2313
+ if ('visible' in changes) {
2314
+ // when parent toggles visible
2315
+ if (this.visible) {
2316
+ // becoming visible -> open + (re)start timer
2317
+ this.isOpen = false;
2318
+ setTimeout(() => (this.isOpen = true), 0);
2319
+ this.startDismissTimer();
2320
+ }
2321
+ else {
2322
+ // becoming hidden -> stop timers and ensure closed state
2323
+ this.clearDismissTimer();
2324
+ this.isOpen = false;
2325
+ }
2326
+ }
2327
+ }
2328
+ ngOnDestroy() {
2329
+ this.clearDismissTimer();
2330
+ }
2331
+ // used by the × button, done button, and auto-dismiss
2332
+ close() {
2333
+ if (!this.isOpen && !this.visible)
2334
+ return;
2335
+ this.clearDismissTimer();
2336
+ this.isOpen = false; // triggers exit transition
2337
+ // after exit animation, hide + notify
2338
+ window.setTimeout(() => {
2339
+ if (this.visible) {
2340
+ this.visible = false;
2341
+ this.visibleChange.emit(false);
2342
+ }
2343
+ this.closed.emit();
2344
+ }, this.EXIT_MS);
2345
+ }
2346
+ get status() {
2347
+ return purposeToStatusMap[this.purpose];
2348
+ }
2349
+ startDismissTimer() {
2350
+ if (!this.autoDismiss)
2351
+ return;
2352
+ this.clearDismissTimer();
2353
+ this.dismissTimer = window.setTimeout(() => this.close(), this.progressMs);
2354
+ }
2355
+ clearDismissTimer() {
2356
+ if (this.dismissTimer) {
2357
+ clearTimeout(this.dismissTimer);
2358
+ this.dismissTimer = undefined;
2359
+ }
2360
+ }
2361
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: SnackbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2362
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.0", type: SnackbarComponent, isStandalone: true, selector: "lib-snackbar", inputs: { purpose: "purpose", title: "title", description: "description", fieldPlaceholder: "fieldPlaceholder", titleIcon: "titleIcon", buttonLabel: "buttonLabel", showTitle: ["showTitle", "showTitle", booleanAttribute], showTitleIcon: ["showTitleIcon", "showTitleIcon", booleanAttribute], showClose: ["showClose", "showClose", booleanAttribute], showProgressBar: ["showProgressBar", "showProgressBar", booleanAttribute], showField: ["showField", "showField", booleanAttribute], showButton: ["showButton", "showButton", booleanAttribute], autoDismiss: ["autoDismiss", "autoDismiss", booleanAttribute], visible: ["visible", "visible", booleanAttribute], progressMs: "progressMs", y: "y" }, outputs: { closed: "closed", visibleChange: "visibleChange" }, usesOnChanges: true, ngImport: i0, template: "@if (visible) {\n <section\n class=\"snackbar\"\n [ngClass]=\"{\n 'is-default': purpose === 'default',\n 'is-info': purpose === 'info',\n 'is-success': purpose === 'success',\n 'is-error': purpose === 'error',\n 'is-warning': purpose === 'warning',\n 'is-open': isOpen,\n }\"\n role=\"status\"\n aria-live=\"polite\"\n [style.--snackbar-progress-duration]=\"progressMs + 'ms'\"\n [style.top.px]=\"y\"\n >\n <div class=\"snackbar__left\">\n @if(showTitle) {\n <div class=\"snackbar__title-row\">\n <span \n [ngClass]=\"{\n 'snackbar__title-icon': true,\n 'has-icon': showTitle && showTitleIcon\n }\"\n aria-hidden=\"true\"\n >\n @if (showTitle && showTitleIcon) {\n <i class=\"material-icons\" id=\"snackbar-title-icon\" aria-hidden=\"true\">{{ titleIcon }}</i>\n }\n </span>\n @if (showTitle) {\n <h3 class=\"snackbar__title\">{{ title }}</h3>\n }\n </div>\n }\n <p class=\"snackbar__desc\">\n {{ description }}\n </p>\n @if (showField) {\n <input class=\"snackbar__input\" type=\"text\" [placeholder]=\"fieldPlaceholder\" />\n }\n @if (showButton) {\n <lib-status-button\n label={{buttonLabel}}\n [status]=\"status\"\n [clearVariant]=\"true\"\n (click)=\"close()\"\n ></lib-status-button>\n\n }\n </div>\n <button \n type=\"button\"\n (click)=\"close()\"\n class=\"snackbar__close\"\n aria-label=\"Dismiss\"\n [style.visibility]=\"showClose ? 'visible' : 'hidden'\"\n >\n \u00D7\n </button>\n @if (showProgressBar) {\n <div class=\"snackbar__progress\">\n <div class=\"snackbar__progress-fill\"></div>\n </div>\n }\n </section>\n}\n", styles: [":root{--border-radius-md: 4px;--colors-border-primary: #d0d0d0;--colors-surface-primary: white;--colors-border-information: #457fa6;--colors-surface-information: #ecf2f6;--colors-border-success: #3ba35a;--colors-surface-success: #ebf6ee;--colors-border-warning: #d1c844;--colors-surface-warning: #faf9ec;--colors-border-error: #c73e3d;--colors-surface-error: #f9ecec}.snackbar{--snackbar-border-color: var(--colors-border-primary);--snackbar-bg: var(--colors-surface-primary);--snackbar-text: #141414;--snackbar-title-color: var(--snackbar-text);--snackbar-icon-bg: #e5edf8;--snackbar-accent: #0047ba;--snackbar-input-border: var(--snackbar-border-color);--snackbar-progress: var(--snackbar-accent);display:flex;width:26.25rem;padding:1.25rem 1rem;justify-content:space-between;align-items:flex-start;gap:.25rem;position:relative;border-radius:var(--border-radius-md, 4px);border:1px solid var(--snackbar-border-color);background:var(--snackbar-bg);box-shadow:1px 1px 4px #00000040;color:var(--snackbar-text)}.snackbar__left{display:grid;gap:.5rem;flex:1 1 auto}.snackbar__left>.snackbar__title-row~*{margin-left:var(--snackbar-right-gutter, 1.85rem)}.snackbar__title-row{display:flex;align-items:center;gap:.25rem}.snackbar__title{margin:0;font-weight:400;font-size:1.2rem;line-height:1.2;color:var(--snackbar-title-color)}.snackbar__title-icon{display:inline-flex;align-items:center;justify-content:center;width:1.5em;height:1.5em}.snackbar__title-icon.has-icon{border-radius:50%;background:var(--snackbar-icon-bg);color:var(--snackbar-accent)}.snackbar__icon{font-weight:700;font-size:1rem;line-height:1}.snackbar__desc{margin:0;opacity:.9}.snackbar__input{width:100%;max-width:100%;box-sizing:border-box;padding:.5em;border-radius:4px;border:1px solid var(--snackbar-input-border);background:#fff;color:inherit}.snackbar__close{margin-left:.5em;border:0;background:transparent;color:inherit;opacity:.7;font-size:1.5rem;padding:.25rem;line-height:1;cursor:pointer}.snackbar__progress{position:absolute;left:0;right:0;bottom:-1px;height:4px;overflow:hidden;border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.snackbar__progress-fill{height:100%;width:0%;background:var(--snackbar-progress);animation:fill var(--snackbar-progress-duration, 4s) linear forwards}@keyframes fill{to{width:100%}}.snackbar.is-default{--snackbar-border-color: var(--colors-border-primary, #d0d0d0);--snackbar-bg: var(--colors-surface-primary, white);--snackbar-text: #141414;--snackbar-title-color: #003995;--snackbar-icon-bg: #e5edf8;--snackbar-accent: #003995;--snackbar-input-border: #d0d0d0;--snackbar-progress: #003995}.snackbar.is-info{--snackbar-border-color: var(--colors-border-information, #457fa6);--snackbar-bg: var(--colors-surface-information, #ecf2f6);--snackbar-text: #24495c;--snackbar-title-color: #24495c;--snackbar-icon-bg: #a2bfd3;--snackbar-accent: #306a88;--snackbar-input-border: #a2bfd3;--snackbar-progress: #306a88}.snackbar.is-success{--snackbar-border-color: var(--colors-border-success, #3ba35a);--snackbar-bg: var(--colors-surface-success, #ebf6ee);--snackbar-text: #20522e;--snackbar-title-color: #20522e;--snackbar-icon-bg: #9dd1ac;--snackbar-accent: #2e7d3f;--snackbar-input-border: #9dd1ac;--snackbar-progress: #2e7d3f}.snackbar.is-warning{--snackbar-border-color: var(--colors-border-warning, #d1c844);--snackbar-bg: var(--colors-surface-warning, #faf9ec);--snackbar-text: #514a18;--snackbar-title-color: #514a18;--snackbar-icon-bg: #e8e3a1;--snackbar-accent: #7a6f13;--snackbar-input-border: #d1c844;--snackbar-progress: #7a6f13}.snackbar.is-error{--snackbar-border-color: var(--colors-border-error, #c73e3d);--snackbar-bg: var(--colors-surface-error, #f9ecec);--snackbar-text: #611a1a;--snackbar-title-color: #611a1a;--snackbar-icon-bg: #e39e9e;--snackbar-accent: #912525;--snackbar-input-border: #c73e3d;--snackbar-progress: #912525}.snackbar{position:fixed;top:24px;right:24px;z-index:1000;transform:translate(120%);transition:transform .6s ease}.snackbar.is-open{transform:translate(0)}lib-status-button{--status-button-padding: .25em .5em .25em 0}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: StatusButtonComponent, selector: "lib-status-button", inputs: ["label", "status", "leftIcon", "rightIcon", "hideLeftIcon", "hideRightIcon", "clearVariant"] }] }); }
2363
+ }
2364
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: SnackbarComponent, decorators: [{
2365
+ type: Component,
2366
+ args: [{ selector: 'lib-snackbar', standalone: true, imports: [NgClass, StatusButtonComponent], template: "@if (visible) {\n <section\n class=\"snackbar\"\n [ngClass]=\"{\n 'is-default': purpose === 'default',\n 'is-info': purpose === 'info',\n 'is-success': purpose === 'success',\n 'is-error': purpose === 'error',\n 'is-warning': purpose === 'warning',\n 'is-open': isOpen,\n }\"\n role=\"status\"\n aria-live=\"polite\"\n [style.--snackbar-progress-duration]=\"progressMs + 'ms'\"\n [style.top.px]=\"y\"\n >\n <div class=\"snackbar__left\">\n @if(showTitle) {\n <div class=\"snackbar__title-row\">\n <span \n [ngClass]=\"{\n 'snackbar__title-icon': true,\n 'has-icon': showTitle && showTitleIcon\n }\"\n aria-hidden=\"true\"\n >\n @if (showTitle && showTitleIcon) {\n <i class=\"material-icons\" id=\"snackbar-title-icon\" aria-hidden=\"true\">{{ titleIcon }}</i>\n }\n </span>\n @if (showTitle) {\n <h3 class=\"snackbar__title\">{{ title }}</h3>\n }\n </div>\n }\n <p class=\"snackbar__desc\">\n {{ description }}\n </p>\n @if (showField) {\n <input class=\"snackbar__input\" type=\"text\" [placeholder]=\"fieldPlaceholder\" />\n }\n @if (showButton) {\n <lib-status-button\n label={{buttonLabel}}\n [status]=\"status\"\n [clearVariant]=\"true\"\n (click)=\"close()\"\n ></lib-status-button>\n\n }\n </div>\n <button \n type=\"button\"\n (click)=\"close()\"\n class=\"snackbar__close\"\n aria-label=\"Dismiss\"\n [style.visibility]=\"showClose ? 'visible' : 'hidden'\"\n >\n \u00D7\n </button>\n @if (showProgressBar) {\n <div class=\"snackbar__progress\">\n <div class=\"snackbar__progress-fill\"></div>\n </div>\n }\n </section>\n}\n", styles: [":root{--border-radius-md: 4px;--colors-border-primary: #d0d0d0;--colors-surface-primary: white;--colors-border-information: #457fa6;--colors-surface-information: #ecf2f6;--colors-border-success: #3ba35a;--colors-surface-success: #ebf6ee;--colors-border-warning: #d1c844;--colors-surface-warning: #faf9ec;--colors-border-error: #c73e3d;--colors-surface-error: #f9ecec}.snackbar{--snackbar-border-color: var(--colors-border-primary);--snackbar-bg: var(--colors-surface-primary);--snackbar-text: #141414;--snackbar-title-color: var(--snackbar-text);--snackbar-icon-bg: #e5edf8;--snackbar-accent: #0047ba;--snackbar-input-border: var(--snackbar-border-color);--snackbar-progress: var(--snackbar-accent);display:flex;width:26.25rem;padding:1.25rem 1rem;justify-content:space-between;align-items:flex-start;gap:.25rem;position:relative;border-radius:var(--border-radius-md, 4px);border:1px solid var(--snackbar-border-color);background:var(--snackbar-bg);box-shadow:1px 1px 4px #00000040;color:var(--snackbar-text)}.snackbar__left{display:grid;gap:.5rem;flex:1 1 auto}.snackbar__left>.snackbar__title-row~*{margin-left:var(--snackbar-right-gutter, 1.85rem)}.snackbar__title-row{display:flex;align-items:center;gap:.25rem}.snackbar__title{margin:0;font-weight:400;font-size:1.2rem;line-height:1.2;color:var(--snackbar-title-color)}.snackbar__title-icon{display:inline-flex;align-items:center;justify-content:center;width:1.5em;height:1.5em}.snackbar__title-icon.has-icon{border-radius:50%;background:var(--snackbar-icon-bg);color:var(--snackbar-accent)}.snackbar__icon{font-weight:700;font-size:1rem;line-height:1}.snackbar__desc{margin:0;opacity:.9}.snackbar__input{width:100%;max-width:100%;box-sizing:border-box;padding:.5em;border-radius:4px;border:1px solid var(--snackbar-input-border);background:#fff;color:inherit}.snackbar__close{margin-left:.5em;border:0;background:transparent;color:inherit;opacity:.7;font-size:1.5rem;padding:.25rem;line-height:1;cursor:pointer}.snackbar__progress{position:absolute;left:0;right:0;bottom:-1px;height:4px;overflow:hidden;border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.snackbar__progress-fill{height:100%;width:0%;background:var(--snackbar-progress);animation:fill var(--snackbar-progress-duration, 4s) linear forwards}@keyframes fill{to{width:100%}}.snackbar.is-default{--snackbar-border-color: var(--colors-border-primary, #d0d0d0);--snackbar-bg: var(--colors-surface-primary, white);--snackbar-text: #141414;--snackbar-title-color: #003995;--snackbar-icon-bg: #e5edf8;--snackbar-accent: #003995;--snackbar-input-border: #d0d0d0;--snackbar-progress: #003995}.snackbar.is-info{--snackbar-border-color: var(--colors-border-information, #457fa6);--snackbar-bg: var(--colors-surface-information, #ecf2f6);--snackbar-text: #24495c;--snackbar-title-color: #24495c;--snackbar-icon-bg: #a2bfd3;--snackbar-accent: #306a88;--snackbar-input-border: #a2bfd3;--snackbar-progress: #306a88}.snackbar.is-success{--snackbar-border-color: var(--colors-border-success, #3ba35a);--snackbar-bg: var(--colors-surface-success, #ebf6ee);--snackbar-text: #20522e;--snackbar-title-color: #20522e;--snackbar-icon-bg: #9dd1ac;--snackbar-accent: #2e7d3f;--snackbar-input-border: #9dd1ac;--snackbar-progress: #2e7d3f}.snackbar.is-warning{--snackbar-border-color: var(--colors-border-warning, #d1c844);--snackbar-bg: var(--colors-surface-warning, #faf9ec);--snackbar-text: #514a18;--snackbar-title-color: #514a18;--snackbar-icon-bg: #e8e3a1;--snackbar-accent: #7a6f13;--snackbar-input-border: #d1c844;--snackbar-progress: #7a6f13}.snackbar.is-error{--snackbar-border-color: var(--colors-border-error, #c73e3d);--snackbar-bg: var(--colors-surface-error, #f9ecec);--snackbar-text: #611a1a;--snackbar-title-color: #611a1a;--snackbar-icon-bg: #e39e9e;--snackbar-accent: #912525;--snackbar-input-border: #c73e3d;--snackbar-progress: #912525}.snackbar{position:fixed;top:24px;right:24px;z-index:1000;transform:translate(120%);transition:transform .6s ease}.snackbar.is-open{transform:translate(0)}lib-status-button{--status-button-padding: .25em .5em .25em 0}\n"] }]
2367
+ }], propDecorators: { purpose: [{
2368
+ type: Input
2369
+ }], title: [{
2370
+ type: Input
2371
+ }], description: [{
2372
+ type: Input
2373
+ }], fieldPlaceholder: [{
2374
+ type: Input
2375
+ }], titleIcon: [{
2376
+ type: Input
2377
+ }], buttonLabel: [{
2378
+ type: Input
2379
+ }], showTitle: [{
2380
+ type: Input,
2381
+ args: [{ transform: booleanAttribute }]
2382
+ }], showTitleIcon: [{
2383
+ type: Input,
2384
+ args: [{ transform: booleanAttribute }]
2385
+ }], showClose: [{
2386
+ type: Input,
2387
+ args: [{ transform: booleanAttribute }]
2388
+ }], showProgressBar: [{
2389
+ type: Input,
2390
+ args: [{ transform: booleanAttribute }]
2391
+ }], showField: [{
2392
+ type: Input,
2393
+ args: [{ transform: booleanAttribute }]
2394
+ }], showButton: [{
2395
+ type: Input,
2396
+ args: [{ transform: booleanAttribute }]
2397
+ }], autoDismiss: [{
2398
+ type: Input,
2399
+ args: [{ transform: booleanAttribute }]
2400
+ }], visible: [{
2401
+ type: Input,
2402
+ args: [{ transform: booleanAttribute }]
2403
+ }], progressMs: [{
2404
+ type: Input
2405
+ }], y: [{
2406
+ type: Input
2407
+ }], closed: [{
2408
+ type: Output
2409
+ }], visibleChange: [{
2410
+ type: Output
2411
+ }] } });
2412
+
2413
+ class SnackbarService {
2414
+ constructor(appRef, env) {
2415
+ this.appRef = appRef;
2416
+ this.env = env;
2417
+ }
2418
+ /**
2419
+ * This open method is what consuming code will use to create a snackbar
2420
+ * @param snackbarOptions interface found above
2421
+ * @param anchor HTML Element, optional. The snackbar's entrance and exit animations will happen on this element's Y axis (px)
2422
+ * @returns void.
2423
+ */
2424
+ open(snackbarOptions = {}, anchor) {
2425
+ const compRef = createComponent(SnackbarComponent, {
2426
+ environmentInjector: this.env,
2427
+ });
2428
+ const desiredY = anchor // could be clamped if above or below viewport
2429
+ ? anchor.getBoundingClientRect().top
2430
+ : snackbarOptions.y ?? 24;
2431
+ Object.assign(compRef.instance, {
2432
+ purpose: 'warning',
2433
+ title: 'Snackbar Title',
2434
+ description: 'Lorem ipsum is the standard dummy text in the design industry',
2435
+ titleIcon: 'info',
2436
+ fieldPlaceholder: 'Placeholder',
2437
+ showTitle: true,
2438
+ showTitleIcon: true,
2439
+ showClose: true,
2440
+ showProgressBar: true,
2441
+ showField: true,
2442
+ showButton: true,
2443
+ progressMs: 4000,
2444
+ autoDismiss: true,
2445
+ y: desiredY,
2446
+ right: 24,
2447
+ zIndex: 10000,
2448
+ visible: true,
2449
+ ...snackbarOptions,
2450
+ });
2451
+ this.appRef.attachView(compRef.hostView);
2452
+ document.body.appendChild(compRef.location.nativeElement);
2453
+ compRef.changeDetectorRef.detectChanges();
2454
+ // Clamp Y after it’s in the DOM
2455
+ const margin = 8;
2456
+ const adjustY = () => {
2457
+ const host = compRef.location.nativeElement.querySelector('section.snackbar') ??
2458
+ compRef.location.nativeElement;
2459
+ const h = host.offsetHeight || 0;
2460
+ const maxTop = Math.max(margin, window.innerHeight - h - margin);
2461
+ const clamped = Math.min(Math.max(desiredY, margin), maxTop);
2462
+ if (compRef.instance.y !== clamped) {
2463
+ compRef.instance.y = clamped;
2464
+ compRef.changeDetectorRef.detectChanges();
2465
+ }
2466
+ };
2467
+ // Measure on next frame (styles applied)
2468
+ const rafId = requestAnimationFrame(adjustY);
2469
+ // re-clamp on resize (in case viewport changes while visible)
2470
+ const onResize = () => adjustY();
2471
+ window.addEventListener('resize', onResize);
2472
+ // Cleanup when closed
2473
+ const sub = compRef.instance.closed.subscribe(() => {
2474
+ sub.unsubscribe();
2475
+ cancelAnimationFrame(rafId);
2476
+ window.removeEventListener('resize', onResize);
2477
+ this.appRef.detachView(compRef.hostView);
2478
+ compRef.destroy();
2479
+ });
2480
+ return {
2481
+ close: () => compRef.instance.close(),
2482
+ };
2483
+ }
2484
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: SnackbarService, deps: [{ token: i0.ApplicationRef }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable }); }
2485
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: SnackbarService, providedIn: 'root' }); }
2486
+ }
2487
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.0", ngImport: i0, type: SnackbarService, decorators: [{
2488
+ type: Injectable,
2489
+ args: [{ providedIn: 'root' }]
2490
+ }], ctorParameters: () => [{ type: i0.ApplicationRef }, { type: i0.EnvironmentInjector }] });
2491
+
2175
2492
  /*
2176
2493
  * Public API Surface of components
2177
2494
  */
@@ -2180,5 +2497,5 @@ const isSearchScope = (scope) => searchScopeValues.includes(scope);
2180
2497
  * Generated bundle index. Do not edit.
2181
2498
  */
2182
2499
 
2183
- export { ADVANCED_SEARCH_FIELD_MAP, ADVANCED_SEARCH_OPTIONS, ADVANCED_SEARCH_QUALIFIER_MAP, HbllFooterComponent, HbllHeaderComponent, HbllItemTypeIconPipe, HeaderWithImpersonationComponent, ImpersonateModalComponent, ImpersonateUserPipe, ImpersonationBannerComponent, LIBRARY_HOURS_API_URL, SsSearchBarComponent, defaultOidcBaseUri, defaultOidcDefaultIdp, getUserStatusFromRoles, isAdvancedSearchExternalFieldOption, isAdvancedSearchFieldOption, isAdvancedSearchLocalFieldOption, isSearchScope };
2500
+ 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 };
2184
2501
  //# sourceMappingURL=byuhbll-components.mjs.map