UNPKG

@ux-aspects/ux-aspects

Version:

Open source user interface framework for building modern, responsive, mobile big data applications

1,096 lines (1,070 loc) 1.83 MB
import { Observable, BehaviorSubject, Subject, ReplaySubject, merge, of, combineLatest, timer, fromEvent, Subscription, from, isObservable, concat } from 'rxjs'; import * as i0 from '@angular/core'; import { Directive, InjectionToken, inject, Injectable, RendererFactory2, ElementRef, NgZone, EventEmitter, Output, Input, HostBinding, Component, NgModule, Renderer2, PLATFORM_ID, HostListener, ContentChildren, ChangeDetectorRef, QueryList, ChangeDetectionStrategy, ContentChild, TemplateRef, ViewChild, ViewContainerRef, forwardRef, LOCALE_ID, Pipe, ViewEncapsulation, DestroyRef, ViewChildren, SecurityContext, HostAttributeToken, Injector, ApplicationRef, ComponentFactoryResolver, DOCUMENT, IterableDiffers } from '@angular/core'; import { takeUntil, filter, debounceTime, map, switchMap, take, pairwise, distinctUntilChanged, first, tap, delay, combineLatest as combineLatest$1, auditTime, mergeMap, skip, withLatestFrom, startWith } from 'rxjs/operators'; import { coerceBooleanProperty, coerceNumberProperty, coerceArray, coerceCssPixelValue } from '@angular/cdk/coercion'; import { FocusMonitor, FocusKeyManager, A11yModule, CdkMonitorFocus, CdkTrapFocus, LiveAnnouncer } from '@angular/cdk/a11y'; import { isPlatformBrowser, CommonModule, AsyncPipe, NgClass, NgTemplateOutlet, formatDate, WeekDay, DatePipe, JsonPipe, DecimalPipe, LocationStrategy, isPlatformServer } from '@angular/common'; import { Platform, PlatformModule } from '@angular/cdk/platform'; import { SplitComponent, SplitAreaComponent, AngularSplitModule } from 'angular-split'; import { END, HOME, DOWN_ARROW, RIGHT_ARROW, UP_ARROW, LEFT_ARROW, TAB, ENTER, SPACE, ESCAPE, DELETE, BACKSPACE, PAGE_DOWN, PAGE_UP } from '@angular/cdk/keycodes'; import { RouterLink, RouterModule, Router, NavigationEnd, ActivatedRoute, NavigationCancel, NavigationError } from '@angular/router'; import * as i1 from '@angular/forms'; import { NG_VALUE_ACCESSOR, FormsModule, FormGroupDirective, NG_VALIDATORS } from '@angular/forms'; import { trigger, transition, style, animate, query, stagger, state } from '@angular/animations'; import { Overlay, OverlayModule, ScrollDispatcher } from '@angular/cdk/overlay'; import { TemplatePortal, ComponentPortal, DomPortalOutlet } from '@angular/cdk/portal'; import { CdkObserveContent, ObserversModule as ObserversModule$1 } from '@angular/cdk/observers'; import { ScrollDispatcher as ScrollDispatcher$1, ViewportRuler } from '@angular/cdk/scrolling'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { moveItemInArray, CdkDropListGroup, CdkDropList, CdkDrag, CdkDragHandle, CDK_DRAG_HANDLE, CDK_DRAG_PARENT, transferArrayItem, CDK_DROP_LIST, DragDropModule } from '@angular/cdk/drag-drop'; import { DomSanitizer } from '@angular/platform-browser'; import { HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { select, transition as transition$1, easeCubic, pointer, interpolate, arc, zoom, zoomTransform, hierarchy, tree, linkVertical, scaleLinear, partition, sum } from 'd3'; import { ArrayDataSource } from '@angular/cdk/collections'; import { FlatTreeControl, CdkTree, CdkTreeNodeDef, CdkTreeNode, CdkTreeModule } from '@angular/cdk/tree'; import { AutofillMonitor } from '@angular/cdk/text-field'; var Color; (function (Color) { Color["Primary"] = "primary"; Color["Accent"] = "accent"; Color["Secondary"] = "secondary"; Color["Alternate1"] = "alternate1"; Color["Alternate2"] = "alternate2"; Color["Alternate3"] = "alternate3"; Color["Vibrant1"] = "vibrant1"; Color["Vibrant2"] = "vibrant2"; Color["Grey1"] = "grey1"; Color["Grey2"] = "grey2"; Color["Grey3"] = "grey3"; Color["Grey4"] = "grey4"; Color["Grey5"] = "grey5"; Color["Grey6"] = "grey6"; Color["Grey7"] = "grey7"; Color["Grey8"] = "grey8"; Color["Chart1"] = "chart1"; Color["Chart2"] = "chart2"; Color["Chart3"] = "chart3"; Color["Chart4"] = "chart4"; Color["Chart5"] = "chart5"; Color["Chart6"] = "chart6"; Color["Ok"] = "ok"; Color["Warning"] = "warning"; Color["Critical"] = "critical"; Color["Partition1"] = "partition1"; Color["Partition9"] = "partition9"; Color["Partition10"] = "partition10"; Color["Partition11"] = "partition11"; Color["Partition12"] = "partition12"; Color["Partition13"] = "partition13"; Color["Partition14"] = "partition14"; Color["SocialChartNode"] = "social-chart-node"; Color["SocialChartEdge"] = "social-chart-edge"; })(Color || (Color = {})); /** * Determine the type of icon based upon the identifier. * * We support the following iconset: * * - `ux-icon` - UX Icon Set * - `component` - Component icon not tied to a specific set * * @param identifier - The name of the icon */ function getIconType(identifier) { if (identifier && identifier.trim().indexOf('ux-') === 0) { return IconType.UxIcon; } return IconType.Component; } var IconType; (function (IconType) { IconType["UxIcon"] = "ux-icon"; IconType["Component"] = "component"; })(IconType || (IconType = {})); /** * This is a simple RxJS operator to allow us to avoid the * "expression has changed after it was checked issue" * by making the subscription asynchronous. We could just use a * delay operator but this uses a timeout which is significantly * slower than using requestAnimationFrame. */ const tick = () => (source) => new Observable(subscriber => { source.subscribe({ next(value) { requestAnimationFrame(() => subscriber.next(value)); }, error(err) { subscriber.error(err); }, complete() { subscriber.complete(); }, }); }); /** * A button will trigger a click event whenever the a mouse click occurs or the enter key is pressed. * These functions can be used to identify if a `click` event was caused by the keyboard or * by a mouse. * * The `event.detail` property will change based on the source of the event. * A mouse click will have varying values based on the browser, however * the enter key will always have a value of `0` so we can check against that */ function isKeyboardTrigger(event) { return event.detail === 0; } function isMouseTrigger(event) { return !isKeyboardTrigger(event); } class AccordionPanelHeadingDirective { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionPanelHeadingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: AccordionPanelHeadingDirective, isStandalone: true, selector: "ux-accordion-panel-header", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionPanelHeadingDirective, decorators: [{ type: Directive, args: [{ selector: 'ux-accordion-panel-header' }] }] }); const ACCESSIBILITY_OPTIONS_TOKEN = new InjectionToken('ACCESSIBILITY_OPTIONS'); class AccessibilityOptionsService { constructor() { /** Get the user specified options - but handle cases where they may not be specified */ this._options = inject(ACCESSIBILITY_OPTIONS_TOKEN, { optional: true }); /** Determine the default options */ this._defaultOptions = { mouseFocusIndicator: false, touchFocusIndicator: false, keyboardFocusIndicator: true, programmaticFocusIndicator: false, }; } /** Get the complete options populating unspecified options with the default values */ get options() { return { ...this._defaultOptions, ...this._options }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccessibilityOptionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccessibilityOptionsService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccessibilityOptionsService, decorators: [{ type: Injectable }] }); class LocalFocusIndicatorOptions { } class FocusIndicator { /** Apply a class when the item is focused */ set isFocused(isFocused) { // update the class on the element isFocused ? this._renderer.addClass(this._element, 'ux-focus-indicator-active') : this._renderer.removeClass(this._element, 'ux-focus-indicator-active'); // emit the focus state this.isFocused$.next(isFocused); } /** Provide a convenience getter to allow access to focus state without a subscription */ get isFocused() { return this.isFocused$.value; } constructor(_element, _focusMonitor, _renderer, _options, _focusIndicatorOrigin) { this._element = _element; this._focusMonitor = _focusMonitor; this._renderer = _renderer; this._options = _options; this._focusIndicatorOrigin = _focusIndicatorOrigin; /** An observable to monitor the focus state */ this.isFocused$ = new BehaviorSubject(false); /** An observable to monitor the focus origin */ this.origin$ = new Subject(); /** Remove all subscriptions on destroy */ this._onDestroy = new Subject(); // check if the element is already being monitored if (!_element.classList.contains('ux-focus-indicator')) { this.initialise(); } } /** Setup the focus monitoring */ initialise() { // add a class to the element to specify we are controlling the focus this._renderer.addClass(this._element, 'ux-focus-indicator'); // watch for any changes to the focus state this._focusMonitor .monitor(this._element, this._options.checkChildren) .pipe(takeUntil(this._onDestroy)) .subscribe(this.onFocusChange.bind(this)); } /** Focus the element with a specific origin */ focus(origin, options) { this._focusIndicatorOrigin.setOrigin(origin); this._element.focus(options); } /** Tear down the subscriptions */ destroy() { this._onDestroy.next(); this._onDestroy.complete(); this.isFocused$.complete(); this._focusMonitor.stopMonitoring(this._element); } /** Allow the options to be updates */ setOptions(options) { this._options = { ...this._options, ...options }; } /** Monitor changes to an elements focus state */ onFocusChange(origin) { // if the origin is null then we blurred if (origin === null) { this.isFocused = false; this.origin$.next(null); return; } // get the origin if there is one const syntheticOrigin = this._focusIndicatorOrigin.getOrigin(); // emit the origin this.origin$.next(syntheticOrigin || origin); switch (syntheticOrigin || origin) { case 'mouse': this.isFocused = this._options.mouseFocusIndicator; break; case 'touch': this.isFocused = this._options.touchFocusIndicator; break; case 'keyboard': this.isFocused = this._options.keyboardFocusIndicator; break; case 'program': this.isFocused = this._options.programmaticFocusIndicator; break; default: this.isFocused = false; } } } class FocusIndicatorOriginService { /** Store the event source origin */ setOrigin(origin) { this._origin = origin; } /** Get the most recent event origin */ getOrigin() { // get the most recent origin if there is one const origin = this._origin; // we should clear the origin so this value doesn't cause issues with future focus events this._origin = null; return origin; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorOriginService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorOriginService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorOriginService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); class FocusIndicatorService { constructor() { this._localOptions = inject(ACCESSIBILITY_OPTIONS_TOKEN, { optional: true }); this.rendererFactory = inject(RendererFactory2); this._focusMonitor = inject(FocusMonitor); this._globalOptions = inject(AccessibilityOptionsService); this._focusIndicatorOrigin = inject(FocusIndicatorOriginService); // programmatically create a renderer as it can't be injected into a service this._renderer = this.rendererFactory.createRenderer(null, null); } /** This is essentially just a factory method to prevent the user having to pass in focus monitor, renderer and global options each time */ monitor(element, options = { ...this._globalOptions.options, ...this._localOptions, checkChildren: false, }) { return new FocusIndicator(element, this._focusMonitor, this._renderer, { ...this._globalOptions.options, ...this._localOptions, ...options }, this._focusIndicatorOrigin); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorService, decorators: [{ type: Injectable }], ctorParameters: () => [] }); class FocusIndicatorDirective { /** Specify whether or not we should mark this element as having focus if a child is focused */ set checkChildren(checkChildren) { // allow a string to be used so we can skip checking a binding for performance benefits checkChildren = coerceBooleanProperty(checkChildren); if (checkChildren !== null && checkChildren !== undefined) { this._checkChildren = checkChildren; this.setOptions(); } } /** Indicate whether or not mouse events should cause the focus indicator to appear - will override any global setting */ set mouseFocusIndicator(mouseFocusIndicator) { // allow a string to be used so we can skip checking a binding for performance benefits mouseFocusIndicator = coerceBooleanProperty(mouseFocusIndicator); if (mouseFocusIndicator !== null && mouseFocusIndicator !== undefined) { this._options.set('mouseFocusIndicator', mouseFocusIndicator); this.setOptions(); } } /** Indicate whether or not touch events should cause the focus indicator to appear - will override any global setting */ set touchFocusIndicator(touchFocusIndicator) { // allow a string to be used so we can skip checking a binding for performance benefits touchFocusIndicator = coerceBooleanProperty(touchFocusIndicator); if (touchFocusIndicator !== null && touchFocusIndicator !== undefined) { this._options.set('touchFocusIndicator', touchFocusIndicator); this.setOptions(); } } /** Indicate whether or not keyboard events should cause the focus indicator to appear - will override any global setting */ set keyboardFocusIndicator(keyboardFocusIndicator) { // allow a string to be used so we can skip checking a binding for performance benefits keyboardFocusIndicator = coerceBooleanProperty(keyboardFocusIndicator); if (keyboardFocusIndicator !== null && keyboardFocusIndicator !== undefined) { this._options.set('keyboardFocusIndicator', keyboardFocusIndicator); this.setOptions(); } } /** Indicate whether or not programmatic events should cause the focus indicator to appear - will override any global setting */ set programmaticFocusIndicator(programmaticFocusIndicator) { // allow a string to be used so we can skip checking a binding for performance benefits programmaticFocusIndicator = coerceBooleanProperty(programmaticFocusIndicator); if (programmaticFocusIndicator !== null && programmaticFocusIndicator !== undefined) { this._options.set('programmaticFocusIndicator', programmaticFocusIndicator); this.setOptions(); } } constructor() { this.optionsService = inject(AccessibilityOptionsService); this._elementRef = inject(ElementRef); this._focusIndicatorService = inject(FocusIndicatorService); this._ngZone = inject(NgZone); this.localOptions = inject(LocalFocusIndicatorOptions, { optional: true }); /** Emit the latest focus state */ this.indicator = new EventEmitter(); /** Store a private reference for the checkChildren option */ this._checkChildren = false; /** Store all configuation options*/ this._options = new Map(); /** Unsubscribe on component destroy */ this._onDestroy = new Subject(); // set the inital option values based on global options for (const option in this.optionsService.options || {}) { this._options.set(option, this.optionsService.options[option]); } // set the inital option values based on local options (if there are any) for (const option in this.localOptions || {}) { this._options.set(option, this.localOptions[option]); } } /** Setup the focus monitoring */ ngOnInit() { // start the focus monitoring this._focusIndicator = this._focusIndicatorService.monitor(this._elementRef.nativeElement, { checkChildren: this._checkChildren, mouseFocusIndicator: this._options.get('mouseFocusIndicator'), touchFocusIndicator: this._options.get('touchFocusIndicator'), keyboardFocusIndicator: this._options.get('keyboardFocusIndicator'), programmaticFocusIndicator: this._options.get('programmaticFocusIndicator'), }); // subscribe to the focus state to emit an event on change this._focusIndicator.isFocused$.pipe(takeUntil(this._onDestroy)).subscribe(isFocused => { // emit the latest value this._ngZone.run(() => this.indicator.emit(isFocused)); }); } /** Tear down the directive */ ngOnDestroy() { if (this._focusIndicator) { this._focusIndicator.destroy(); } // unsubscribe from all observables this._onDestroy.next(); this._onDestroy.complete(); } /** Focus this element with a specific origin */ focus(origin, options) { this._focusIndicator.focus(origin, options); } /** Update the focus indicator with the latest options */ setOptions() { if (this._focusIndicator) { this._focusIndicator.setOptions({ checkChildren: this._checkChildren, mouseFocusIndicator: this._options.get('mouseFocusIndicator'), touchFocusIndicator: this._options.get('touchFocusIndicator'), keyboardFocusIndicator: this._options.get('keyboardFocusIndicator'), programmaticFocusIndicator: this._options.get('programmaticFocusIndicator'), }); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: FocusIndicatorDirective, isStandalone: true, selector: "[uxFocusIndicator]", inputs: { checkChildren: "checkChildren", mouseFocusIndicator: "mouseFocusIndicator", touchFocusIndicator: "touchFocusIndicator", keyboardFocusIndicator: "keyboardFocusIndicator", programmaticFocusIndicator: "programmaticFocusIndicator" }, outputs: { indicator: "indicator" }, exportAs: ["ux-focus-indicator"], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorDirective, decorators: [{ type: Directive, args: [{ selector: '[uxFocusIndicator]', exportAs: 'ux-focus-indicator', }] }], ctorParameters: () => [], propDecorators: { checkChildren: [{ type: Input }], mouseFocusIndicator: [{ type: Input }], touchFocusIndicator: [{ type: Input }], keyboardFocusIndicator: [{ type: Input }], programmaticFocusIndicator: [{ type: Input }], indicator: [{ type: Output }] } }); class AccordionService { constructor() { this.collapseOthers = false; this.collapse = new Subject(); } collapseAll() { this.collapse.next(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionService, decorators: [{ type: Injectable }] }); let uniqueId$e = 1; class AccordionPanelComponent { constructor() { this.accordion = inject(AccordionService); this.panelId = `ux-accordion-panel-${uniqueId$e++}`; this.headingId = `${this.panelId}-heading`; this.disabled = false; this.expanded = false; this.expandedChange = new EventEmitter(); this._onDestroy = new Subject(); } ngOnInit() { this.accordion.collapse.pipe(takeUntil(this._onDestroy)).subscribe(() => this.collapse()); } ngOnDestroy() { this._onDestroy.next(); this._onDestroy.complete(); } toggle() { if (this.expanded) { this.collapse(); return; } // check if we should collapse others if (this.accordion.collapseOthers) { this.accordion.collapseAll(); } // store the new expanded state this.expand(); } expand() { if (this.disabled === false && this.expanded === false) { this.expanded = true; this.expandedChange.next(true); } } collapse() { if (this.disabled === false && this.expanded === true) { this.expanded = false; this.expandedChange.next(false); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: AccordionPanelComponent, isStandalone: true, selector: "ux-accordion-panel", inputs: { panelId: "panelId", headingId: "headingId", disabled: "disabled", heading: "heading", expanded: "expanded" }, outputs: { expandedChange: "expandedChange" }, host: { properties: { "class.panel-open": "this.expanded" }, classAttribute: "panel panel-default" }, ngImport: i0, template: "<div\n class=\"panel-heading\"\n role=\"button\"\n uxFocusIndicator\n [tabindex]=\"disabled ? -1 : 0\"\n [id]=\"headingId\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-disabled]=\"disabled\"\n (click)=\"toggle()\"\n (keydown.enter)=\"toggle()\"\n (keydown.space)=\"toggle(); $event.preventDefault()\"\n (keydown.spacebar)=\"toggle(); $event.preventDefault()\"\n>\n <div class=\"panel-title\">\n {{ heading }}\n <ng-content select=\"ux-accordion-panel-header\"></ng-content>\n </div>\n</div>\n\n<div\n [id]=\"panelId\"\n class=\"panel-collapse collapse\"\n [class.in]=\"expanded\"\n role=\"tabpanel\"\n [attr.aria-labelledby]=\"headingId\"\n>\n <div class=\"panel-body\">\n <ng-content></ng-content>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionPanelComponent, decorators: [{ type: Component, args: [{ selector: 'ux-accordion-panel', host: { class: 'panel panel-default', }, imports: [FocusIndicatorDirective], template: "<div\n class=\"panel-heading\"\n role=\"button\"\n uxFocusIndicator\n [tabindex]=\"disabled ? -1 : 0\"\n [id]=\"headingId\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-disabled]=\"disabled\"\n (click)=\"toggle()\"\n (keydown.enter)=\"toggle()\"\n (keydown.space)=\"toggle(); $event.preventDefault()\"\n (keydown.spacebar)=\"toggle(); $event.preventDefault()\"\n>\n <div class=\"panel-title\">\n {{ heading }}\n <ng-content select=\"ux-accordion-panel-header\"></ng-content>\n </div>\n</div>\n\n<div\n [id]=\"panelId\"\n class=\"panel-collapse collapse\"\n [class.in]=\"expanded\"\n role=\"tabpanel\"\n [attr.aria-labelledby]=\"headingId\"\n>\n <div class=\"panel-body\">\n <ng-content></ng-content>\n </div>\n</div>\n" }] }], propDecorators: { panelId: [{ type: Input }], headingId: [{ type: Input }], disabled: [{ type: Input }], heading: [{ type: Input }], expanded: [{ type: Input }, { type: HostBinding, args: ['class.panel-open'] }], expandedChange: [{ type: Output }] } }); class AccordionComponent { constructor() { this._accordion = inject(AccordionService); } set collapseOthers(collapseOthers) { this._accordion.collapseOthers = collapseOthers; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: AccordionComponent, isStandalone: true, selector: "ux-accordion", inputs: { collapseOthers: "collapseOthers" }, host: { attributes: { "aria-multiselectable": "true" }, classAttribute: "panel-group" }, providers: [AccordionService], ngImport: i0, template: "<ng-content></ng-content>\n" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AccordionComponent, decorators: [{ type: Component, args: [{ selector: 'ux-accordion', providers: [AccordionService], host: { class: 'panel-group', 'aria-multiselectable': 'true', }, template: "<ng-content></ng-content>\n" }] }], propDecorators: { collapseOthers: [{ type: Input }] } }); const KEPPEL_COLOR_SET = { primary: '#00a7a2', accent: '#7b63a3', secondary: '#fff', alternate1: '#3baa43', alternate2: '#025662', alternate3: '#b08f5c', vibrant1: '#00cceb', vibrant2: '#ff9048', grey1: '#2a2a2a', grey2: '#333', grey3: '#666', grey4: '#999', grey5: '#ccc', grey6: '#eee', grey7: '#f5f5f5', grey8: '#fafafa', chart1: '#00a7a2', chart2: '#7b63a3', chart3: '#3baa43', chart4: '#025662', chart5: '#b08f5c', chart6: '#ccc', ok: '#3baa43', warning: '#ff9048', critical: '#ff454f', partition1: '#635387', partition9: '#4a4066', partition10: '#308935', partition11: '#023e42', partition12: '#91744d', partition13: '#999', partition14: '#294266', 'social-chart-node': '#00cceb', 'social-chart-edge': '#00cceb', }; const MICRO_FOCUS_COLOR_SET = { 'brand-blue': '#0073e7', cerulean: '#1668c1', aqua: '#29ceff', aquamarine: '#2fd6c3', fuchsia: '#c6179d', indigo: '#7425ad', 'dark-blue': '#231ca5', white: '#ffffff', 'slightly-gray': '#f5f7f8', 'bright-gray': '#f1f2f3', gray: '#dcdedf', silver: '#bdbec0', 'dim-gray': '#656668', 'dark-gray': '#323435', black: '#000000', 'crimson-negative': '#e5004c', apricot: '#f48b34', yellow: '#fcdb1f', 'green-positive': '#1aac60', ultramarine: '#3939c6', skyblue: '#00abf3', 'pale-aqua': '#43e4ff', 'pale-green': '#1ffbba', lime: '#75da4d', orange: '#ffce00', magenta: '#eb23c2', 'pale-purple': '#ba47e2', 'dark-ultramarine': '#271782', steelblue: '#014272', 'arctic-blue': '#0b8eac', emerald: '#00a989', olive: '#5bba36', goldenrod: '#ffb000', purple: '#9b1e83', 'pale-eggplant': '#5216ac', red: '#ff454f', 'pale-amber': '#ffb24d', 'pale-lemon': '#fde159', 'pale-emerald': '#33c180', plum: '#b21646', copper: '#e57828', amber: '#ffc002', 'leaf-green': '#118c4f', 'forest-green': '#00645a', primary: '#0073e7', accent: '#7425ad', secondary: '#ffffff', alternate1: '#29ceff', alternate2: '#2fd6c3', alternate3: '#c6179d', vibrant1: '#43e4ff', vibrant2: '#ffce00', grey1: '#000000', grey2: '#323435', grey3: '#656668', grey4: '#bdbec0', grey5: '#dcdedf', grey6: '#f1f2f3', grey7: '#f5f7f8', grey8: '#ffffff', chart1: '#3939c6', chart2: '#00abf3', chart3: '#75da4d', chart4: '#ffce00', chart5: '#eb23c2', chart6: '#ba47e2', info: '#00abf3', ok: '#1aac60', warning: '#fcdb1f', danger: '#f48b34', critical: '#e5004c', partition1: '#7425ad', partition9: '#5216ac', partition10: '#5bba36', partition11: '#014272', partition12: '#ffb000', partition13: '#bdbec0', partition14: '#271782', 'social-chart-node': '#ff00ff', 'social-chart-edge': '#ff00ff', }; const colorSets = { keppel: { colorValueSet: KEPPEL_COLOR_SET, }, microFocus: { colorValueSet: MICRO_FOCUS_COLOR_SET, }, }; /** Provide a default color set for an application */ const COLOR_SET_TOKEN = new InjectionToken('COLOR_SET_TOKEN'); class ThemeColor { constructor(_r, _g, _b, _a = '1') { this._r = _r; this._g = _g; this._b = _b; this._a = _a; } /** * Create a ThemeColor object from a CSS color string * @param value The CSS color string to derive a ThemeColor object from */ static parse(value) { let r, g, b, a = '1'; const rgbaPattern = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/; const shortHexPattern = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; const longHexPattern = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; const rgbaMatch = value.match(rgbaPattern); const shortHexMatch = value.match(shortHexPattern); const longHexMatch = value.match(longHexPattern); if (rgbaMatch) { r = rgbaMatch[1]; g = rgbaMatch[2]; b = rgbaMatch[3]; a = rgbaMatch[4] ? rgbaMatch[4] : '1'; } else if (longHexMatch) { r = parseInt(longHexMatch[1], 16).toString(); g = parseInt(longHexMatch[2], 16).toString(); b = parseInt(longHexMatch[3], 16).toString(); } else if (shortHexMatch) { r = parseInt(shortHexMatch[1] + shortHexMatch[1], 16).toString(); g = parseInt(shortHexMatch[2] + shortHexMatch[2], 16).toString(); b = parseInt(shortHexMatch[3] + shortHexMatch[3], 16).toString(); } else { throw new Error(`Cannot parse color - ${value} is not a valid color.`); } return new ThemeColor(r, g, b, a); } /** * Clone a theme color so it can be modified without affecting other places using the color * @param themeColor The original theme color to clone */ static from(themeColor) { return new ThemeColor(themeColor.getRed(), themeColor.getGreen(), themeColor.getBlue(), themeColor.getAlpha()); } /** * Determine if an object is an instance of a theme color. * Using a simple instanceof check will not always work in plunker * where the ThemeColor is from @ux-aspects/ux-aspects and the color * comes from @micro-focus/ux-aspects */ static isInstanceOf(themeColor) { return (typeof themeColor === 'object' && // eslint-disable-next-line no-prototype-builtins themeColor.hasOwnProperty('_r') && // eslint-disable-next-line no-prototype-builtins themeColor.hasOwnProperty('_g') && // eslint-disable-next-line no-prototype-builtins themeColor.hasOwnProperty('_b') && // eslint-disable-next-line no-prototype-builtins themeColor.hasOwnProperty('_a')); } /** * Convert the theme color to a CSS hex color code */ toHex() { let red = parseInt(this._r).toString(16); let green = parseInt(this._g).toString(16); let blue = parseInt(this._b).toString(16); if (red.length < 2) { red = '0' + red; } if (green.length < 2) { green = '0' + green; } if (blue.length < 2) { blue = '0' + blue; } return '#' + red + green + blue; } /** * Convert the theme color to a CSS rgb color code */ toRgb() { return 'rgb(' + this._r + ', ' + this._g + ', ' + this._b + ')'; } /** * Convert the theme color to a CSS rgbs color code */ toRgba() { return 'rgba(' + this._r + ', ' + this._g + ', ' + this._b + ', ' + this._a + ')'; } /** * Get the red value from the RGBA color value */ getRed() { return this._r; } /** * Get the green value from the RGBA color value */ getGreen() { return this._g; } /** * Get the blue value from the RGBA color value */ getBlue() { return this._b; } /** * Get the alpha value from the RGBA color value */ getAlpha() { return this._a; } /** * Set the red value from the RGBA color value */ setRed(red) { this._r = red; return this; } /** * Set the green value from the RGBA color value */ setGreen(green) { this._g = green; return this; } /** * Set the blue value from the RGBA color value */ setBlue(blue) { this._b = blue; return this; } /** * Set the alpha value from the RGBA color value */ setAlpha(alpha) { this._a = alpha.toString(); return this; } } class ColorService { /** Allow the color set to be provided in a forRoot function otherwise set it to the Keppel theme by default */ constructor() { this._colorSet = inject(COLOR_SET_TOKEN, { optional: true }); // resolve the theme based on the colorset this._theme = this.getTheme(this._colorSet); } /** * Get a ThemeColor object from a color name * @param colorName The name of the color from the color palette */ getColor(colorName) { // get the matching ThemeColor from the active theme const themeColor = this._theme[this.resolveColorName(colorName)]; // if there is not a match then throw an error if (!themeColor) { throw new Error('Color not found: ' + colorName); } return new ThemeColor(themeColor.getRed(), themeColor.getGreen(), themeColor.getBlue(), themeColor.getAlpha()); } /** * Get the active color set */ getColorSet() { return this._colorSet; } /** * Define the current color set and produce a Theme from it */ setColorSet(colorSet) { this._colorSet = colorSet; this._theme = this.getTheme(colorSet); } /** * Resolve a color value. This may be the name of a color from the color set * or it may simply be a hex or rgb(a) color value. This function will return * a CSS color value regardless of which one of these formats it is * @param value The color name, hex code or rgb(a) value to resolve * @returns If the color is the name of a color in the set, the `rgba` color will be returned, otherwise the original CSS value will be returned. */ resolve(value) { if (!value) { return; } const colorName = this.resolveColorName(value); for (const color in this._theme) { if (colorName === color.toLowerCase()) { return this.getColor(colorName).toRgba(); } } return value; } /** * Converts a color name to an appropriate ColorSet name. For example * a color may be written in lower-camel-case, however color sets are in * kebab-case. This will convert to the appropriate naming format * @param colorName The color name to resolve */ resolveColorName(colorName = '') { return colorName.replace(/\s+/g, '-').toLowerCase(); } /** Determine if the current colorset has a specific color */ colorExists(name) { return !!Object.keys(this._theme).find(colorName => colorName === this.resolveColorName(name)); } /** Create a theme from a colorset */ getTheme(colorSet) { // create a new theme object const theme = {}; // ensure we have a colorset if (!colorSet) { colorSet = colorSets.keppel; } // iterate over each hex code and convert it to a theme color for (const color in colorSet.colorValueSet) { theme[color] = ThemeColor.parse(colorSet.colorValueSet[color]); } return theme; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ColorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ColorService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ColorService, decorators: [{ type: Injectable }], ctorParameters: () => [] }); class ColorServiceModule { /** * The function allows the consuming applications to specify the applications * color set once in the app module, eg: * ``` * ColorServiceModule.forRoot(colorSets.microFocus); * ``` * @param colorSet The color set the application should use */ static forRoot(colorSet) { return { ngModule: ColorServiceModule, providers: [ { provide: COLOR_SET_TOKEN, useValue: colorSet ? colorSet : colorSets.keppel }, ColorService, ], }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ColorServiceModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.9", ngImport: i0, type: ColorServiceModule }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ColorServiceModule }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ColorServiceModule, decorators: [{ type: NgModule, args: [{}] }] }); class ContrastService { /** * Calculate the contract ratio between two colors. * This uses the official WCAG Color Contrast Ratio * Algorithm: https://www.w3.org/TR/WCAG20-TECHS/G17.html */ getContrastColor(backgroundColor, lightColor, darkColor) { // get a ThemeColor from the ColorPickerColor const themeColor = ThemeColor.parse(backgroundColor.toHex()); const background = this.getLuminance(themeColor); const light = this.getLuminance(lightColor); const dark = this.getLuminance(darkColor); // determine the contrast for both black and white const whiteContrast = (light + 0.05) / (background + 0.05); const blackContrast = (background + 0.05) / (dark + 0.05); // return the color with the most contrast ratio return blackContrast > whiteContrast ? darkColor : lightColor; } getLuminance(color) { // normalize the colors let r = +color.getRed() / 255; let g = +color.getGreen() / 255; let b = +color.getBlue() / 255; // calculate the value required for each color component r = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4); g = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4); b = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4); // return the luminance return 0.2126 * r + 0.7152 * g + 0.0722 * b; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ContrastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ContrastService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ContrastService, decorators: [{ type: Injectable }] }); class ColorContrastDirective { constructor() { this._colorService = inject(ColorService); this._contrastService = inject(ContrastService); /** Store the light color as a ThemeColor object */ this._lightColor = ThemeColor.parse('#fff'); /** Store the light color as a ThemeColor object */ this._darkColor = ThemeColor.parse('#000'); } /** * Define the background color for contrast comparison. * This can be a CSS color value or the name of a * color from the color palette. */ set uxColorContrast(backgroundColor) { this._backgroundColor = ThemeColor.parse(this._colorService.resolve(backgroundColor)); } /** * Define the light color for contrast comparison. * This can be a CSS color value or the name of a * color from the color palette. */ set lightColor(lightColor) { this._lightColor = ThemeColor.parse(this._colorService.resolve(lightColor)); } /** * Define the dark color for contrast comparison. * This can be a CSS color value or the name of a * color from the color palette. */ set darkColor(darkColor) { this._darkColor = ThemeColor.parse(this._colorService.resolve(darkColor)); } /** Determine the color to set based on the supplied parameters */ get _color() { return this._backgroundColor ? this._contrastService .getContrastColor(this._backgroundColor, this._lightColor, this._darkColor) .toRgba() : null; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ColorContrastDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: ColorContrastDirective, isStandalone: true, selector: "[uxColorContrast]", inputs: { uxColorContrast: "uxColorContrast", lightColor: "lightColor", darkColor: "darkColor" }, host: { properties: { "style.color": "this._color" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ColorContrastDirective, decorators: [{ type: Directive, args: [{ selector: '[uxColorContrast]' }] }], propDecorators: { uxColorContrast: [{ type: Input }], lightColor: [{ type: Input }], darkColor: [{ type: Input }], _color: [{ type: HostBinding, args: ['style.color'] }] } }); /** * This directive can be used to target specific elements based on their CSS * class so we can control when the focus shows. This will help prevent us * polluting the FocusIndicatorDirective with an lot of selectors. * * If the button has a uxFocusIndicator, uxMenuTriggerFor or uxMenuNavigationToggle directive applied we should skip this */ class DefaultFocusIndicatorDirective extends FocusIndicatorDirective { constructor() { super(); // Enable programmatic focus by default this.programmaticFocusIndicator = true; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: DefaultFocusIndicatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: DefaultFocusIndicatorDirective, isStandalone: true, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])", usesInheritance: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: DefaultFocusIndicatorDirective, decorators: [{ type: Directive, args: [{ selector: '.btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])', }] }], ctorParameters: () => [] }); class FocusIndicatorOptionsDirective { constructor() { this._options = inject(LocalFocusIndicatorOptions, { self: true }); } /** If `true`, this element will receive a focus indicator when the element is clicked on. */ set mouseFocusIndicator(mouseFocusIndicator) { this._options.mouseFocusIndicator = mouseFocusIndicator; } /** If `true`, this element will receive a focus indicator when the element is touched. */ set touchFocusIndicator(touchFocusIndicator) { this._options.touchFocusIndicator = touchFocusIndicator; } /** If `true`, this element will receive a focus indicator when the element is focused using the keyboard. */ set keyboardFocusIndicator(keyboardFocusIndicator) { this._options.keyboardFocusIndicator = keyboardFocusIndicator; } /** If `true`, this element will receive a focus indicator when the element is programmatically focused. */ set programmaticFocusIndicator(programmaticFocusIndicator) { this._options.programmaticFocusIndicator = programmaticFocusIndicator; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorOptionsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: FocusIndicatorOptionsDirective, isStandalone: true, selector: "[uxFocusIndicatorOptions]", inputs: { mouseFocusIndicator: "mouseFocusIndicator", touchFocusIndicator: "touchFocusIndicator", keyboardFocusIndicator: "keyboardFocusIndicator", programmaticFocusIndicator: "programmaticFocusIndicator" }, providers: [LocalFocusIndicatorOptions], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: FocusIndicatorOptionsDirective, decorators: [{ type: Directive, args: [{ selector: '[uxFocusIndicatorOptions]', providers: [LocalFocusIndicatorOptions], }] }], propDecorators: { mouseFocusIndicator: [{ type: Input }], touchFocusIndicator: [{ type: Input }], keyboardFocusIndicator: [{ type: Input }], programmaticFocusIndicator: [{ type: Input }] } }); /** * When working with component host elements * we cannot apply directives, eg. FocusIndicatorOriginDirective * however we may still want the functionality to be applied to * the host element. This class allows the host element to become * a focus indicator origin */ class FocusIndicatorOrigin { constructor(_focusIndicatorOrigin, elementRef, renderer) { this._focusIndicatorOrigin = _focusIndicatorOrigin; /** Store all event handlers */ this._handlers = []; // add event handlers this._handlers = [ renderer.listen(elementRef.nativeElement, 'click', () => th