UNPKG

@clr/angular

Version:

Angular components for Clarity

1,279 lines (1,256 loc) 373 kB
import * as i0 from '@angular/core'; import { Injectable, Directive, ViewChild, Component, PLATFORM_ID, Inject, DOCUMENT, EventEmitter, ElementRef, booleanAttribute, Input, Output, Optional, ContentChild, ChangeDetectionStrategy, ContentChildren, forwardRef, HostListener, ViewContainerRef, runInInjectionContext, Injector, ChangeDetectorRef, NgZone, Renderer2, inject, EnvironmentInjector, TemplateRef, IterableDiffers, afterNextRender, ViewChildren, InjectionToken, NgModule } from '@angular/core'; import * as i2 from '@clr/angular/utils'; import { uniqueIdFactory, Keys, HostWrapper, IfExpandService, ClrLoadingState, ClrExpandableAnimationDirective, LoadingListener, WillyWonka, OompaLoompa, ClrKeyFocus, DomAdapter, ClrIfExpanded, CdkDragModule, CdkTrapFocusModule, ClrLoadingModule, ClrConditionalModule, ClrOutsideClickModule, ClrExpandableAnimationModule, ClrKeyFocusModule } from '@clr/angular/utils'; import * as i2$2 from 'rxjs'; import { Subject, BehaviorSubject, fromEvent, ReplaySubject, combineLatest, merge, of } from 'rxjs'; import { filter, takeUntil, delay, debounceTime, map, switchMap } from 'rxjs/operators'; import * as i3 from '@clr/angular/popover/common'; import { ClrPopoverPosition, ClrPopoverType, ClrPopoverHostDirective, mapPopoverKeyToPosition, ClrPopoverModuleNext } from '@clr/angular/popover/common'; import * as i1 from '@clr/angular/modal'; import * as i9 from '@angular/common'; import { isPlatformBrowser, NgForOf, CommonModule } from '@angular/common'; import * as i5 from '@clr/angular/icon'; import { ClarityIcons, ellipsisVerticalIcon, viewColumnsIcon, windowCloseIcon, arrowIcon, timesIcon, twoWayArrowsIcon, stepForward2Icon, angleDoubleIcon, filterGridCircleIcon, filterGridIcon, ClrIcon } from '@clr/angular/icon'; import * as i4 from '@clr/angular/forms/common'; import { ClrControlLabel } from '@clr/angular/forms/common'; import * as i5$1 from '@clr/angular/forms/number-input'; import { ClrNumberInputModule } from '@clr/angular/forms/number-input'; import * as i13 from '@angular/forms'; import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import * as i5$2 from '@clr/angular/forms/input'; import { ClrInputModule } from '@clr/angular/forms/input'; import { ClrSignpost } from '@clr/angular/popover/signpost'; import * as i12 from '@clr/angular/forms/radio'; import { ClrRadioModule } from '@clr/angular/forms/radio'; import * as i14 from '@clr/angular/progress/spinner'; import { ClrSpinnerModule } from '@clr/angular/progress/spinner'; import * as i2$1 from '@angular/cdk/bidi'; import { Directionality } from '@angular/cdk/bidi'; import { coerceNumberProperty } from '@angular/cdk/coercion'; import * as i3$1 from '@angular/cdk/scrolling'; import { FixedSizeVirtualScrollStrategy, VIRTUAL_SCROLL_STRATEGY, ScrollDispatcher, ViewportRuler, CdkVirtualScrollable, CdkVirtualScrollViewport, CDK_VIRTUAL_SCROLL_VIEWPORT, CdkVirtualForOf } from '@angular/cdk/scrolling'; import * as i7 from '@clr/angular/forms/checkbox'; import { ClrCheckboxModule } from '@clr/angular/forms/checkbox'; import { ClrSelectModule } from '@clr/angular/forms/select'; /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ /** * Generic accessor for deep object properties * that can be specified as simple dot-separated strings. */ class NestedProperty { constructor(prop) { this.prop = prop; if (prop.indexOf('.') >= 0) { this.splitProp = prop.split('.'); } } // Safe getter for a deep object property, will not throw an error but return // undefined if one of the intermediate properties is null or undefined. getPropValue(item) { if (this.splitProp) { let value = item; for (const nestedProp of this.splitProp) { if (value === null || typeof value === 'undefined' || typeof value[nestedProp] === 'undefined') { return undefined; } value = value[nestedProp]; } return value; } else { return item[this.prop]; } } } /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DatagridPropertyComparator { constructor(prop) { this.prop = prop; this.nestedProp = new NestedProperty(prop); } compare(a, b) { let propA = this.nestedProp.getPropValue(a); let propB = this.nestedProp.getPropValue(b); if (typeof propA === 'string') { propA = propA.toLowerCase(); } if (typeof propB === 'string') { propB = propB.toLowerCase(); } if (typeof propA === 'undefined' || propA === null) { if (typeof propB === 'undefined' || propB === null) { return 0; } else { return 1; } } else { if (typeof propB === 'undefined' || propB === null) { return -1; } else if (propA < propB) { return -1; } else if (propA > propB) { return 1; } else { return 0; } } } } /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DatagridPropertyNumericFilter { constructor(prop, exact = false) { this.prop = prop; this.exact = exact; this.nestedProp = new NestedProperty(prop); } accepts(item, low, high) { const propValue = this.nestedProp.getPropValue(item); if (propValue === undefined) { return false; } if (low !== null && (typeof propValue !== 'number' || propValue < low)) { return false; } if (high !== null && (typeof propValue !== 'number' || propValue > high)) { return false; } return true; } } /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DatagridNumericFilterImpl { constructor(filterFn) { this.filterFn = filterFn; /** * The Observable required as part of the Filter interface */ this._changes = new Subject(); /** * Internal values and accessor */ this._low = null; this._high = null; } // We do not want to expose the Subject itself, but the Observable which is read-only get changes() { return this._changes.asObservable(); } get value() { return [this._low, this._high]; } set value(vals) { const low = vals[0]; const high = vals[1]; if (low !== this._low || high !== this._high) { this._low = low; this._high = high; this._changes.next([this._low, this._high]); } } get low() { return this._low; } set low(low) { if (low !== this._low) { this._low = low; this._changes.next([this._low, this._high]); } } get high() { return this._high; } set high(high) { if (high !== this._high) { this._high = high; this._changes.next([this._low, this._high]); } } get state() { if (this.filterFn instanceof DatagridPropertyNumericFilter) { return { property: this.filterFn.prop, low: this._low, high: this._high, }; } return this; } /** * Indicates if the filter is currently active, (at least one input is set) */ isActive() { return this._low !== null || this.high !== null; } /** * Tests if an item matches a search text */ accepts(item) { // We have a filter function in case someone wants to implement a numeric // filter that always passes nulls or similar return this.filterFn.accepts(item, this._low, this._high); } equals(other) { if (other instanceof DatagridNumericFilterImpl) { if (other.filterFn instanceof DatagridPropertyNumericFilter) { return (this.filterFn instanceof DatagridPropertyNumericFilter && other.filterFn.prop === this.filterFn.prop && other.low === this._low && other.high === this._high); } return other === this; } return false; } } /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DatagridPropertyStringFilter { constructor(prop, exact = false) { this.prop = prop; this.exact = exact; this.nestedProp = new NestedProperty(prop); } accepts(item, search) { const propValue = this.nestedProp.getPropValue(item); if (typeof propValue === 'undefined') { return false; } else if (this.exact) { return ('' + propValue).toLowerCase() === search; } else { return ('' + propValue).toLowerCase().indexOf(search) >= 0; } } } /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DatagridStringFilterImpl { constructor(filterFn) { this.filterFn = filterFn; /** * The Observable required as part of the Filter interface */ this._changes = new Subject(); /** * Input value converted to lowercase */ this._lowerCaseValue = ''; /** * Raw input value */ this._rawValue = ''; } // We do not want to expose the Subject itself, but the Observable which is read-only get changes() { return this._changes.asObservable(); } get lowerCaseValue() { return this._lowerCaseValue; } get state() { if (this.filterFn instanceof DatagridPropertyStringFilter) { return { property: this.filterFn.prop, value: this.value, }; } return this; } get value() { return this._rawValue; } /** * Common setter for the input value */ set value(value) { if (!value) { value = ''; } if (value !== this._rawValue) { this._rawValue = value; this._lowerCaseValue = value.toLowerCase().trim(); this._changes.next(value); } } /** * Indicates if the filter is currently active, meaning the input is not empty */ isActive() { return !!this.value; } /** * Tests if an item matches a search text */ accepts(item) { // We always test with the lowercase value of the input, to stay case insensitive return this.filterFn.accepts(item, this.lowerCaseValue); } equals(other) { if (other instanceof DatagridStringFilterImpl) { if (other.filterFn instanceof DatagridPropertyStringFilter) { return (this.filterFn instanceof DatagridPropertyStringFilter && other.filterFn.prop === this.filterFn.prop && other.value === this.value); } return other === this; } return false; } } /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ /** * Enumeration representing the sorting order of a datagrid column. It is a constant Enum, * i.e. each value needs to be treated as a `number`, starting at index 0. * * @export * @enum {number} */ var ClrDatagridSortOrder; (function (ClrDatagridSortOrder) { ClrDatagridSortOrder[ClrDatagridSortOrder["UNSORTED"] = 0] = "UNSORTED"; ClrDatagridSortOrder[ClrDatagridSortOrder["ASC"] = 1] = "ASC"; ClrDatagridSortOrder[ClrDatagridSortOrder["DESC"] = -1] = "DESC"; })(ClrDatagridSortOrder || (ClrDatagridSortOrder = {})); var ClrDatagridAriaSortOrder; (function (ClrDatagridAriaSortOrder) { ClrDatagridAriaSortOrder["UNSORTED"] = "none"; ClrDatagridAriaSortOrder["ASC"] = "ascending"; ClrDatagridAriaSortOrder["DESC"] = "descending"; })(ClrDatagridAriaSortOrder || (ClrDatagridAriaSortOrder = {})); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class CustomFilter { } /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const STRICT_WIDTH_CLASS = 'datagrid-fixed-width'; const HIDDEN_COLUMN_CLASS = 'datagrid-hidden-column'; /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ /* * This provider implements some form of synchronous debouncing through a lock pattern * to avoid emitting multiple state changes for a single user action. */ class StateDebouncer { constructor() { /* * This is the lock, to only emit once all the changes have finished processing */ this.nbChanges = 0; /** * The Observable that lets other classes subscribe to global state changes */ this._change = new Subject(); } // We do not want to expose the Subject itself, but the Observable which is read-only get change() { return this._change.asObservable(); } changeStart() { this.nbChanges++; } changeDone() { if (--this.nbChanges === 0) { this._change.next(); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StateDebouncer, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StateDebouncer }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StateDebouncer, decorators: [{ type: Injectable }] }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class Page { constructor(stateDebouncer) { this.stateDebouncer = stateDebouncer; this.activated = false; /** * Page size, a value of 0 means no pagination */ this._size = 0; /** * Current page */ this._current = 1; /** * The Observable that lets other classes subscribe to page changes */ this._change = new Subject(); this.preventEmit = false; this._sizeChange = new Subject(); } get size() { return this._size; } set size(size) { const oldSize = this._size; if (size !== oldSize) { if (!this.preventEmit) { this.stateDebouncer.changeStart(); } this._size = size; if (size === 0) { this._current = 1; } else { // Yeap. That's the formula to keep the first item from the old page still // displayed in the new one. this._current = Math.floor((oldSize / size) * (this._current - 1)) + 1; } // We always emit an event even if the current page index didn't change, because // the size changing means the items inside the page are different if (!this.preventEmit) { this._change.next(this._current); this._sizeChange.next(this._size); this.stateDebouncer.changeDone(); } } this.preventEmit = false; } get totalItems() { return this._totalItems || 0; // remains 0 if not set to avoid breaking change } set totalItems(total) { this._totalItems = total; // If we have less items than before, we might need to change the current page if (this.current > this.last) { this.current = this.last; } } get last() { if (this._last) { return this._last; } // If the last page isn't known, we compute it from the last item's index if (this.size > 0 && this.totalItems) { return Math.ceil(this.totalItems / this.size); } return 1; } set last(page) { this._last = page; } // We do not want to expose the Subject itself, but the Observable which is read-only get change() { return this._change.asObservable(); } get sizeChange() { return this._sizeChange.asObservable(); } get current() { return this._current; } set current(page) { if (page !== this._current) { this.stateDebouncer.changeStart(); this._current = page; this._change.next(page); this.stateDebouncer.changeDone(); } } /** * Index of the first item displayed on the current page, starting at 0, -1 if none displayed */ get firstItem() { if (this._totalItems === 0) { return -1; } if (this.size === 0) { return 0; } return (this.current - 1) * this.size; } /** * Index of the last item displayed on the current page, starting at 0, -1 if none displayed */ get lastItem() { if (this._totalItems === 0) { return -1; } if (this.size === 0) { return this.totalItems - 1; } let lastInPage = this.current * this.size - 1; if (this.totalItems) { lastInPage = Math.min(lastInPage, this.totalItems - 1); } return lastInPage; } /** * Moves to the previous page if it exists */ previous() { if (this.current > 1) { this.current--; } } /** * Moves to the next page if it exists */ next() { if (this.current < this.last) { this.current++; } } /** * Resets the page size to 0 */ resetPageSize(preventEmit = false) { this.preventEmit = preventEmit; this.size = 0; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: Page, deps: [{ token: StateDebouncer }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: Page }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: Page, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: StateDebouncer }] }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class FiltersProvider { constructor(_page, stateDebouncer) { this._page = _page; this.stateDebouncer = stateDebouncer; /** * This subject is the list of filters that changed last, not the whole list. * We emit a list rather than just one filter to allow batch changes to several at once. */ this._change = new Subject(); /** * List of all filters, whether they're active or not */ this._all = []; } // We do not want to expose the Subject itself, but the Observable which is read-only get change() { return this._change.asObservable(); } /** * Tests if at least one filter is currently active */ hasActiveFilters() { // We do not use getActiveFilters() because this function will be called much more often // and stopping the loop early might be relevant. for (const { filter } of this._all) { if (filter && filter.isActive()) { return true; } } return false; } /** * Returns a list of all currently active filters */ getActiveFilters() { const ret = []; for (const { filter } of this._all) { if (filter && filter.isActive()) { ret.push(filter); } } return ret; } /** * Registers a filter, and returns a deregistration function */ add(filter) { const subscription = filter.changes.subscribe(() => this.resetPageAndEmitFilterChange([filter])); let hasUnregistered = false; const registered = new RegisteredFilter(filter, () => { if (hasUnregistered) { return; } subscription.unsubscribe(); const matchIndex = this._all.findIndex(item => item.filter === filter); if (matchIndex >= 0) { this._all.splice(matchIndex, 1); } if (filter.isActive()) { this.resetPageAndEmitFilterChange([]); } hasUnregistered = true; }); this._all.push(registered); if (filter.isActive()) { this.resetPageAndEmitFilterChange([filter]); } return registered; } /** * Accepts an item if it is accepted by all currently active filters */ accepts(item) { for (const { filter } of this._all) { if (filter && filter.isActive() && !filter.accepts(item)) { return false; } } return true; } resetPageAndEmitFilterChange(filters) { this.stateDebouncer.changeStart(); // filtering may change the page number such that current page number doesn't exist in the filtered dataset. // So here we always set the current page to 1 so that it'll fetch first page's data with the given filter. this._page.current = 1; this._change.next(filters); this.stateDebouncer.changeDone(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FiltersProvider, deps: [{ token: Page }, { token: StateDebouncer }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FiltersProvider }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FiltersProvider, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: Page }, { type: StateDebouncer }] }); class RegisteredFilter { constructor(filter, unregister) { this.filter = filter; this.unregister = unregister; } } /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DatagridFilterRegistrar { constructor(filters) { this.filters = filters; } get filter() { return this.registered && this.registered.filter; } ngOnDestroy() { this.deleteFilter(); } setFilter(filter) { // If we previously had another filter, we unregister it this.deleteFilter(); if (filter instanceof RegisteredFilter) { this.registered = filter; } else if (filter) { this.registered = this.filters.add(filter); } } deleteFilter() { if (this.registered) { this.registered.unregister(); delete this.registered; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DatagridFilterRegistrar, deps: [{ token: FiltersProvider }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: DatagridFilterRegistrar, isStandalone: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DatagridFilterRegistrar, decorators: [{ type: Directive }], ctorParameters: () => [{ type: FiltersProvider }] }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class WrappedColumn { ngAfterViewInit() { // Create the cells view in memory, not the DOM. this.columnView = this.templateRef.createEmbeddedView(null); } ngOnDestroy() { this.columnView.destroy(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: WrappedColumn, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: WrappedColumn, isStandalone: false, selector: "dg-wrapped-column", viewQueries: [{ propertyName: "templateRef", first: true, predicate: ["columnPortal"], descendants: true }], ngImport: i0, template: ` <ng-template #columnPortal> <ng-content></ng-content> </ng-template> `, isInline: true }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: WrappedColumn, decorators: [{ type: Component, args: [{ selector: 'dg-wrapped-column', template: ` <ng-template #columnPortal> <ng-content></ng-content> </ng-template> `, standalone: false, }] }], propDecorators: { templateRef: [{ type: ViewChild, args: ['columnPortal'] }] } }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class Sort { constructor(stateDebouncer) { this.stateDebouncer = stateDebouncer; /** * Ascending order if false, descending if true */ this._reverse = false; /** * The Observable that lets other classes subscribe to sort changes */ this._change = new Subject(); } get comparator() { return this._comparator; } set comparator(value) { this.stateDebouncer.changeStart(); this._comparator = value; this.emitChange(); this.stateDebouncer.changeDone(); } get reverse() { return this._reverse; } set reverse(value) { this.stateDebouncer.changeStart(); this._reverse = value; this.emitChange(); this.stateDebouncer.changeDone(); } // We do not want to expose the Subject itself, but the Observable which is read-only get change() { return this._change.asObservable(); } /** * Sets a comparator as the current one, or toggles reverse if the comparator is already used. The * optional forceReverse input parameter allows to override that toggling behavior by sorting in * reverse order if `true`. * * @memberof Sort */ toggle(sortBy, forceReverse) { this.stateDebouncer.changeStart(); // We modify private properties directly, to batch the change event if (this.comparator === sortBy) { this._reverse = typeof forceReverse !== 'undefined' ? forceReverse || !this._reverse : !this._reverse; } else { this._comparator = sortBy; this._reverse = typeof forceReverse !== 'undefined' ? forceReverse : false; } this.emitChange(); this.stateDebouncer.changeDone(); } /** * Clears the current sorting order */ clear() { this.comparator = null; } /** * Compares two objects according to the current comparator */ compare(a, b) { return (this.reverse ? -1 : 1) * this.comparator.compare(a, b); } emitChange() { this._change.next(this); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: Sort, deps: [{ token: StateDebouncer }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: Sort }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: Sort, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: StateDebouncer }] }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DetailService { constructor(modalStackService) { this.modalStackService = modalStackService; this.preventScroll = false; this.toggleState = false; this._enabled = false; this._state = new BehaviorSubject(this.toggleState); } get enabled() { return this._enabled; } set enabled(state) { this._enabled = state; } get preventFocusScroll() { return this.preventScroll; } set preventFocusScroll(preventScroll) { this.preventScroll = preventScroll; } get state() { return this.cache; } get stateChange() { return this._state.asObservable(); } get isOpen() { return this.toggleState === true; } open(item, button) { this.cache = item; this.button = button; this.toggleState = true; this._state.next(this.toggleState); this.modalStackService.trackModalOpen(this); } close() { this.toggleState = false; this.returnFocus(); this._state.next(this.toggleState); this.modalStackService.trackModalClose(this); } returnFocus() { if (this.button) { this.button.focus({ preventScroll: this.preventFocusScroll }); this.button = null; } } toggle(item, button) { if (this.isRowOpen(item) || !item) { this.close(); } else { this.open(item, button); } } isRowOpen(item) { return !!(this.toggleState && this.cache === item); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DetailService, deps: [{ token: i1.ModalStackService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DetailService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DetailService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: i1.ModalStackService }] }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ var DatagridRenderStep; (function (DatagridRenderStep) { DatagridRenderStep[DatagridRenderStep["ALIGN_COLUMNS"] = 0] = "ALIGN_COLUMNS"; DatagridRenderStep[DatagridRenderStep["CALCULATE_MODE_ON"] = 1] = "CALCULATE_MODE_ON"; DatagridRenderStep[DatagridRenderStep["CALCULATE_MODE_OFF"] = 2] = "CALCULATE_MODE_OFF"; DatagridRenderStep[DatagridRenderStep["CLEAR_WIDTHS"] = 3] = "CLEAR_WIDTHS"; DatagridRenderStep[DatagridRenderStep["COMPUTE_COLUMN_WIDTHS"] = 4] = "COMPUTE_COLUMN_WIDTHS"; })(DatagridRenderStep || (DatagridRenderStep = {})); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DatagridRenderOrganizer { constructor() { this._renderStep = new Subject(); this.alreadySized = false; } get renderStep() { return this._renderStep.asObservable(); } filterRenderSteps(step) { return this.renderStep.pipe(filter(testStep => step === testStep)); } resize() { this._renderStep.next(DatagridRenderStep.CALCULATE_MODE_ON); if (this.alreadySized) { this._renderStep.next(DatagridRenderStep.CLEAR_WIDTHS); } this._renderStep.next(DatagridRenderStep.COMPUTE_COLUMN_WIDTHS); this._renderStep.next(DatagridRenderStep.ALIGN_COLUMNS); // NOT USED this.alreadySized = true; this._renderStep.next(DatagridRenderStep.CALCULATE_MODE_OFF); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DatagridRenderOrganizer, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DatagridRenderOrganizer }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DatagridRenderOrganizer, decorators: [{ type: Injectable }] }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ const MIN_COLUMN_WIDTH = 96; // This service allows DatagridHeaderRenderer and ClrDatagridColumnSeparator // to share column resize data with each other. class ColumnResizerService { constructor(el, domAdapter, organizer) { this.el = el; this.domAdapter = domAdapter; this.organizer = organizer; this._resizedBy = 0; } get resizedBy() { return this._resizedBy; } get minColumnWidth() { return this.domAdapter.minWidth(this.el.nativeElement) || MIN_COLUMN_WIDTH; } get maxResizeRange() { return this.widthBeforeResize - this.minColumnWidth; } get widthAfterResize() { return this.widthBeforeResize + this._resizedBy; } startResize() { this._resizedBy = 0; this.isWithinMaxResizeRange = true; this.widthBeforeResize = this.domAdapter.clientRect(this.el.nativeElement).width; } endResize() { this.organizer.resize(); } calculateResize(resizedBy) { // calculates the resize amount within the allowed range if (resizedBy < -this.maxResizeRange) { this._resizedBy = -this.maxResizeRange; this.isWithinMaxResizeRange = false; } else { this._resizedBy = resizedBy; this.isWithinMaxResizeRange = true; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnResizerService, deps: [{ token: i0.ElementRef }, { token: i2.DomAdapter }, { token: DatagridRenderOrganizer }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnResizerService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnResizerService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i2.DomAdapter }, { type: DatagridRenderOrganizer }] }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ /** * @description * Internal datagrid service that holds a reference to the clr-dg-table element and exposes a method to get height. */ class TableSizeService { constructor(platformId) { this.platformId = platformId; } get tableRef() { return this._tableRef; } set tableRef(element) { this._tableRef = element; } set table(table) { if (isPlatformBrowser(this.platformId) && table.nativeElement) { this.tableRef = table.nativeElement.querySelector('.datagrid-table'); } } // Used when resizing columns to show the column border being dragged. getColumnDragHeight() { if (!this.tableRef) { return null; } return `${this.tableRef.clientHeight}px`; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TableSizeService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TableSizeService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TableSizeService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }] }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ // Default resize length on each keyboard move event const KEYBOARD_RESIZE_LENGTH = 12; class ClrDatagridColumnSeparator { constructor(columnResizerService, renderer, ngZone, tableSizeService, commonString, document) { this.columnResizerService = columnResizerService; this.renderer = renderer; this.ngZone = ngZone; this.tableSizeService = tableSizeService; this.commonString = commonString; this.document = document; this.columnSeparatorId = uniqueIdFactory(); this.resizeStartedOnKeyDown = false; this.unlisteners = []; } get descriptionId() { return `${this.columnSeparatorId}-aria-describedby`; } get resizeTrackerEl() { return this.resizeTrackerRef.nativeElement; } get columnHandleEl() { return this.columnHandleRef.nativeElement; } ngAfterViewInit() { this.ngZone.runOutsideAngular(() => { this.unlisteners.push(this.renderer.listen(this.columnHandleEl, 'keydown', event => { this.showTrackerOnFirstKeyDown(event); this.moveTrackerOnKeyDown(event); })); this.unlisteners.push(this.renderer.listen(this.columnHandleEl, 'keyup', event => { this.hideTrackerOnKeyUp(event); })); }); } ngOnDestroy() { this.unlisteners.forEach(unlistener => unlistener()); } showTracker() { this.columnResizerService.startResize(); const tableHeight = this.tableSizeService.getColumnDragHeight(); this.renderer.setStyle(this.resizeTrackerEl, 'height', tableHeight); this.renderer.setStyle(this.resizeTrackerEl, 'display', 'block'); } moveTracker(movedBy) { this.columnResizerService.calculateResize(movedBy); this.renderer.setStyle(this.resizeTrackerEl, 'transform', `translateX(${this.columnResizerService.resizedBy}px)`); this.renderer.setStyle(this.document.body, 'cursor', 'col-resize'); this.redFlagTracker(); } hideTracker() { this.columnResizerService.endResize(); this.renderer.setStyle(this.resizeTrackerEl, 'display', 'none'); this.renderer.setStyle(this.resizeTrackerEl, 'transform', `translateX(0px)`); this.renderer.setStyle(this.columnHandleEl, 'transform', `translateX(0px)`); this.renderer.setStyle(this.document.body, 'cursor', 'auto'); } showTrackerOnFirstKeyDown(event) { if (!this.resizeStartedOnKeyDown && (this.isArrowLeftKeyEvent(event) || this.isArrowRightKeyEvent(event))) { this.resizeStartedOnKeyDown = true; this.renderer.addClass(this.resizeTrackerEl, 'on-arrow-key-resize'); this.showTracker(); } } moveTrackerOnKeyDown(event) { if (this.isArrowLeftKeyEvent(event)) { event.stopPropagation(); this.moveTracker(this.columnResizerService.resizedBy - KEYBOARD_RESIZE_LENGTH); } else if (this.isArrowRightKeyEvent(event)) { event.stopPropagation(); this.moveTracker(this.columnResizerService.resizedBy + KEYBOARD_RESIZE_LENGTH); } } hideTrackerOnKeyUp(event) { if (this.resizeStartedOnKeyDown && (this.isArrowLeftKeyEvent(event) || this.isArrowRightKeyEvent(event))) { this.resizeStartedOnKeyDown = false; this.renderer.removeClass(this.resizeTrackerEl, 'on-arrow-key-resize'); this.hideTracker(); this.columnHandleEl.focus(); } } redFlagTracker() { if (this.isWithinMaxResizeRange !== this.columnResizerService.isWithinMaxResizeRange) { this.isWithinMaxResizeRange = this.columnResizerService.isWithinMaxResizeRange; if (!this.isWithinMaxResizeRange) { this.renderer.addClass(this.resizeTrackerEl, 'exceeded-max'); } else { this.renderer.removeClass(this.resizeTrackerEl, 'exceeded-max'); } } } isArrowLeftKeyEvent(event) { return event.key === Keys.ArrowLeft; } isArrowRightKeyEvent(event) { return event.key === Keys.ArrowRight; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridColumnSeparator, deps: [{ token: ColumnResizerService }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: TableSizeService }, { token: i2.ClrCommonStringsService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: ClrDatagridColumnSeparator, isStandalone: false, selector: "clr-dg-column-separator", host: { properties: { "class.datagrid-column-separator": "true" } }, viewQueries: [{ propertyName: "resizeTrackerRef", first: true, predicate: ["resizeTracker"], descendants: true }, { propertyName: "columnHandleRef", first: true, predicate: ["columnHandle"], descendants: true }], ngImport: i0, template: ` <button type="button" class="datagrid-column-handle" [attr.aria-label]="commonString.keys.columnSeparatorAriaLabel" [attr.aria-describedby]="descriptionId" cdkDrag cdkDragLockAxis="x" (cdkDragStarted)="showTracker()" (cdkDragMoved)="moveTracker($event.distance.x)" (cdkDragEnded)="hideTracker(); $event.source._dragRef.reset()" #columnHandle ></button> <span class="clr-sr-only" [attr.id]="descriptionId"> {{ commonString.keys.columnSeparatorDescription }} </span> <div class="datagrid-column-resize-tracker" #resizeTracker></div> `, isInline: true, dependencies: [{ kind: "directive", type: i2.CdkDragModule_CdkDrag, selector: "[cdkDrag]" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridColumnSeparator, decorators: [{ type: Component, args: [{ selector: 'clr-dg-column-separator', template: ` <button type="button" class="datagrid-column-handle" [attr.aria-label]="commonString.keys.columnSeparatorAriaLabel" [attr.aria-describedby]="descriptionId" cdkDrag cdkDragLockAxis="x" (cdkDragStarted)="showTracker()" (cdkDragMoved)="moveTracker($event.distance.x)" (cdkDragEnded)="hideTracker(); $event.source._dragRef.reset()" #columnHandle ></button> <span class="clr-sr-only" [attr.id]="descriptionId"> {{ commonString.keys.columnSeparatorDescription }} </span> <div class="datagrid-column-resize-tracker" #resizeTracker></div> `, host: { '[class.datagrid-column-separator]': 'true', }, standalone: false, }] }], ctorParameters: () => [{ type: ColumnResizerService }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: TableSizeService }, { type: i2.ClrCommonStringsService }, { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }], propDecorators: { resizeTrackerRef: [{ type: ViewChild, args: ['resizeTracker'] }], columnHandleRef: [{ type: ViewChild, args: ['columnHandle'] }] } }); /* * Copyright (c) 2016-2026 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * This software is released under MIT license. * The full license information can be found in LICENSE in the root directory of this project. */ class DefaultKeyNavigationStrategy { constructor(utils) { this.utils = utils; } keyUp(currentCellCoords) { const nextCellCoords = this.utils.createNextCellCoords(currentCellCoords); if (currentCellCoords.y === 0) { return nextCellCoords; } nextCellCoords.y = currentCellCoords.y - 1; const isActionCell = this.utils.isActionCell(currentCellCoords); if (this.utils.isSingleCellExpandedRow(nextCellCoords.y) && !isActionCell && this.utils.isDetailsRow(nextCellCoords.y)) { nextCellCoords.x = 0; } else if (this.utils.isDetailsRow(nextCellCoords.y)) { if (isActionCell) { nextCellCoords.y = nextCellCoords.y - 1; } else { nextCellCoords.x = nextCellCoords.x - this.utils.actionCellCount(currentCellCoords.y); } } return nextCellCoords; } keyDown(currentCellCoords) { const nextCellCoords = this.utils.createNextCellCoords(currentCellCoords); const numOfRows = this.utils.rows ? this.utils.rows.length - 1 : 0; if (currentCellCoords.y >= numOfRows) { return nextCellCoords; } const isActionCell = this.utils.isActionCell(currentCellCoords); nextCellCoords.y = currentCellCoords.y + 1; if (!isActionCell && this.utils.isRowReplaced(nextCellCoords.y)) { nextCellCoords.y = nextCellCoords.y + 1; nextCellCoords.x = this.utils.isSingleCellExpandedRow(nextCellCoords.y) ? 0 : nextCellCoords.x - this.utils.actionCellCount(currentCellCoords.y); } return nextCellCoords; } keyLeft(currentCellCoords) { const nextCellCoords = this.utils.createNextCellCoords(currentCellCoords); if (currentCellCoords.x === 0) { return nextCellCoords; } nextCellCoords.x = currentCellCoords.x - 1; return nextCellCoords;