@clr/angular 18.2.2 → 18.3.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,14 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- 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';
2
+ import { InjectionToken, 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, RendererStyleFlags2, ViewChildren, NgModule } from '@angular/core';
3
3
  import * as i2 from '@clr/angular/utils';
4
4
  import { uniqueIdFactory, Keys, HostWrapper, IfExpandService, ClrLoadingState, ClrExpandableAnimationDirective, LoadingListener, WillyWonka, OompaLoompa, ClrKeyFocus, DomAdapter, ClrIfExpanded, CdkDragModule, CdkTrapFocusModule, ClrLoadingModule, ClrConditionalModule, ClrOutsideClickModule, ClrExpandableAnimationModule, ClrKeyFocusModule } from '@clr/angular/utils';
5
- import * as i2$2 from 'rxjs';
5
+ import * as i12 from 'rxjs';
6
6
  import { Subject, BehaviorSubject, fromEvent, ReplaySubject, combineLatest, merge, of } from 'rxjs';
7
7
  import { filter, takeUntil, delay, debounceTime, map, switchMap } from 'rxjs/operators';
8
8
  import * as i3 from '@clr/angular/popover/common';
9
9
  import { ClrPopoverPosition, ClrPopoverType, ClrPopoverHostDirective, mapPopoverKeyToPosition, ClrPopoverModuleNext } from '@clr/angular/popover/common';
10
10
  import * as i1 from '@clr/angular/modal';
11
- import * as i9 from '@angular/common';
11
+ import * as i10 from '@angular/common';
12
12
  import { isPlatformBrowser, NgForOf, CommonModule } from '@angular/common';
13
13
  import * as i5 from '@clr/angular/icon';
14
14
  import { ClarityIcons, ellipsisVerticalIcon, viewColumnsIcon, windowCloseIcon, arrowIcon, timesIcon, twoWayArrowsIcon, stepForward2Icon, angleDoubleIcon, filterGridCircleIcon, filterGridIcon, ClrIcon } from '@clr/angular/icon';
@@ -16,14 +16,14 @@ import * as i4 from '@clr/angular/forms/common';
16
16
  import { ClrControlLabel } from '@clr/angular/forms/common';
17
17
  import * as i5$1 from '@clr/angular/forms/number-input';
18
18
  import { ClrNumberInputModule } from '@clr/angular/forms/number-input';
19
- import * as i13 from '@angular/forms';
19
+ import * as i14 from '@angular/forms';
20
20
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
21
21
  import * as i5$2 from '@clr/angular/forms/input';
22
22
  import { ClrInputModule } from '@clr/angular/forms/input';
23
23
  import { ClrSignpost } from '@clr/angular/popover/signpost';
24
- import * as i12 from '@clr/angular/forms/radio';
24
+ import * as i13 from '@clr/angular/forms/radio';
25
25
  import { ClrRadioModule } from '@clr/angular/forms/radio';
26
- import * as i14 from '@clr/angular/progress/spinner';
26
+ import * as i15 from '@clr/angular/progress/spinner';
27
27
  import { ClrSpinnerModule } from '@clr/angular/progress/spinner';
28
28
  import * as i2$1 from '@angular/cdk/bidi';
29
29
  import { Directionality } from '@angular/cdk/bidi';
@@ -341,6 +341,23 @@ class DatagridStringFilterImpl {
341
341
  }
342
342
  }
343
343
 
344
+ /*
345
+ * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
346
+ * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
347
+ * This software is released under MIT license.
348
+ * The full license information can be found in LICENSE in the root directory of this project.
349
+ */
350
+ var DatagridColumnChanges;
351
+ (function (DatagridColumnChanges) {
352
+ DatagridColumnChanges[DatagridColumnChanges["WIDTH"] = 0] = "WIDTH";
353
+ DatagridColumnChanges[DatagridColumnChanges["HIDDEN"] = 1] = "HIDDEN";
354
+ DatagridColumnChanges[DatagridColumnChanges["INITIALIZE"] = 2] = "INITIALIZE";
355
+ DatagridColumnChanges[DatagridColumnChanges["PINNED"] = 3] = "PINNED";
356
+ })(DatagridColumnChanges || (DatagridColumnChanges = {}));
357
+ const ALL_COLUMN_CHANGES = Object.keys(DatagridColumnChanges)
358
+ .map(key => DatagridColumnChanges[key])
359
+ .filter(key => key === parseInt(key, 10) && key !== DatagridColumnChanges.INITIALIZE); // extracts only integer keys
360
+
344
361
  /*
345
362
  * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
346
363
  * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
@@ -367,6 +384,23 @@ var ClrDatagridAriaSortOrder;
367
384
  ClrDatagridAriaSortOrder["DESC"] = "descending";
368
385
  })(ClrDatagridAriaSortOrder || (ClrDatagridAriaSortOrder = {}));
369
386
 
387
+ /*
388
+ * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
389
+ * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
390
+ * This software is released under MIT license.
391
+ * The full license information can be found in LICENSE in the root directory of this project.
392
+ */
393
+ const COLUMN_STATE = new InjectionToken('COLUMN_STATE');
394
+ function columnStateFactory() {
395
+ return new BehaviorSubject({
396
+ changes: [],
397
+ });
398
+ }
399
+ const COLUMN_STATE_PROVIDER = {
400
+ provide: COLUMN_STATE,
401
+ useFactory: columnStateFactory,
402
+ };
403
+
370
404
  /*
371
405
  * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
372
406
  * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
@@ -384,6 +418,7 @@ class CustomFilter {
384
418
  */
385
419
  const STRICT_WIDTH_CLASS = 'datagrid-fixed-width';
386
420
  const HIDDEN_COLUMN_CLASS = 'datagrid-hidden-column';
421
+ const PINNED_COLUMN_CLASS = 'datagrid-pinned-column';
387
422
 
388
423
  /*
389
424
  * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
@@ -848,9 +883,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
848
883
  * This software is released under MIT license.
849
884
  * The full license information can be found in LICENSE in the root directory of this project.
850
885
  */
886
+ const MAX_DETAIL_WIDTH = 100;
887
+ const DEFAULT_DETAIL_WIDTH = 66;
888
+ const MIN_DETAIL_WIDTH = 0;
851
889
  class DetailService {
852
890
  constructor(modalStackService) {
853
891
  this.modalStackService = modalStackService;
892
+ this.detailWidth = DEFAULT_DETAIL_WIDTH;
854
893
  this.preventScroll = false;
855
894
  this.toggleState = false;
856
895
  this._enabled = false;
@@ -914,6 +953,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
914
953
  type: Injectable
915
954
  }], ctorParameters: () => [{ type: i1.ModalStackService }] });
916
955
 
956
+ /*
957
+ * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
958
+ * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
959
+ * This software is released under MIT license.
960
+ * The full license information can be found in LICENSE in the root directory of this project.
961
+ */
962
+ class ColumnsService {
963
+ constructor() {
964
+ this.columns = [];
965
+ this.columnsStateChange = new BehaviorSubject(null);
966
+ this._cache = [];
967
+ }
968
+ get columnStates() {
969
+ return this.columns.map(column => column.value);
970
+ }
971
+ get hasHideableColumns() {
972
+ return this.columnStates.filter(state => state.hideable).length > 0;
973
+ }
974
+ get visibleColumns() {
975
+ return this.columnStates.filter(state => !state.hidden);
976
+ }
977
+ get hasPinnedColumns() {
978
+ return this.columnStates.some(state => state.pinned);
979
+ }
980
+ /**
981
+ * Tells whether the column at the given index is pinned (static). Columns and cells are
982
+ * matched by their declaration index, so this is what the header, the rows and a column aligned
983
+ * row detail use to decide which views belong in the static container.
984
+ */
985
+ isPinned(columnIndex) {
986
+ return !!this.columns[columnIndex]?.value.pinned;
987
+ }
988
+ cache() {
989
+ this._cache = this.columns.map(subject => {
990
+ const value = { ...subject.value };
991
+ delete value.changes;
992
+ return value;
993
+ });
994
+ }
995
+ hasCache() {
996
+ return !!this._cache.length;
997
+ }
998
+ resetToLastCache() {
999
+ this._cache.forEach((state, index) => {
1000
+ // Just emit the exact value from the cache
1001
+ const cachedState = { ...state, changes: ALL_COLUMN_CHANGES };
1002
+ this.columns[index].next(cachedState);
1003
+ this.columnsStateChange.next(cachedState);
1004
+ });
1005
+ this._cache = [];
1006
+ }
1007
+ // Helper method to emit a change to a column only when there is an actual diff to process for that column
1008
+ emitStateChangeAt(columnIndex, diff) {
1009
+ if (!this.columns[columnIndex]) {
1010
+ return;
1011
+ }
1012
+ this.emitStateChange(this.columns[columnIndex], diff);
1013
+ }
1014
+ emitStateChange(column, diff) {
1015
+ const changedState = { ...column.value, ...diff };
1016
+ column.next(changedState);
1017
+ this.columnsStateChange.next(changedState);
1018
+ }
1019
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1020
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnsService }); }
1021
+ }
1022
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnsService, decorators: [{
1023
+ type: Injectable
1024
+ }] });
1025
+
917
1026
  /*
918
1027
  * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
919
1028
  * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
@@ -2235,7 +2344,7 @@ class DatagridNumericFilter extends DatagridFilterRegistrar {
2235
2344
  </clr-number-input-container>
2236
2345
  </div>
2237
2346
  </clr-dg-filter>
2238
- `, isInline: true, dependencies: [{ kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i5$1.ClrNumberInput, selector: "input[type=\"number\"][clrNumberInput]" }, { kind: "component", type: i5$1.ClrNumberInputContainer, selector: "clr-number-input-container" }, { kind: "directive", type: i13.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i13.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClrDatagridFilter, selector: "clr-dg-filter", inputs: ["clrDgFilterOpen", "clrDgFilter"], outputs: ["clrDgFilterOpenChange"] }] }); }
2347
+ `, isInline: true, dependencies: [{ kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i5$1.ClrNumberInput, selector: "input[type=\"number\"][clrNumberInput]" }, { kind: "component", type: i5$1.ClrNumberInputContainer, selector: "clr-number-input-container" }, { kind: "directive", type: i14.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i14.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i14.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i14.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClrDatagridFilter, selector: "clr-dg-filter", inputs: ["clrDgFilterOpen", "clrDgFilter"], outputs: ["clrDgFilterOpenChange"] }] }); }
2239
2348
  }
2240
2349
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DatagridNumericFilter, decorators: [{
2241
2350
  type: Component,
@@ -2428,7 +2537,7 @@ class DatagridStringFilter extends DatagridFilterRegistrar {
2428
2537
  />
2429
2538
  </clr-input-container>
2430
2539
  </clr-dg-filter>
2431
- `, isInline: true, dependencies: [{ kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i5$2.ClrInput, selector: "[clrInput]" }, { kind: "component", type: i5$2.ClrInputContainer, selector: "clr-input-container" }, { kind: "directive", type: i13.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClrDatagridFilter, selector: "clr-dg-filter", inputs: ["clrDgFilterOpen", "clrDgFilter"], outputs: ["clrDgFilterOpenChange"] }] }); }
2540
+ `, isInline: true, dependencies: [{ kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i5$2.ClrInput, selector: "[clrInput]" }, { kind: "component", type: i5$2.ClrInputContainer, selector: "clr-input-container" }, { kind: "directive", type: i14.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i14.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i14.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClrDatagridFilter, selector: "clr-dg-filter", inputs: ["clrDgFilterOpen", "clrDgFilter"], outputs: ["clrDgFilterOpenChange"] }] }); }
2432
2541
  }
2433
2542
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DatagridStringFilter, decorators: [{
2434
2543
  type: Component,
@@ -2484,7 +2593,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
2484
2593
  * The full license information can be found in LICENSE in the root directory of this project.
2485
2594
  */
2486
2595
  class ClrDatagridColumn extends DatagridFilterRegistrar {
2487
- constructor(el, _sort, filters, vcr, detailService, changeDetectorRef, commonStrings) {
2596
+ constructor(el, _sort, filters, vcr, detailService, changeDetectorRef, commonStrings, columnsService, columnState) {
2488
2597
  super(filters);
2489
2598
  this.el = el;
2490
2599
  this._sort = _sort;
@@ -2492,8 +2601,21 @@ class ClrDatagridColumn extends DatagridFilterRegistrar {
2492
2601
  this.detailService = detailService;
2493
2602
  this.changeDetectorRef = changeDetectorRef;
2494
2603
  this.commonStrings = commonStrings;
2604
+ this.columnsService = columnsService;
2605
+ this.columnState = columnState;
2495
2606
  this.disableUnsort = false;
2607
+ // Disabled for now, together with the toggle in the template above and togglePinned() below.
2608
+ // Kept as a line comment rather than a doc comment, so api-extractor does not attach it to the
2609
+ // next declaration.
2610
+ //
2611
+ // /**
2612
+ // * Shows a pin toggle in the column header, letting the user pin and unpin the column from within
2613
+ // * the datagrid. It only adds the control - the pinned state itself stays on `clrDgPinned`.
2614
+ // */
2615
+ // @Input({ alias: 'clrDgPinnable', transform: booleanAttribute }) pinnable = false;
2496
2616
  this.sortOrderChange = new EventEmitter();
2617
+ // Only togglePinned() ever emitted this, so it is disabled along with the pin toggle.
2618
+ // @Output('clrDgPinnedChange') pinnedChange = new EventEmitter<boolean>();
2497
2619
  this.filterValueChange = new EventEmitter();
2498
2620
  /**
2499
2621
  * A custom filter for this column that can be provided in the projected content
@@ -2514,12 +2636,21 @@ class ClrDatagridColumn extends DatagridFilterRegistrar {
2514
2636
  */
2515
2637
  this.subscriptions = [];
2516
2638
  this._showSeparator = true;
2639
+ this._pinned = false;
2517
2640
  this.subscriptions.push(this.listenForSortingChanges());
2518
2641
  this.subscriptions.push(this.listenForDetailPaneChanges());
2519
2642
  }
2520
2643
  get isHidden() {
2521
2644
  return this.el.nativeElement.classList.contains(HIDDEN_COLUMN_CLASS);
2522
2645
  }
2646
+ /**
2647
+ * Whether the column is currently rendered as pinned. This can differ from the `pinned` input
2648
+ * while the detail pane is open, because pinning is suspended for as long as it shows a single
2649
+ * column.
2650
+ */
2651
+ get isPinned() {
2652
+ return this.el.nativeElement.classList.contains(PINNED_COLUMN_CLASS);
2653
+ }
2523
2654
  get showSeparator() {
2524
2655
  return this._showSeparator;
2525
2656
  }
@@ -2527,6 +2658,29 @@ class ClrDatagridColumn extends DatagridFilterRegistrar {
2527
2658
  this._showSeparator = value;
2528
2659
  this.changeDetectorRef.markForCheck();
2529
2660
  }
2661
+ /**
2662
+ * Pins the column to the left of the datagrid, so it stays visible while the remaining
2663
+ * columns are scrolled horizontally. Pinned columns keep their declaration order and are
2664
+ * rendered before the scrollable ones, right after the built-in row controls.
2665
+ */
2666
+ get pinned() {
2667
+ return this._pinned;
2668
+ }
2669
+ set pinned(value) {
2670
+ if (this._pinned === value) {
2671
+ return;
2672
+ }
2673
+ this._pinned = value;
2674
+ // The column state is what the header and the rows read to decide where to project the
2675
+ // column and its cells. It is optional only so the column keeps working when it is used
2676
+ // outside the render directives, e.g. in isolated tests.
2677
+ if (this.columnState) {
2678
+ this.columnsService.emitStateChange(this.columnState, {
2679
+ pinned: value,
2680
+ changes: [DatagridColumnChanges.PINNED],
2681
+ });
2682
+ }
2683
+ }
2530
2684
  // TODO: We might want to make this an enum in the future
2531
2685
  get colType() {
2532
2686
  return this._colType;
@@ -2705,6 +2859,21 @@ class ClrDatagridColumn extends DatagridFilterRegistrar {
2705
2859
  this._sortDirection = this._sortOrder === ClrDatagridSortOrder.DESC ? 'down' : 'up';
2706
2860
  this.sortOrderChange.emit(this._sortOrder);
2707
2861
  }
2862
+ // Disabled for now, together with the pin toggle in the template and the clrDgPinnable input.
2863
+ //
2864
+ // /**
2865
+ // * Pins or unpins the column from the header control. Going through the `pinned` setter keeps the
2866
+ // * rendering in sync, and the output lets the application follow a change it did not initiate -
2867
+ // * without it a one-way [clrDgPinned] binding would write the old value straight back.
2868
+ // */
2869
+ // protected togglePinned(event: MouseEvent) {
2870
+ // // The header is a drop target for the column ordering addon and a click target for the sort
2871
+ // // button next to us, so the toggle keeps its click to itself.
2872
+ // event.stopPropagation();
2873
+ //
2874
+ // this.pinned = !this.pinned;
2875
+ // this.pinnedChange.emit(this.pinned);
2876
+ // }
2708
2877
  listenForDetailPaneChanges() {
2709
2878
  return this.detailService.stateChange.subscribe(state => {
2710
2879
  if (this.showSeparator !== !state) {
@@ -2748,9 +2917,26 @@ class ClrDatagridColumn extends DatagridFilterRegistrar {
2748
2917
  delete this.initFilterValue;
2749
2918
  }
2750
2919
  }
2751
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridColumn, deps: [{ token: i0.ElementRef }, { token: Sort }, { token: FiltersProvider }, { token: i0.ViewContainerRef }, { token: DetailService }, { token: i0.ChangeDetectorRef }, { token: i2.ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component }); }
2752
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagridColumn, isStandalone: false, selector: "clr-dg-column", inputs: { filterStringPlaceholder: ["clrFilterStringPlaceholder", "filterStringPlaceholder"], filterNumberMaxPlaceholder: ["clrFilterNumberMaxPlaceholder", "filterNumberMaxPlaceholder"], filterNumberMinPlaceholder: ["clrFilterNumberMinPlaceholder", "filterNumberMinPlaceholder"], disableUnsort: ["clrDgDisableUnsort", "disableUnsort"], colType: ["clrDgColType", "colType"], field: ["clrDgField", "field"], sortBy: ["clrDgSortBy", "sortBy"], sortOrder: ["clrDgSortOrder", "sortOrder"], updateFilterValue: ["clrFilterValue", "updateFilterValue"] }, outputs: { sortOrderChange: "clrDgSortOrderChange", filterValueChange: "clrFilterValueChange" }, host: { attributes: { "role": "columnheader" }, properties: { "class.datagrid-column": "true", "attr.aria-sort": "ariaSort" } }, queries: [{ propertyName: "projectedFilter", first: true, predicate: CustomFilter, descendants: true }], viewQueries: [{ propertyName: "titleContainer", first: true, predicate: ["titleContainer"], descendants: true, read: ElementRef }], usesInheritance: true, usesOnChanges: true, hostDirectives: [{ directive: i3.ClrPopoverHostDirective }], ngImport: i0, template: `
2920
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridColumn, deps: [{ token: i0.ElementRef }, { token: Sort }, { token: FiltersProvider }, { token: i0.ViewContainerRef }, { token: DetailService }, { token: i0.ChangeDetectorRef }, { token: i2.ClrCommonStringsService }, { token: ColumnsService }, { token: COLUMN_STATE, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
2921
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagridColumn, isStandalone: false, selector: "clr-dg-column", inputs: { filterStringPlaceholder: ["clrFilterStringPlaceholder", "filterStringPlaceholder"], filterNumberMaxPlaceholder: ["clrFilterNumberMaxPlaceholder", "filterNumberMaxPlaceholder"], filterNumberMinPlaceholder: ["clrFilterNumberMinPlaceholder", "filterNumberMinPlaceholder"], disableUnsort: ["clrDgDisableUnsort", "disableUnsort"], pinned: ["clrDgPinned", "pinned", booleanAttribute], colType: ["clrDgColType", "colType"], field: ["clrDgField", "field"], sortBy: ["clrDgSortBy", "sortBy"], sortOrder: ["clrDgSortOrder", "sortOrder"], updateFilterValue: ["clrFilterValue", "updateFilterValue"] }, outputs: { sortOrderChange: "clrDgSortOrderChange", filterValueChange: "clrFilterValueChange" }, host: { attributes: { "role": "columnheader" }, properties: { "class.datagrid-column": "true", "attr.aria-sort": "ariaSort" } }, queries: [{ propertyName: "projectedFilter", first: true, predicate: CustomFilter, descendants: true }], viewQueries: [{ propertyName: "titleContainer", first: true, predicate: ["titleContainer"], descendants: true, read: ElementRef }], usesInheritance: true, usesOnChanges: true, hostDirectives: [{ directive: i3.ClrPopoverHostDirective }], ngImport: i0, template: `
2753
2922
  <div class="datagrid-column-flex">
2923
+ <!--
2924
+ clrDgPinnable is disabled for now. Restoring it means uncommenting this block together with
2925
+ the pinnable input, the clrDgPinnedChange output and togglePinned() below, the
2926
+ .datagrid-column-pin styles, the pin/unpin icons in ClrDatagridModule and the
2927
+ pinColumn/unpinColumn common strings.
2928
+
2929
+ @if (pinnable) {
2930
+ <button
2931
+ class="datagrid-column-pin"
2932
+ type="button"
2933
+ [attr.aria-label]="pinned ? commonStrings.keys.unpinColumn : commonStrings.keys.pinColumn"
2934
+ (click)="togglePinned($event)"
2935
+ >
2936
+ <cds-icon [size]="'12'" [shape]="pinned ? 'unpin' : 'pin'" solid aria-hidden="true"></cds-icon>
2937
+ </button>
2938
+ }
2939
+ -->
2754
2940
  @if (sortable) {
2755
2941
  <button class="datagrid-column-title" (click)="sort()" type="button" #titleContainer>
2756
2942
  <ng-container *ngTemplateOutlet="columnTitle"></ng-container>
@@ -2794,7 +2980,7 @@ class ClrDatagridColumn extends DatagridFilterRegistrar {
2794
2980
  <clr-dg-column-separator></clr-dg-column-separator>
2795
2981
  }
2796
2982
  </div>
2797
- `, isInline: true, dependencies: [{ kind: "directive", type: i9.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i5.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "component", type: ClrDatagridColumnSeparator, selector: "clr-dg-column-separator" }, { kind: "component", type: DatagridNumericFilter, selector: "clr-dg-numeric-filter", inputs: ["clrFilterMinPlaceholder", "clrFilterMaxPlaceholder", "clrFilterFromLabel", "clrFilterToLabel", "clrFilterValue", "clrDgNumericFilter"], outputs: ["clrFilterValueChange"] }, { kind: "component", type: DatagridStringFilter, selector: "clr-dg-string-filter", inputs: ["clrFilterPlaceholder", "clrFilterLabel", "clrDgStringFilter", "clrFilterValue"], outputs: ["clrFilterValueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2983
+ `, isInline: true, dependencies: [{ kind: "directive", type: i10.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i5.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "component", type: ClrDatagridColumnSeparator, selector: "clr-dg-column-separator" }, { kind: "component", type: DatagridNumericFilter, selector: "clr-dg-numeric-filter", inputs: ["clrFilterMinPlaceholder", "clrFilterMaxPlaceholder", "clrFilterFromLabel", "clrFilterToLabel", "clrFilterValue", "clrDgNumericFilter"], outputs: ["clrFilterValueChange"] }, { kind: "component", type: DatagridStringFilter, selector: "clr-dg-string-filter", inputs: ["clrFilterPlaceholder", "clrFilterLabel", "clrDgStringFilter", "clrFilterValue"], outputs: ["clrFilterValueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2798
2984
  }
2799
2985
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridColumn, decorators: [{
2800
2986
  type: Component,
@@ -2802,6 +2988,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
2802
2988
  selector: 'clr-dg-column',
2803
2989
  template: `
2804
2990
  <div class="datagrid-column-flex">
2991
+ <!--
2992
+ clrDgPinnable is disabled for now. Restoring it means uncommenting this block together with
2993
+ the pinnable input, the clrDgPinnedChange output and togglePinned() below, the
2994
+ .datagrid-column-pin styles, the pin/unpin icons in ClrDatagridModule and the
2995
+ pinColumn/unpinColumn common strings.
2996
+
2997
+ @if (pinnable) {
2998
+ <button
2999
+ class="datagrid-column-pin"
3000
+ type="button"
3001
+ [attr.aria-label]="pinned ? commonStrings.keys.unpinColumn : commonStrings.keys.pinColumn"
3002
+ (click)="togglePinned($event)"
3003
+ >
3004
+ <cds-icon [size]="'12'" [shape]="pinned ? 'unpin' : 'pin'" solid aria-hidden="true"></cds-icon>
3005
+ </button>
3006
+ }
3007
+ -->
2805
3008
  @if (sortable) {
2806
3009
  <button class="datagrid-column-title" (click)="sort()" type="button" #titleContainer>
2807
3010
  <ng-container *ngTemplateOutlet="columnTitle"></ng-container>
@@ -2855,7 +3058,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
2855
3058
  changeDetection: ChangeDetectionStrategy.OnPush,
2856
3059
  standalone: false,
2857
3060
  }]
2858
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: Sort }, { type: FiltersProvider }, { type: i0.ViewContainerRef }, { type: DetailService }, { type: i0.ChangeDetectorRef }, { type: i2.ClrCommonStringsService }], propDecorators: { filterStringPlaceholder: [{
3061
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: Sort }, { type: FiltersProvider }, { type: i0.ViewContainerRef }, { type: DetailService }, { type: i0.ChangeDetectorRef }, { type: i2.ClrCommonStringsService }, { type: ColumnsService }, { type: i12.BehaviorSubject, decorators: [{
3062
+ type: Optional
3063
+ }, {
3064
+ type: Inject,
3065
+ args: [COLUMN_STATE]
3066
+ }] }], propDecorators: { filterStringPlaceholder: [{
2859
3067
  type: Input,
2860
3068
  args: ['clrFilterStringPlaceholder']
2861
3069
  }], filterNumberMaxPlaceholder: [{
@@ -2876,6 +3084,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
2876
3084
  }], titleContainer: [{
2877
3085
  type: ViewChild,
2878
3086
  args: ['titleContainer', { read: ElementRef }]
3087
+ }], pinned: [{
3088
+ type: Input,
3089
+ args: [{ alias: 'clrDgPinned', transform: booleanAttribute }]
2879
3090
  }], colType: [{
2880
3091
  type: Input,
2881
3092
  args: ['clrDgColType']
@@ -3118,6 +3329,7 @@ class ClrDatagridItems {
3118
3329
  }
3119
3330
  ngOnDestroy() {
3120
3331
  this.subscriptions.forEach(sub => sub.unsubscribe());
3332
+ this.items.smartenDown();
3121
3333
  }
3122
3334
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridItems, deps: [{ token: i0.TemplateRef }, { token: i0.IterableDiffers }, { token: Items }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
3123
3335
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: ClrDatagridItems, isStandalone: false, selector: "[clrDgItems][clrDgItemsOf]", inputs: { rawItems: ["clrDgItemsOf", "rawItems"], trackBy: ["clrDgItemsTrackBy", "trackBy"] }, ngImport: i0 }); }
@@ -3936,7 +4148,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
3936
4148
  */
3937
4149
  let nbRow = 0;
3938
4150
  class ClrDatagridRow {
3939
- constructor(selection, rowActionService, globalExpandable, expand, detailService, displayMode, vcr, renderer, el, commonStrings, items, document) {
4151
+ constructor(selection, rowActionService, globalExpandable, expand, detailService, displayMode, vcr, renderer, el, commonStrings, items, columnsService, document) {
3940
4152
  this.selection = selection;
3941
4153
  this.rowActionService = rowActionService;
3942
4154
  this.globalExpandable = globalExpandable;
@@ -3947,6 +4159,7 @@ class ClrDatagridRow {
3947
4159
  this.el = el;
3948
4160
  this.commonStrings = commonStrings;
3949
4161
  this.items = items;
4162
+ this.columnsService = columnsService;
3950
4163
  this.document = document;
3951
4164
  this.selectedChanged = new EventEmitter(false);
3952
4165
  this.expandedChange = new EventEmitter(false);
@@ -4074,11 +4287,7 @@ class ClrDatagridRow {
4074
4287
  }
4075
4288
  ngAfterContentInit() {
4076
4289
  this.dgCells.changes.subscribe(() => {
4077
- this.dgCells.forEach(cell => {
4078
- if (!cell._view.destroyed) {
4079
- this._scrollableCells.insert(cell._view);
4080
- }
4081
- });
4290
+ this.insertCellViews();
4082
4291
  });
4083
4292
  }
4084
4293
  ngAfterViewInit() {
@@ -4088,6 +4297,10 @@ class ClrDatagridRow {
4088
4297
  for (let i = this._scrollableCells.length; i > 0; i--) {
4089
4298
  this._scrollableCells.detach();
4090
4299
  }
4300
+ // remove cell views of pinned columns from their static container
4301
+ for (let i = this._pinnedCells.length; i > 0; i--) {
4302
+ this._pinnedCells.detach();
4303
+ }
4091
4304
  // remove cell views from calculated view
4092
4305
  for (let i = this._calculatedCells.length; i > 0; i--) {
4093
4306
  this._calculatedCells.detach();
@@ -4112,11 +4325,7 @@ class ClrDatagridRow {
4112
4325
  }
4113
4326
  else {
4114
4327
  this.displayCells = true;
4115
- this.dgCells.forEach(cell => {
4116
- if (!cell._view.destroyed) {
4117
- this._scrollableCells.insert(cell._view);
4118
- }
4119
- });
4328
+ this.insertCellViews();
4120
4329
  }
4121
4330
  }), this.expand.animate.subscribe(() => {
4122
4331
  this.expandAnimationTrigger = !this.expandAnimationTrigger;
@@ -4173,6 +4382,21 @@ class ClrDatagridRow {
4173
4382
  this.toggle(selected);
4174
4383
  }
4175
4384
  }
4385
+ /**
4386
+ * Projects the cells into the display containers. Cells of pinned columns go into their static
4387
+ * container so they stay visible during horizontal scroll; the rest stay scrollable. Cells are
4388
+ * matched with their column by declaration index, and iterating in that order keeps both groups
4389
+ * aligned with the header.
4390
+ */
4391
+ insertCellViews() {
4392
+ this.dgCells.forEach((cell, index) => {
4393
+ if (cell._view.destroyed) {
4394
+ return;
4395
+ }
4396
+ const container = this.columnsService.isPinned(index) ? this._pinnedCells : this._scrollableCells;
4397
+ container.insert(cell._view);
4398
+ });
4399
+ }
4176
4400
  rangeSelect() {
4177
4401
  const items = this.items.displayed;
4178
4402
  if (!items) {
@@ -4195,12 +4419,12 @@ class ClrDatagridRow {
4195
4419
  this.selection.rangeStart = this.item;
4196
4420
  }
4197
4421
  }
4198
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridRow, deps: [{ token: Selection }, { token: RowActionService }, { token: ExpandableRowsCount }, { token: DatagridIfExpandService }, { token: DetailService }, { token: DisplayModeService }, { token: i0.ViewContainerRef }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i2.ClrCommonStringsService }, { token: Items }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
4422
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridRow, deps: [{ token: Selection }, { token: RowActionService }, { token: ExpandableRowsCount }, { token: DatagridIfExpandService }, { token: DetailService }, { token: DisplayModeService }, { token: i0.ViewContainerRef }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i2.ClrCommonStringsService }, { token: Items }, { token: ColumnsService }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
4199
4423
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagridRow, isStandalone: false, selector: "clr-dg-row", inputs: { detailDisabled: ["clrDgDetailDisabled", "detailDisabled"], detailHidden: ["clrDgDetailHidden", "detailHidden"], skeletonLoading: ["clrDgSkeletonLoading", "skeletonLoading"], item: ["clrDgItem", "item"], clrDgSelectable: "clrDgSelectable", selected: ["clrDgSelected", "selected"], expanded: ["clrDgExpanded", "expanded"], clrDgDetailOpenLabel: "clrDgDetailOpenLabel", clrDgDetailCloseLabel: "clrDgDetailCloseLabel", clrDgRowSelectionLabel: "clrDgRowSelectionLabel" }, outputs: { selectedChanged: "clrDgSelectedChange", expandedChange: "clrDgExpandedChange" }, host: { attributes: { "role": "rowgroup" }, properties: { "class.datagrid-row": "true", "class.datagrid-row-skeleton": "skeletonLoading", "class.datagrid-selected": "selected", "attr.aria-owns": "id" } }, providers: [
4200
4424
  DatagridIfExpandService,
4201
4425
  { provide: IfExpandService, useExisting: DatagridIfExpandService },
4202
4426
  { provide: LoadingListener, useExisting: DatagridIfExpandService },
4203
- ], queries: [{ propertyName: "dgCells", predicate: ClrDatagridCell }], viewQueries: [{ propertyName: "expandAnimation", first: true, predicate: ClrExpandableAnimationDirective, descendants: true }, { propertyName: "detailButton", first: true, predicate: ["detailButton"], descendants: true }, { propertyName: "_stickyCells", first: true, predicate: ["stickyCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_scrollableCells", first: true, predicate: ["scrollableCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_calculatedCells", first: true, predicate: ["calculatedCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_fixedCellTemplate", first: true, predicate: ["fixedCellTemplate"], descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n<div\n role=\"row\"\n [id]=\"id\"\n class=\"datagrid-row-master datagrid-row-flex\"\n [clrExpandableAnimation]=\"expandAnimationTrigger\"\n (mousedown)=\"clearRanges($event)\"\n (click)=\"selectRow(!selected, $event)\"\n [class.datagrid-row-clickable]=\"selection.rowSelectionMode\"\n [class.datagrid-row-detail-open]=\"detailService.isRowOpen(item)\"\n>\n <div class=\"datagrid-row-sticky\">\n <!-- Sticky elements here -->\n <ng-container #stickyCells>\n @if (selection.selectionType === SELECTION_TYPE.Multi) {\n <div\n class=\"datagrid-select datagrid-fixed-column datagrid-cell\"\n [ngClass]=\"{ 'clr-form-control-disabled': !clrDgSelectable }\"\n role=\"gridcell\"\n >\n <div class=\"clr-checkbox-wrapper\">\n <input\n tabindex=\"-1\"\n type=\"checkbox\"\n [ngModel]=\"selected\"\n (ngModelChange)=\"toggle($event)\"\n [id]=\"checkboxId\"\n [disabled]=\"clrDgSelectable ? null : true\"\n [attr.aria-disabled]=\"clrDgSelectable ? null : true\"\n />\n <!-- Usage of class clr-col-null here prevents clr-col-* classes from being added when a datagrid is wrapped inside clrForm -->\n <label [for]=\"checkboxId\" class=\"clr-control-label clr-col-null\" (click)=\"clearRanges($event)\">\n <span class=\"clr-sr-only\">{{clrDgRowSelectionLabel || commonStrings.keys.select}}</span>\n </label>\n </div>\n </div>\n } @if (selection.selectionType === SELECTION_TYPE.Single) {\n <div\n class=\"datagrid-select datagrid-fixed-column datagrid-cell\"\n [ngClass]=\"{ 'clr-form-control-disabled': !clrDgSelectable }\"\n role=\"gridcell\"\n >\n <clr-radio-wrapper>\n <input\n tabindex=\"-1\"\n type=\"radio\"\n clrRadio\n clrDgSingleSelectionRadio\n [clrDgIdentityFn]=\"identifyBy\"\n [id]=\"radioId\"\n [name]=\"selection.id + '-radio'\"\n [value]=\"item\"\n [ngModel]=\"selection.currentSingle\"\n (ngModelChange)=\"selection.current = [$event]\"\n [disabled]=\"clrDgSelectable ? null : true\"\n [attr.aria-disabled]=\"clrDgSelectable ? null : true\"\n />\n <label class=\"clr-control-label clr-col-null\" [for]=\"radioId\">\n <span class=\"clr-sr-only\">{{ clrDgRowSelectionLabel || commonStrings.keys.select }}</span>\n </label>\n </clr-radio-wrapper>\n </div>\n } @if (rowActionService.hasActionableRow) {\n <div class=\"datagrid-row-actions datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n <ng-content select=\"clr-dg-action-overflow\"></ng-content>\n </div>\n } @if (globalExpandable.hasExpandableRow) {\n <div class=\"datagrid-expandable-caret datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n @if (expand.expandable) { @if (!expand.loading) {\n <button\n tabindex=\"-1\"\n (click)=\"toggleExpand($event)\"\n type=\"button\"\n class=\"datagrid-expandable-caret-button\"\n [attr.aria-expanded]=\"expand.expanded\"\n [attr.aria-label]=\"expand.expanded ? clrDgDetailCloseLabel : clrDgDetailOpenLabel\"\n [attr.aria-controls]=\"expand.hasExpandTemplate && !expand.expanded ? null : expandableId\"\n >\n <cds-icon\n shape=\"angle\"\n class=\"datagrid-expandable-caret-icon\"\n [direction]=\"expand.expanded ? 'down' : 'right'\"\n [attr.title]=\"expand.expanded ? commonStrings.keys.collapse : commonStrings.keys.expand\"\n ></cds-icon>\n </button>\n } @if (expand.loading) {\n <clr-spinner clrSmall>{{ commonStrings.keys.loading }}</clr-spinner>\n } }\n </div>\n } @if (detailService.enabled) {\n <div class=\"datagrid-detail-caret datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n @if (!detailHidden) {\n <button\n tabindex=\"-1\"\n (click)=\"openDetails($event, detailButton)\"\n type=\"button\"\n #detailButton\n class=\"datagrid-detail-caret-button\"\n [disabled]=\"detailDisabled\"\n [class.is-open]=\"detailService.isRowOpen(item)\"\n [attr.aria-label]=\"detailService.isRowOpen(item) ? clrDgDetailCloseLabel : clrDgDetailOpenLabel\"\n [attr.aria-expanded]=\"detailService.isRowOpen(item)\"\n [attr.aria-controls]=\"detailService.id\"\n aria-haspopup=\"dialog\"\n >\n <cds-icon\n shape=\"angle-double\"\n [direction]=\"detailService.isRowOpen(item) ? 'left' : 'right'\"\n class=\"datagrid-detail-caret-icon\"\n [attr.title]=\"detailService.isRowOpen(item) ? commonStrings.keys.close: commonStrings.keys.open\"\n ></cds-icon>\n </button>\n }\n </div>\n }\n </ng-container>\n <!-- placeholder for projecting other sticky cells as pinned-->\n </div>\n <div class=\"datagrid-row-scrollable\" [ngClass]=\"{'is-replaced': replaced && expanded}\">\n <div class=\"datagrid-scrolling-cells\">\n <ng-content select=\"clr-dg-cell\"></ng-content>\n <ng-container #scrollableCells></ng-container>\n </div>\n <!-- details here when replace, re-visit when sticky container is used for pinned cells -->\n @if (replaced && !expand.loading) {\n <ng-template [ngTemplateOutlet]=\"detail\"></ng-template>\n } @if (!replaced && !expand.loading) {\n <ng-template [ngTemplateOutlet]=\"detail\"></ng-template>\n }\n </div>\n</div>\n<!--\nWe need the \"project into template\" hacks because we need this in 2 different places\ndepending on whether the details replace the row or not.\n-->\n<ng-template #detail>\n <ng-content select=\"clr-dg-row-detail\"></ng-content>\n</ng-template>\n\n<ng-container #calculatedCells></ng-container>\n\n<ng-template #fixedCellTemplate>\n <div class=\"datagrid-fixed-column datagrid-cell\" role=\"gridcell\"></div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i9.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i9.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i5.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i12.ClrRadio, selector: "[clrRadio]" }, { kind: "component", type: i12.ClrRadioWrapper, selector: "clr-radio-wrapper" }, { kind: "directive", type: i13.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i13.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i13.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.ClrExpandableAnimationDirective, selector: "[clrExpandableAnimation]", inputs: ["clrExpandableAnimation"] }, { kind: "component", type: i14.ClrSpinner, selector: "clr-spinner", inputs: ["clrInline", "clrInverse", "clrSmall", "clrMedium"] }, { kind: "directive", type: ClrDatagridSingleSelectionValueAccessor, selector: "input[type=radio][clrDgSingleSelectionRadio]", inputs: ["value", "clrDgIdentityFn"] }, { kind: "directive", type: ClrDatagridSelectionCellDirective, selector: ".datagrid-select" }] }); }
4427
+ ], queries: [{ propertyName: "dgCells", predicate: ClrDatagridCell }], viewQueries: [{ propertyName: "expandAnimation", first: true, predicate: ClrExpandableAnimationDirective, descendants: true }, { propertyName: "detailButton", first: true, predicate: ["detailButton"], descendants: true }, { propertyName: "_stickyCells", first: true, predicate: ["stickyCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_pinnedCells", first: true, predicate: ["pinnedCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_scrollableCells", first: true, predicate: ["scrollableCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_calculatedCells", first: true, predicate: ["calculatedCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_fixedCellTemplate", first: true, predicate: ["fixedCellTemplate"], descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n<div\n role=\"row\"\n [id]=\"id\"\n class=\"datagrid-row-master datagrid-row-flex\"\n (mousedown)=\"clearRanges($event)\"\n (click)=\"selectRow(!selected, $event)\"\n [class.datagrid-row-clickable]=\"selection.rowSelectionMode\"\n [class.datagrid-row-detail-open]=\"detailService.isRowOpen(item)\"\n>\n <div class=\"datagrid-row-sticky\">\n <!-- Sticky elements here -->\n <ng-container #stickyCells>\n @if (selection.selectionType === SELECTION_TYPE.Multi) {\n <div\n class=\"datagrid-select datagrid-fixed-column datagrid-cell\"\n [ngClass]=\"{ 'clr-form-control-disabled': !clrDgSelectable }\"\n role=\"gridcell\"\n >\n <div class=\"clr-checkbox-wrapper\">\n <input\n tabindex=\"-1\"\n type=\"checkbox\"\n [ngModel]=\"selected\"\n (ngModelChange)=\"toggle($event)\"\n [id]=\"checkboxId\"\n [disabled]=\"clrDgSelectable ? null : true\"\n [attr.aria-disabled]=\"clrDgSelectable ? null : true\"\n />\n <!-- Usage of class clr-col-null here prevents clr-col-* classes from being added when a datagrid is wrapped inside clrForm -->\n <label [for]=\"checkboxId\" class=\"clr-control-label clr-col-null\" (click)=\"clearRanges($event)\">\n <span class=\"clr-sr-only\">{{clrDgRowSelectionLabel || commonStrings.keys.select}}</span>\n </label>\n </div>\n </div>\n } @if (selection.selectionType === SELECTION_TYPE.Single) {\n <div\n class=\"datagrid-select datagrid-fixed-column datagrid-cell\"\n [ngClass]=\"{ 'clr-form-control-disabled': !clrDgSelectable }\"\n role=\"gridcell\"\n >\n <clr-radio-wrapper>\n <input\n tabindex=\"-1\"\n type=\"radio\"\n clrRadio\n clrDgSingleSelectionRadio\n [clrDgIdentityFn]=\"identifyBy\"\n [id]=\"radioId\"\n [name]=\"selection.id + '-radio'\"\n [value]=\"item\"\n [ngModel]=\"selection.currentSingle\"\n (ngModelChange)=\"selection.current = [$event]\"\n [disabled]=\"clrDgSelectable ? null : true\"\n [attr.aria-disabled]=\"clrDgSelectable ? null : true\"\n />\n <label class=\"clr-control-label clr-col-null\" [for]=\"radioId\">\n <span class=\"clr-sr-only\">{{ clrDgRowSelectionLabel || commonStrings.keys.select }}</span>\n </label>\n </clr-radio-wrapper>\n </div>\n } @if (rowActionService.hasActionableRow) {\n <div class=\"datagrid-row-actions datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n <ng-content select=\"clr-dg-action-overflow\"></ng-content>\n </div>\n } @if (globalExpandable.hasExpandableRow) {\n <div class=\"datagrid-expandable-caret datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n @if (expand.expandable) { @if (!expand.loading) {\n <button\n tabindex=\"-1\"\n (click)=\"toggleExpand($event)\"\n type=\"button\"\n class=\"datagrid-expandable-caret-button\"\n [attr.aria-expanded]=\"expand.expanded\"\n [attr.aria-label]=\"expand.expanded ? clrDgDetailCloseLabel : clrDgDetailOpenLabel\"\n [attr.aria-controls]=\"expand.hasExpandTemplate && !expand.expanded ? null : expandableId\"\n >\n <cds-icon\n shape=\"angle\"\n class=\"datagrid-expandable-caret-icon\"\n [direction]=\"expand.expanded ? 'down' : 'right'\"\n [attr.title]=\"expand.expanded ? commonStrings.keys.collapse : commonStrings.keys.expand\"\n ></cds-icon>\n </button>\n } @if (expand.loading) {\n <clr-spinner clrSmall>{{ commonStrings.keys.loading }}</clr-spinner>\n } }\n </div>\n } @if (detailService.enabled) {\n <div class=\"datagrid-detail-caret datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n @if (!detailHidden) {\n <button\n tabindex=\"-1\"\n (click)=\"openDetails($event, detailButton)\"\n type=\"button\"\n #detailButton\n class=\"datagrid-detail-caret-button\"\n [disabled]=\"detailDisabled\"\n [class.is-open]=\"detailService.isRowOpen(item)\"\n [attr.aria-label]=\"detailService.isRowOpen(item) ? clrDgDetailCloseLabel : clrDgDetailOpenLabel\"\n [attr.aria-expanded]=\"detailService.isRowOpen(item)\"\n [attr.aria-controls]=\"detailService.id\"\n aria-haspopup=\"dialog\"\n >\n <cds-icon\n shape=\"angle-double\"\n [direction]=\"detailService.isRowOpen(item) ? 'left' : 'right'\"\n class=\"datagrid-detail-caret-icon\"\n [attr.title]=\"detailService.isRowOpen(item) ? commonStrings.keys.close: commonStrings.keys.open\"\n ></cds-icon>\n </button>\n }\n </div>\n }\n </ng-container>\n </div>\n <div\n class=\"datagrid-row-scrollable\"\n [ngClass]=\"{'is-replaced': replaced && expanded}\"\n [clrExpandableAnimation]=\"expandAnimationTrigger\"\n >\n <div class=\"datagrid-scrolling-cells\">\n <div class=\"datagrid-pinned-cells\">\n <ng-container #pinnedCells></ng-container>\n </div>\n <ng-content select=\"clr-dg-cell\"></ng-content>\n <ng-container #scrollableCells></ng-container>\n </div>\n\n @if (!expand.loading) {\n <ng-template [ngTemplateOutlet]=\"detail\"></ng-template>\n }\n </div>\n</div>\n<!--\nWe need the \"project into template\" hacks because we need this in 2 different places\ndepending on whether the details replace the row or not.\n-->\n<ng-template #detail>\n <ng-content select=\"clr-dg-row-detail\"></ng-content>\n</ng-template>\n\n<ng-container #calculatedCells></ng-container>\n\n<ng-template #fixedCellTemplate>\n <div class=\"datagrid-fixed-column datagrid-cell\" role=\"gridcell\"></div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i10.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i10.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i5.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i13.ClrRadio, selector: "[clrRadio]" }, { kind: "component", type: i13.ClrRadioWrapper, selector: "clr-radio-wrapper" }, { kind: "directive", type: i14.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i14.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i14.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i14.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i14.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.ClrExpandableAnimationDirective, selector: "[clrExpandableAnimation]", inputs: ["clrExpandableAnimation"] }, { kind: "component", type: i15.ClrSpinner, selector: "clr-spinner", inputs: ["clrInline", "clrInverse", "clrSmall", "clrMedium"] }, { kind: "directive", type: ClrDatagridSingleSelectionValueAccessor, selector: "input[type=radio][clrDgSingleSelectionRadio]", inputs: ["value", "clrDgIdentityFn"] }, { kind: "directive", type: ClrDatagridSelectionCellDirective, selector: ".datagrid-select" }] }); }
4204
4428
  }
4205
4429
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridRow, decorators: [{
4206
4430
  type: Component,
@@ -4214,8 +4438,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
4214
4438
  DatagridIfExpandService,
4215
4439
  { provide: IfExpandService, useExisting: DatagridIfExpandService },
4216
4440
  { provide: LoadingListener, useExisting: DatagridIfExpandService },
4217
- ], standalone: false, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n<div\n role=\"row\"\n [id]=\"id\"\n class=\"datagrid-row-master datagrid-row-flex\"\n [clrExpandableAnimation]=\"expandAnimationTrigger\"\n (mousedown)=\"clearRanges($event)\"\n (click)=\"selectRow(!selected, $event)\"\n [class.datagrid-row-clickable]=\"selection.rowSelectionMode\"\n [class.datagrid-row-detail-open]=\"detailService.isRowOpen(item)\"\n>\n <div class=\"datagrid-row-sticky\">\n <!-- Sticky elements here -->\n <ng-container #stickyCells>\n @if (selection.selectionType === SELECTION_TYPE.Multi) {\n <div\n class=\"datagrid-select datagrid-fixed-column datagrid-cell\"\n [ngClass]=\"{ 'clr-form-control-disabled': !clrDgSelectable }\"\n role=\"gridcell\"\n >\n <div class=\"clr-checkbox-wrapper\">\n <input\n tabindex=\"-1\"\n type=\"checkbox\"\n [ngModel]=\"selected\"\n (ngModelChange)=\"toggle($event)\"\n [id]=\"checkboxId\"\n [disabled]=\"clrDgSelectable ? null : true\"\n [attr.aria-disabled]=\"clrDgSelectable ? null : true\"\n />\n <!-- Usage of class clr-col-null here prevents clr-col-* classes from being added when a datagrid is wrapped inside clrForm -->\n <label [for]=\"checkboxId\" class=\"clr-control-label clr-col-null\" (click)=\"clearRanges($event)\">\n <span class=\"clr-sr-only\">{{clrDgRowSelectionLabel || commonStrings.keys.select}}</span>\n </label>\n </div>\n </div>\n } @if (selection.selectionType === SELECTION_TYPE.Single) {\n <div\n class=\"datagrid-select datagrid-fixed-column datagrid-cell\"\n [ngClass]=\"{ 'clr-form-control-disabled': !clrDgSelectable }\"\n role=\"gridcell\"\n >\n <clr-radio-wrapper>\n <input\n tabindex=\"-1\"\n type=\"radio\"\n clrRadio\n clrDgSingleSelectionRadio\n [clrDgIdentityFn]=\"identifyBy\"\n [id]=\"radioId\"\n [name]=\"selection.id + '-radio'\"\n [value]=\"item\"\n [ngModel]=\"selection.currentSingle\"\n (ngModelChange)=\"selection.current = [$event]\"\n [disabled]=\"clrDgSelectable ? null : true\"\n [attr.aria-disabled]=\"clrDgSelectable ? null : true\"\n />\n <label class=\"clr-control-label clr-col-null\" [for]=\"radioId\">\n <span class=\"clr-sr-only\">{{ clrDgRowSelectionLabel || commonStrings.keys.select }}</span>\n </label>\n </clr-radio-wrapper>\n </div>\n } @if (rowActionService.hasActionableRow) {\n <div class=\"datagrid-row-actions datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n <ng-content select=\"clr-dg-action-overflow\"></ng-content>\n </div>\n } @if (globalExpandable.hasExpandableRow) {\n <div class=\"datagrid-expandable-caret datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n @if (expand.expandable) { @if (!expand.loading) {\n <button\n tabindex=\"-1\"\n (click)=\"toggleExpand($event)\"\n type=\"button\"\n class=\"datagrid-expandable-caret-button\"\n [attr.aria-expanded]=\"expand.expanded\"\n [attr.aria-label]=\"expand.expanded ? clrDgDetailCloseLabel : clrDgDetailOpenLabel\"\n [attr.aria-controls]=\"expand.hasExpandTemplate && !expand.expanded ? null : expandableId\"\n >\n <cds-icon\n shape=\"angle\"\n class=\"datagrid-expandable-caret-icon\"\n [direction]=\"expand.expanded ? 'down' : 'right'\"\n [attr.title]=\"expand.expanded ? commonStrings.keys.collapse : commonStrings.keys.expand\"\n ></cds-icon>\n </button>\n } @if (expand.loading) {\n <clr-spinner clrSmall>{{ commonStrings.keys.loading }}</clr-spinner>\n } }\n </div>\n } @if (detailService.enabled) {\n <div class=\"datagrid-detail-caret datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n @if (!detailHidden) {\n <button\n tabindex=\"-1\"\n (click)=\"openDetails($event, detailButton)\"\n type=\"button\"\n #detailButton\n class=\"datagrid-detail-caret-button\"\n [disabled]=\"detailDisabled\"\n [class.is-open]=\"detailService.isRowOpen(item)\"\n [attr.aria-label]=\"detailService.isRowOpen(item) ? clrDgDetailCloseLabel : clrDgDetailOpenLabel\"\n [attr.aria-expanded]=\"detailService.isRowOpen(item)\"\n [attr.aria-controls]=\"detailService.id\"\n aria-haspopup=\"dialog\"\n >\n <cds-icon\n shape=\"angle-double\"\n [direction]=\"detailService.isRowOpen(item) ? 'left' : 'right'\"\n class=\"datagrid-detail-caret-icon\"\n [attr.title]=\"detailService.isRowOpen(item) ? commonStrings.keys.close: commonStrings.keys.open\"\n ></cds-icon>\n </button>\n }\n </div>\n }\n </ng-container>\n <!-- placeholder for projecting other sticky cells as pinned-->\n </div>\n <div class=\"datagrid-row-scrollable\" [ngClass]=\"{'is-replaced': replaced && expanded}\">\n <div class=\"datagrid-scrolling-cells\">\n <ng-content select=\"clr-dg-cell\"></ng-content>\n <ng-container #scrollableCells></ng-container>\n </div>\n <!-- details here when replace, re-visit when sticky container is used for pinned cells -->\n @if (replaced && !expand.loading) {\n <ng-template [ngTemplateOutlet]=\"detail\"></ng-template>\n } @if (!replaced && !expand.loading) {\n <ng-template [ngTemplateOutlet]=\"detail\"></ng-template>\n }\n </div>\n</div>\n<!--\nWe need the \"project into template\" hacks because we need this in 2 different places\ndepending on whether the details replace the row or not.\n-->\n<ng-template #detail>\n <ng-content select=\"clr-dg-row-detail\"></ng-content>\n</ng-template>\n\n<ng-container #calculatedCells></ng-container>\n\n<ng-template #fixedCellTemplate>\n <div class=\"datagrid-fixed-column datagrid-cell\" role=\"gridcell\"></div>\n</ng-template>\n" }]
4218
- }], ctorParameters: () => [{ type: Selection }, { type: RowActionService }, { type: ExpandableRowsCount }, { type: DatagridIfExpandService }, { type: DetailService }, { type: DisplayModeService }, { type: i0.ViewContainerRef }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i2.ClrCommonStringsService }, { type: Items }, { type: undefined, decorators: [{
4441
+ ], standalone: false, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n<div\n role=\"row\"\n [id]=\"id\"\n class=\"datagrid-row-master datagrid-row-flex\"\n (mousedown)=\"clearRanges($event)\"\n (click)=\"selectRow(!selected, $event)\"\n [class.datagrid-row-clickable]=\"selection.rowSelectionMode\"\n [class.datagrid-row-detail-open]=\"detailService.isRowOpen(item)\"\n>\n <div class=\"datagrid-row-sticky\">\n <!-- Sticky elements here -->\n <ng-container #stickyCells>\n @if (selection.selectionType === SELECTION_TYPE.Multi) {\n <div\n class=\"datagrid-select datagrid-fixed-column datagrid-cell\"\n [ngClass]=\"{ 'clr-form-control-disabled': !clrDgSelectable }\"\n role=\"gridcell\"\n >\n <div class=\"clr-checkbox-wrapper\">\n <input\n tabindex=\"-1\"\n type=\"checkbox\"\n [ngModel]=\"selected\"\n (ngModelChange)=\"toggle($event)\"\n [id]=\"checkboxId\"\n [disabled]=\"clrDgSelectable ? null : true\"\n [attr.aria-disabled]=\"clrDgSelectable ? null : true\"\n />\n <!-- Usage of class clr-col-null here prevents clr-col-* classes from being added when a datagrid is wrapped inside clrForm -->\n <label [for]=\"checkboxId\" class=\"clr-control-label clr-col-null\" (click)=\"clearRanges($event)\">\n <span class=\"clr-sr-only\">{{clrDgRowSelectionLabel || commonStrings.keys.select}}</span>\n </label>\n </div>\n </div>\n } @if (selection.selectionType === SELECTION_TYPE.Single) {\n <div\n class=\"datagrid-select datagrid-fixed-column datagrid-cell\"\n [ngClass]=\"{ 'clr-form-control-disabled': !clrDgSelectable }\"\n role=\"gridcell\"\n >\n <clr-radio-wrapper>\n <input\n tabindex=\"-1\"\n type=\"radio\"\n clrRadio\n clrDgSingleSelectionRadio\n [clrDgIdentityFn]=\"identifyBy\"\n [id]=\"radioId\"\n [name]=\"selection.id + '-radio'\"\n [value]=\"item\"\n [ngModel]=\"selection.currentSingle\"\n (ngModelChange)=\"selection.current = [$event]\"\n [disabled]=\"clrDgSelectable ? null : true\"\n [attr.aria-disabled]=\"clrDgSelectable ? null : true\"\n />\n <label class=\"clr-control-label clr-col-null\" [for]=\"radioId\">\n <span class=\"clr-sr-only\">{{ clrDgRowSelectionLabel || commonStrings.keys.select }}</span>\n </label>\n </clr-radio-wrapper>\n </div>\n } @if (rowActionService.hasActionableRow) {\n <div class=\"datagrid-row-actions datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n <ng-content select=\"clr-dg-action-overflow\"></ng-content>\n </div>\n } @if (globalExpandable.hasExpandableRow) {\n <div class=\"datagrid-expandable-caret datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n @if (expand.expandable) { @if (!expand.loading) {\n <button\n tabindex=\"-1\"\n (click)=\"toggleExpand($event)\"\n type=\"button\"\n class=\"datagrid-expandable-caret-button\"\n [attr.aria-expanded]=\"expand.expanded\"\n [attr.aria-label]=\"expand.expanded ? clrDgDetailCloseLabel : clrDgDetailOpenLabel\"\n [attr.aria-controls]=\"expand.hasExpandTemplate && !expand.expanded ? null : expandableId\"\n >\n <cds-icon\n shape=\"angle\"\n class=\"datagrid-expandable-caret-icon\"\n [direction]=\"expand.expanded ? 'down' : 'right'\"\n [attr.title]=\"expand.expanded ? commonStrings.keys.collapse : commonStrings.keys.expand\"\n ></cds-icon>\n </button>\n } @if (expand.loading) {\n <clr-spinner clrSmall>{{ commonStrings.keys.loading }}</clr-spinner>\n } }\n </div>\n } @if (detailService.enabled) {\n <div class=\"datagrid-detail-caret datagrid-fixed-column datagrid-cell\" role=\"gridcell\">\n @if (!detailHidden) {\n <button\n tabindex=\"-1\"\n (click)=\"openDetails($event, detailButton)\"\n type=\"button\"\n #detailButton\n class=\"datagrid-detail-caret-button\"\n [disabled]=\"detailDisabled\"\n [class.is-open]=\"detailService.isRowOpen(item)\"\n [attr.aria-label]=\"detailService.isRowOpen(item) ? clrDgDetailCloseLabel : clrDgDetailOpenLabel\"\n [attr.aria-expanded]=\"detailService.isRowOpen(item)\"\n [attr.aria-controls]=\"detailService.id\"\n aria-haspopup=\"dialog\"\n >\n <cds-icon\n shape=\"angle-double\"\n [direction]=\"detailService.isRowOpen(item) ? 'left' : 'right'\"\n class=\"datagrid-detail-caret-icon\"\n [attr.title]=\"detailService.isRowOpen(item) ? commonStrings.keys.close: commonStrings.keys.open\"\n ></cds-icon>\n </button>\n }\n </div>\n }\n </ng-container>\n </div>\n <div\n class=\"datagrid-row-scrollable\"\n [ngClass]=\"{'is-replaced': replaced && expanded}\"\n [clrExpandableAnimation]=\"expandAnimationTrigger\"\n >\n <div class=\"datagrid-scrolling-cells\">\n <div class=\"datagrid-pinned-cells\">\n <ng-container #pinnedCells></ng-container>\n </div>\n <ng-content select=\"clr-dg-cell\"></ng-content>\n <ng-container #scrollableCells></ng-container>\n </div>\n\n @if (!expand.loading) {\n <ng-template [ngTemplateOutlet]=\"detail\"></ng-template>\n }\n </div>\n</div>\n<!--\nWe need the \"project into template\" hacks because we need this in 2 different places\ndepending on whether the details replace the row or not.\n-->\n<ng-template #detail>\n <ng-content select=\"clr-dg-row-detail\"></ng-content>\n</ng-template>\n\n<ng-container #calculatedCells></ng-container>\n\n<ng-template #fixedCellTemplate>\n <div class=\"datagrid-fixed-column datagrid-cell\" role=\"gridcell\"></div>\n</ng-template>\n" }]
4442
+ }], ctorParameters: () => [{ type: Selection }, { type: RowActionService }, { type: ExpandableRowsCount }, { type: DatagridIfExpandService }, { type: DetailService }, { type: DisplayModeService }, { type: i0.ViewContainerRef }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i2.ClrCommonStringsService }, { type: Items }, { type: ColumnsService }, { type: undefined, decorators: [{
4219
4443
  type: Inject,
4220
4444
  args: [DOCUMENT]
4221
4445
  }] }], propDecorators: { selectedChanged: [{
@@ -4245,6 +4469,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
4245
4469
  }], _stickyCells: [{
4246
4470
  type: ViewChild,
4247
4471
  args: ['stickyCells', { read: ViewContainerRef }]
4472
+ }], _pinnedCells: [{
4473
+ type: ViewChild,
4474
+ args: ['pinnedCells', { read: ViewContainerRef }]
4248
4475
  }], _scrollableCells: [{
4249
4476
  type: ViewChild,
4250
4477
  args: ['scrollableCells', { read: ViewContainerRef }]
@@ -4586,81 +4813,6 @@ function createCdkVirtualForOfDirective(viewContainerRef, templateRef, iterableD
4586
4813
  return cdkVirtualForInjector.get(CdkVirtualForOf);
4587
4814
  }
4588
4815
 
4589
- /*
4590
- * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
4591
- * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
4592
- * This software is released under MIT license.
4593
- * The full license information can be found in LICENSE in the root directory of this project.
4594
- */
4595
- var DatagridColumnChanges;
4596
- (function (DatagridColumnChanges) {
4597
- DatagridColumnChanges[DatagridColumnChanges["WIDTH"] = 0] = "WIDTH";
4598
- DatagridColumnChanges[DatagridColumnChanges["HIDDEN"] = 1] = "HIDDEN";
4599
- DatagridColumnChanges[DatagridColumnChanges["INITIALIZE"] = 2] = "INITIALIZE";
4600
- })(DatagridColumnChanges || (DatagridColumnChanges = {}));
4601
- const ALL_COLUMN_CHANGES = Object.keys(DatagridColumnChanges)
4602
- .map(key => DatagridColumnChanges[key])
4603
- .filter(key => key === parseInt(key, 10) && key !== DatagridColumnChanges.INITIALIZE); // extracts only integer keys
4604
-
4605
- /*
4606
- * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
4607
- * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
4608
- * This software is released under MIT license.
4609
- * The full license information can be found in LICENSE in the root directory of this project.
4610
- */
4611
- class ColumnsService {
4612
- constructor() {
4613
- this.columns = [];
4614
- this.columnsStateChange = new BehaviorSubject(null);
4615
- this._cache = [];
4616
- }
4617
- get columnStates() {
4618
- return this.columns.map(column => column.value);
4619
- }
4620
- get hasHideableColumns() {
4621
- return this.columnStates.filter(state => state.hideable).length > 0;
4622
- }
4623
- get visibleColumns() {
4624
- return this.columnStates.filter(state => !state.hidden);
4625
- }
4626
- cache() {
4627
- this._cache = this.columns.map(subject => {
4628
- const value = { ...subject.value };
4629
- delete value.changes;
4630
- return value;
4631
- });
4632
- }
4633
- hasCache() {
4634
- return !!this._cache.length;
4635
- }
4636
- resetToLastCache() {
4637
- this._cache.forEach((state, index) => {
4638
- // Just emit the exact value from the cache
4639
- const cachedState = { ...state, changes: ALL_COLUMN_CHANGES };
4640
- this.columns[index].next(cachedState);
4641
- this.columnsStateChange.next(cachedState);
4642
- });
4643
- this._cache = [];
4644
- }
4645
- // Helper method to emit a change to a column only when there is an actual diff to process for that column
4646
- emitStateChangeAt(columnIndex, diff) {
4647
- if (!this.columns[columnIndex]) {
4648
- return;
4649
- }
4650
- this.emitStateChange(this.columns[columnIndex], diff);
4651
- }
4652
- emitStateChange(column, diff) {
4653
- const changedState = { ...column.value, ...diff };
4654
- column.next(changedState);
4655
- this.columnsStateChange.next(changedState);
4656
- }
4657
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4658
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnsService }); }
4659
- }
4660
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColumnsService, decorators: [{
4661
- type: Injectable
4662
- }] });
4663
-
4664
4816
  /*
4665
4817
  * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
4666
4818
  * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
@@ -4750,6 +4902,7 @@ class DatagridCellRenderer {
4750
4902
  resetState(state) {
4751
4903
  this.setWidth(state);
4752
4904
  this.setHidden(state);
4905
+ this.setPinned(state);
4753
4906
  }
4754
4907
  setWidth(state) {
4755
4908
  if (state.strictWidth) {
@@ -4768,6 +4921,14 @@ class DatagridCellRenderer {
4768
4921
  this.renderer.removeClass(this.el.nativeElement, HIDDEN_COLUMN_CLASS);
4769
4922
  }
4770
4923
  }
4924
+ setPinned(state) {
4925
+ if (state.pinned) {
4926
+ this.renderer.addClass(this.el.nativeElement, PINNED_COLUMN_CLASS);
4927
+ }
4928
+ else {
4929
+ this.renderer.removeClass(this.el.nativeElement, PINNED_COLUMN_CLASS);
4930
+ }
4931
+ }
4771
4932
  clearWidth() {
4772
4933
  this.renderer.removeClass(this.el.nativeElement, STRICT_WIDTH_CLASS);
4773
4934
  this.renderer.setStyle(this.el.nativeElement, 'width', null);
@@ -4941,7 +5102,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
4941
5102
  * The full license information can be found in LICENSE in the root directory of this project.
4942
5103
  */
4943
5104
  class ClrDatagrid {
4944
- constructor(organizer, items, expandableRows, selection, rowActionService, stateProvider, displayMode, renderer, detailService, document, el, page, commonStrings, keyNavigation, zone) {
5105
+ constructor(organizer, items, expandableRows, selection, rowActionService, stateProvider, displayMode, renderer, detailService, document, el, page, commonStrings, keyNavigation, zone, columnsService) {
4945
5106
  this.organizer = organizer;
4946
5107
  this.items = items;
4947
5108
  this.expandableRows = expandableRows;
@@ -4957,6 +5118,7 @@ class ClrDatagrid {
4957
5118
  this.commonStrings = commonStrings;
4958
5119
  this.keyNavigation = keyNavigation;
4959
5120
  this.zone = zone;
5121
+ this.columnsService = columnsService;
4960
5122
  this.clrDgSingleSelectionAriaLabel = this.commonStrings.keys.singleSelectionAriaLabel;
4961
5123
  this.clrDgSingleActionableAriaLabel = this.commonStrings.keys.singleActionableAriaLabel;
4962
5124
  this.clrDetailExpandableAriaLabel = this.commonStrings.keys.detailExpandableAriaLabel;
@@ -4987,6 +5149,11 @@ class ClrDatagrid {
4987
5149
  this.handleResizeChanges(entries);
4988
5150
  });
4989
5151
  });
5152
+ /**
5153
+ * Watches the row controls for anything that changes their width without going through a render
5154
+ * cycle - a density change, or an application restyling them.
5155
+ */
5156
+ this.rowControlsObserver = new ResizeObserver(() => this.updateRowControlsWidth());
4990
5157
  const datagridId = uniqueIdFactory();
4991
5158
  this.selectAllId = 'clr-dg-select-all-' + datagridId;
4992
5159
  detailService.id = datagridId;
@@ -5060,6 +5227,9 @@ class ClrDatagrid {
5060
5227
  get virtualScroll() {
5061
5228
  return this._virtualScroll?.get(0);
5062
5229
  }
5230
+ get isDetailOverlay() {
5231
+ return this.detailService.detailWidth === MAX_DETAIL_WIDTH;
5232
+ }
5063
5233
  ngAfterContentInit() {
5064
5234
  if (!this.items.smart) {
5065
5235
  this.items.all = this.rows.map((row) => row.item);
@@ -5109,6 +5279,7 @@ class ClrDatagrid {
5109
5279
  ngAfterViewInit() {
5110
5280
  this.keyNavigation.initializeKeyGrid(this.el.nativeElement);
5111
5281
  this.updateDetailState();
5282
+ this.zone.runOutsideAngular(() => this.rowControlsObserver.observe(this.rowControls.nativeElement));
5112
5283
  // Emit the state only if it is not an empty object.
5113
5284
  // Default state of `ClrDatagridStateInterface` is an empty object.
5114
5285
  // The refresh emit is needed in Server-driven datagrid when pagination is set, but the data is still not requested.
@@ -5135,6 +5306,10 @@ class ClrDatagrid {
5135
5306
  for (let i = this._projectedDisplayColumns.length; i > 0; i--) {
5136
5307
  this._projectedDisplayColumns.detach();
5137
5308
  }
5309
+ // Remove any projected columns from the projectedStickyColumns container
5310
+ for (let i = this._projectedStickyColumns.length; i > 0; i--) {
5311
+ this._projectedStickyColumns.detach();
5312
+ }
5138
5313
  // Remove any projected columns from the projectedCalculationColumns container
5139
5314
  for (let i = this._projectedCalculationColumns.length; i > 0; i--) {
5140
5315
  this._projectedCalculationColumns.detach();
@@ -5150,12 +5325,21 @@ class ClrDatagrid {
5150
5325
  if (viewChange === DatagridDisplayMode.DISPLAY) {
5151
5326
  // Set state, style for the datagrid to DISPLAY and insert row & columns into containers
5152
5327
  this.renderer.removeClass(this.el.nativeElement, 'datagrid-calculate-mode');
5153
- this.columns.forEach(column => {
5154
- this._projectedDisplayColumns.insert(column._view);
5328
+ // Pinned columns go into the pinned container so they stay visible during horizontal
5329
+ // scroll. Iterating in declaration order keeps both groups in their original order,
5330
+ // which is what matches them with the cells of every row.
5331
+ this.columns.forEach((column, index) => {
5332
+ const container = this.columnsService.isPinned(index)
5333
+ ? this._projectedStickyColumns
5334
+ : this._projectedDisplayColumns;
5335
+ container.insert(column._view);
5155
5336
  });
5156
5337
  this.rows.forEach(row => {
5157
5338
  this._displayedRows.insert(row._view);
5158
5339
  });
5340
+ // The row controls are only laid out in this mode, and this is also the point where
5341
+ // their number can have changed.
5342
+ this.updateRowControlsWidth();
5159
5343
  }
5160
5344
  else {
5161
5345
  // Set state, style for the datagrid to CALCULATE and insert row & columns into containers
@@ -5203,6 +5387,7 @@ class ClrDatagrid {
5203
5387
  this._subscriptions.forEach((sub) => sub.unsubscribe());
5204
5388
  this._virtualScrollSubscriptions.forEach((sub) => sub.unsubscribe());
5205
5389
  this.resizeObserver.disconnect();
5390
+ this.rowControlsObserver.disconnect();
5206
5391
  }
5207
5392
  toggleAllSelected($event) {
5208
5393
  $event.preventDefault();
@@ -5277,6 +5462,42 @@ class ClrDatagrid {
5277
5462
  this._virtualScrollSubscriptions = [];
5278
5463
  }
5279
5464
  }
5465
+ /**
5466
+ * Publishes the width of the row controls - the static container holding the select, action and
5467
+ * caret cells - as a custom property, which is where the pinned columns read the offset they
5468
+ * became static at. They are static right after the controls rather than at the edge of the datagrid,
5469
+ * and `position: sticky` always measures its offsets from that edge, so the distance between the
5470
+ * two has to be measured and handed to the stylesheet.
5471
+ *
5472
+ * The header and the rows get their own value: the header renders one caret column for both
5473
+ * kinds of caret where a row renders one cell per caret, so the two containers are not
5474
+ * necessarily the same width.
5475
+ *
5476
+ * Nothing reads this property unless a column is pinned - `.datagrid-pinned-cells` is the only
5477
+ * consumer, and it is `display: none` while empty - so the measurement is skipped entirely
5478
+ * otherwise. It matters on a datagrid with many rows: `getBoundingClientRect()` forces the
5479
+ * browser to resolve any pending layout first, and on a large datagrid that resolution is not
5480
+ * cheap. Both boxes are read before either write for the same reason - writing the custom
5481
+ * property is itself layout-affecting (`.datagrid-pinned-cells` reads it back for its own
5482
+ * position and width), so reading again in between would force that resolution twice.
5483
+ */
5484
+ updateRowControlsWidth() {
5485
+ const headerControls = this.rowControls?.nativeElement;
5486
+ if (!headerControls || !this.datagridHeader || !this.columnsService.hasPinnedColumns) {
5487
+ return;
5488
+ }
5489
+ const rowControls = this.rowsWrapper.nativeElement.querySelector('.datagrid-row-master > .datagrid-row-sticky:not(.datagrid-row-sticky-scroll)');
5490
+ // Measured from the box rather than from offsetWidth, which is rounded to whole pixels and
5491
+ // would leave the pinned columns off by up to a pixel.
5492
+ const headerWidth = headerControls.getBoundingClientRect().width;
5493
+ // Without a row there is nothing to line up with, so the header is a good enough stand-in.
5494
+ const rowWidth = (rowControls || headerControls).getBoundingClientRect().width;
5495
+ this.setRowControlsWidth(this.datagridHeader.nativeElement, headerWidth);
5496
+ this.setRowControlsWidth(this.rowsWrapper.nativeElement, rowWidth);
5497
+ }
5498
+ setRowControlsWidth(target, width) {
5499
+ this.renderer.setStyle(target, '--clr-datagrid-row-controls-width', `${width}px`, RendererStyleFlags2.DashCase);
5500
+ }
5280
5501
  handleResizeChanges(entries) {
5281
5502
  const rowsWrapper = this.rowsWrapper.nativeElement;
5282
5503
  for (const entry of entries) {
@@ -5295,8 +5516,8 @@ class ClrDatagrid {
5295
5516
  this.renderer.removeClass(rowsWrapper, scrollClass);
5296
5517
  }
5297
5518
  }
5298
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagrid, deps: [{ token: DatagridRenderOrganizer }, { token: Items }, { token: ExpandableRowsCount }, { token: Selection }, { token: RowActionService }, { token: StateProvider }, { token: DisplayModeService }, { token: i0.Renderer2 }, { token: DetailService }, { token: DOCUMENT }, { token: i0.ElementRef }, { token: Page }, { token: i2.ClrCommonStringsService }, { token: KeyNavigationGridController }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
5299
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagrid, isStandalone: false, selector: "clr-datagrid", inputs: { loadingMoreItems: ["clrLoadingMoreItems", "loadingMoreItems"], clrDgSingleSelectionAriaLabel: "clrDgSingleSelectionAriaLabel", clrDgSingleActionableAriaLabel: "clrDgSingleActionableAriaLabel", clrDetailExpandableAriaLabel: "clrDetailExpandableAriaLabel", clrDgDisablePageFocus: "clrDgDisablePageFocus", customSelectAllEnabled: ["clrDgCustomSelectAllEnabled", "customSelectAllEnabled"], selectAllDisabled: ["clrDgSelectAllDisabled", "selectAllDisabled"], loading: ["clrDgLoading", "loading"], selectionType: ["clrDgSelectionType", "selectionType", selectionTypeAttribute], selected: ["clrDgSelected", "selected"], clrDgPreserveSelection: "clrDgPreserveSelection", rowSelectionMode: ["clrDgRowSelection", "rowSelectionMode"], identityFn: ["clrDgItemsIdentityFn", "identityFn"] }, outputs: { selectedChanged: "clrDgSelectedChange", refresh: "clrDgRefresh", customSelectAll: "clrDgCustomSelectAll" }, host: { properties: { "class.datagrid-host": "true", "class.datagrid-detail-open": "detailService.isOpen", "class.datagrid-virtual-scroll": "!!virtualScroll" } }, providers: [
5519
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagrid, deps: [{ token: DatagridRenderOrganizer }, { token: Items }, { token: ExpandableRowsCount }, { token: Selection }, { token: RowActionService }, { token: StateProvider }, { token: DisplayModeService }, { token: i0.Renderer2 }, { token: DetailService }, { token: DOCUMENT }, { token: i0.ElementRef }, { token: Page }, { token: i2.ClrCommonStringsService }, { token: KeyNavigationGridController }, { token: i0.NgZone }, { token: ColumnsService }], target: i0.ɵɵFactoryTarget.Component }); }
5520
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagrid, isStandalone: false, selector: "clr-datagrid", inputs: { loadingMoreItems: ["clrLoadingMoreItems", "loadingMoreItems"], clrDgSingleSelectionAriaLabel: "clrDgSingleSelectionAriaLabel", clrDgSingleActionableAriaLabel: "clrDgSingleActionableAriaLabel", clrDetailExpandableAriaLabel: "clrDetailExpandableAriaLabel", clrDgDisablePageFocus: "clrDgDisablePageFocus", customSelectAllEnabled: ["clrDgCustomSelectAllEnabled", "customSelectAllEnabled"], selectAllDisabled: ["clrDgSelectAllDisabled", "selectAllDisabled"], loading: ["clrDgLoading", "loading"], selectionType: ["clrDgSelectionType", "selectionType", selectionTypeAttribute], selected: ["clrDgSelected", "selected"], clrDgPreserveSelection: "clrDgPreserveSelection", rowSelectionMode: ["clrDgRowSelection", "rowSelectionMode"], identityFn: ["clrDgItemsIdentityFn", "identityFn"] }, outputs: { selectedChanged: "clrDgSelectedChange", refresh: "clrDgRefresh", customSelectAll: "clrDgCustomSelectAll" }, host: { properties: { "class.datagrid-host": "true", "class.datagrid-detail-open": "detailService.isOpen", "class.datagrid-detail-overlay": "isDetailOverlay", "class.datagrid-virtual-scroll": "!!virtualScroll" } }, providers: [
5300
5521
  Selection,
5301
5522
  Sort,
5302
5523
  FiltersProvider,
@@ -5312,7 +5533,7 @@ class ClrDatagrid {
5312
5533
  ColumnsService,
5313
5534
  DisplayModeService,
5314
5535
  KeyNavigationGridController,
5315
- ], queries: [{ propertyName: "iterator", first: true, predicate: ClrDatagridItems, descendants: true }, { propertyName: "placeholder", first: true, predicate: ClrDatagridPlaceholder, descendants: true }, { propertyName: "_virtualScroll", predicate: i0.forwardRef(() => ClrDatagridVirtualScrollDirective) }, { propertyName: "columns", predicate: ClrDatagridColumn }, { propertyName: "rows", predicate: ClrDatagridRow, emitDistinctChangesOnly: false }], viewQueries: [{ propertyName: "datagrid", first: true, predicate: ["datagrid"], descendants: true, read: ElementRef }, { propertyName: "datagridTable", first: true, predicate: ["datagridTable"], descendants: true, read: ElementRef }, { propertyName: "datagridHeader", first: true, predicate: ["datagridHeader"], descendants: true, read: ElementRef }, { propertyName: "contentWrapper", first: true, predicate: ["contentWrapper"], descendants: true, read: ElementRef, static: true }, { propertyName: "rowsWrapper", first: true, predicate: ["rowsWrapper"], descendants: true, read: ElementRef, static: true }, { propertyName: "scrollableColumns", first: true, predicate: ["scrollableColumns"], descendants: true, read: ViewContainerRef }, { propertyName: "_projectedDisplayColumns", first: true, predicate: ["projectedDisplayColumns"], descendants: true, read: ViewContainerRef }, { propertyName: "_projectedCalculationColumns", first: true, predicate: ["projectedCalculationColumns"], descendants: true, read: ViewContainerRef }, { propertyName: "_displayedRows", first: true, predicate: ["displayedRows"], descendants: true, read: ViewContainerRef }, { propertyName: "_calculationRows", first: true, predicate: ["calculationRows"], descendants: true, read: ViewContainerRef }, { propertyName: "_fixedColumnTemplate", first: true, predicate: ["fixedColumnTemplate"], descendants: true }, { propertyName: "selectAllCheckbox", first: true, predicate: ["selectAllCheckbox"], descendants: true }, { propertyName: "stickyHeaders", predicate: ["stickyHeader"], descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<ng-content select=\"clr-dg-action-bar\"></ng-content>\n<div class=\"datagrid-outer-wrapper\">\n <div class=\"datagrid-inner-wrapper\">\n <div class=\"datagrid\" #datagrid [attr.aria-hidden]=\"detailService.isOpen ? true : null\">\n <div class=\"datagrid-table-wrapper\">\n <div role=\"grid\" class=\"datagrid-table\" tabindex=\"-1\" #datagridTable>\n <div role=\"rowgroup\" class=\"datagrid-header\" #datagridHeader>\n <div role=\"row\" class=\"datagrid-row\">\n <div class=\"datagrid-row-master datagrid-row-flex\">\n <div class=\"datagrid-row-sticky\">\n <!--header for datagrid where you can select multiple rows -->\n @if (selection.selectionType === SELECTION_TYPE.Multi) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-select datagrid-fixed-column\"\n (keydown.space)=\"toggleAllSelected($event)\"\n >\n @if (!virtualScroll || customSelectAllEnabled) {\n <div class=\"clr-checkbox-wrapper\">\n <!-- We need to move focus and space-key handling to the parent because of keyboard arrow key navigation,\n which is not able to transfer focus directly on the input when focused with the tab key -->\n <input\n #selectAllCheckbox\n type=\"checkbox\"\n [id]=\"selectAllId\"\n [(ngModel)]=\"allSelected\"\n [attr.aria-label]=\"commonStrings.keys.selectAll\"\n tabindex=\"-1\"\n [attr.disabled]=\"selectAllDisabled ? true : null\"\n />\n <!-- Usage of class clr-col-null here prevents clr-col-* classes from being added when a datagrid is wrapped inside clrForm -->\n <label [for]=\"selectAllId\" class=\"clr-control-label clr-col-null\">\n <span class=\"clr-sr-only\">{{commonStrings.keys.selectAll}}</span>\n </label>\n </div>\n }\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for datagrid where you can select one row only -->\n @if (selection.selectionType === SELECTION_TYPE.Single) {\n <div #stickyHeader role=\"columnheader\" class=\"datagrid-column datagrid-select datagrid-fixed-column\">\n <div class=\"clr-sr-only\">{{clrDgSingleSelectionAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for single row action; only displayType if we have at least one actionable row in datagrid -->\n @if (rowActionService.hasActionableRow) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-row-actions datagrid-fixed-column\"\n >\n <div class=\"clr-sr-only\">{{clrDgSingleActionableAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for carets; only displayType if we have at least one expandable row in datagrid -->\n @if (expandableRows.hasExpandableRow || detailService.enabled) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-expandable-caret datagrid-fixed-column\"\n >\n <div class=\"clr-sr-only\">{{clrDetailExpandableAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n </div>\n <div class=\"datagrid-row-scrollable\">\n <ng-container #projectedDisplayColumns></ng-container>\n </div>\n @if (virtualScroll) {\n <div class=\"datagrid-row-sticky datagrid-row-sticky-scroll\">\n <div class=\"datagrid-column\"></div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <div class=\"datagrid-content\" [class.datagrid-content-virtual]=\"virtualScroll\" #contentWrapper>\n @if (virtualScroll) {\n <div class=\"datagrid-content-virtual-spacer\" [style.height]=\"virtualScroll?.totalContentHeight\"></div>\n }\n <div role=\"presentation\" #rowsWrapper class=\"datagrid-rows\">\n @if (loadingMoreItems) {\n <clr-dg-row class=\"datagrid-row-loading\">\n <clr-dg-cell>\n <clr-spinner clrMedium></clr-spinner>\n <span>{{ commonStrings.keys.loading }}</span>\n </clr-dg-cell>\n </clr-dg-row>\n }\n\n <ng-container #displayedRows></ng-container>\n\n @if (loadingMoreItems) {\n <clr-dg-row class=\"datagrid-row-loading\">\n <clr-dg-cell>\n <clr-spinner clrMedium></clr-spinner>\n <span>{{ commonStrings.keys.loading }}</span>\n </clr-dg-cell>\n </clr-dg-row>\n }\n\n <!-- Custom placeholder overrides the default empty one -->\n <ng-content select=\"clr-dg-placeholder\"></ng-content>\n @if (!placeholder) {\n <clr-dg-placeholder></clr-dg-placeholder>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <ng-content select=\"clr-dg-footer\"></ng-content>\n @if (loading) {\n <div class=\"datagrid-spinner\">\n <clr-spinner clrMedium>Loading</clr-spinner>\n </div>\n }\n </div>\n <ng-content select=\"[clrIfDetail],clr-dg-detail\"></ng-content>\n</div>\n\n<div class=\"datagrid-calculation-table\">\n <div class=\"datagrid-calculation-header\">\n <ng-container #projectedCalculationColumns></ng-container>\n </div>\n <ng-container #calculationRows></ng-container>\n</div>\n\n<ng-template #fixedColumnTemplate>\n <div class=\"datagrid-column datagrid-fixed-column\"></div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i13.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i14.ClrSpinner, selector: "clr-spinner", inputs: ["clrInline", "clrInverse", "clrSmall", "clrMedium"] }, { kind: "component", type: ClrDatagridCell, selector: "clr-dg-cell" }, { kind: "component", type: ClrDatagridPlaceholder, selector: "clr-dg-placeholder" }, { kind: "component", type: ClrDatagridRow, selector: "clr-dg-row", inputs: ["clrDgDetailDisabled", "clrDgDetailHidden", "clrDgSkeletonLoading", "clrDgItem", "clrDgSelectable", "clrDgSelected", "clrDgExpanded", "clrDgDetailOpenLabel", "clrDgDetailCloseLabel", "clrDgRowSelectionLabel"], outputs: ["clrDgSelectedChange", "clrDgExpandedChange"] }, { kind: "directive", type: ClrDatagridSelectionCellDirective, selector: ".datagrid-select" }, { kind: "directive", type: DatagridCellRenderer, selector: "clr-dg-cell" }, { kind: "directive", type: DatagridRowRenderer, selector: "clr-dg-row" }, { kind: "directive", type: ActionableOompaLoompa, selector: "clr-datagrid, clr-dg-row" }, { kind: "directive", type: ExpandableOompaLoompa, selector: "clr-datagrid, clr-dg-row" }] }); }
5536
+ ], queries: [{ propertyName: "iterator", first: true, predicate: ClrDatagridItems, descendants: true }, { propertyName: "placeholder", first: true, predicate: ClrDatagridPlaceholder, descendants: true }, { propertyName: "_virtualScroll", predicate: i0.forwardRef(() => ClrDatagridVirtualScrollDirective) }, { propertyName: "columns", predicate: ClrDatagridColumn }, { propertyName: "rows", predicate: ClrDatagridRow, emitDistinctChangesOnly: false }], viewQueries: [{ propertyName: "datagrid", first: true, predicate: ["datagrid"], descendants: true, read: ElementRef }, { propertyName: "datagridTable", first: true, predicate: ["datagridTable"], descendants: true, read: ElementRef }, { propertyName: "datagridHeader", first: true, predicate: ["datagridHeader"], descendants: true, read: ElementRef }, { propertyName: "contentWrapper", first: true, predicate: ["contentWrapper"], descendants: true, read: ElementRef, static: true }, { propertyName: "rowsWrapper", first: true, predicate: ["rowsWrapper"], descendants: true, read: ElementRef, static: true }, { propertyName: "scrollableColumns", first: true, predicate: ["scrollableColumns"], descendants: true, read: ViewContainerRef }, { propertyName: "_projectedDisplayColumns", first: true, predicate: ["projectedDisplayColumns"], descendants: true, read: ViewContainerRef }, { propertyName: "_projectedStickyColumns", first: true, predicate: ["projectedStickyColumns"], descendants: true, read: ViewContainerRef }, { propertyName: "_projectedCalculationColumns", first: true, predicate: ["projectedCalculationColumns"], descendants: true, read: ViewContainerRef }, { propertyName: "_displayedRows", first: true, predicate: ["displayedRows"], descendants: true, read: ViewContainerRef }, { propertyName: "_calculationRows", first: true, predicate: ["calculationRows"], descendants: true, read: ViewContainerRef }, { propertyName: "_fixedColumnTemplate", first: true, predicate: ["fixedColumnTemplate"], descendants: true }, { propertyName: "selectAllCheckbox", first: true, predicate: ["selectAllCheckbox"], descendants: true }, { propertyName: "rowControls", first: true, predicate: ["rowControls"], descendants: true, read: ElementRef }, { propertyName: "stickyHeaders", predicate: ["stickyHeader"], descendants: true }], ngImport: i0, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<ng-content select=\"clr-dg-action-bar\"></ng-content>\n<div class=\"datagrid-outer-wrapper\">\n <div class=\"datagrid-inner-wrapper\">\n <div class=\"datagrid\" #datagrid [attr.aria-hidden]=\"detailService.isOpen ? true : null\">\n <div class=\"datagrid-table-wrapper\">\n <div role=\"grid\" class=\"datagrid-table\" tabindex=\"-1\" #datagridTable>\n <div role=\"rowgroup\" class=\"datagrid-header\" #datagridHeader>\n <div role=\"row\" class=\"datagrid-row\">\n <div class=\"datagrid-row-master datagrid-row-flex\">\n <div class=\"datagrid-row-sticky\" #rowControls>\n <!--header for datagrid where you can select multiple rows -->\n @if (selection.selectionType === SELECTION_TYPE.Multi) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-select datagrid-fixed-column\"\n (keydown.space)=\"toggleAllSelected($event)\"\n >\n @if (!virtualScroll || customSelectAllEnabled) {\n <div class=\"clr-checkbox-wrapper\">\n <!-- We need to move focus and space-key handling to the parent because of keyboard arrow key navigation,\n which is not able to transfer focus directly on the input when focused with the tab key -->\n <input\n #selectAllCheckbox\n type=\"checkbox\"\n [id]=\"selectAllId\"\n [(ngModel)]=\"allSelected\"\n [attr.aria-label]=\"commonStrings.keys.selectAll\"\n tabindex=\"-1\"\n [attr.disabled]=\"selectAllDisabled ? true : null\"\n />\n <!-- Usage of class clr-col-null here prevents clr-col-* classes from being added when a datagrid is wrapped inside clrForm -->\n <label [for]=\"selectAllId\" class=\"clr-control-label clr-col-null\">\n <span class=\"clr-sr-only\">{{commonStrings.keys.selectAll}}</span>\n </label>\n </div>\n }\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for datagrid where you can select one row only -->\n @if (selection.selectionType === SELECTION_TYPE.Single) {\n <div #stickyHeader role=\"columnheader\" class=\"datagrid-column datagrid-select datagrid-fixed-column\">\n <div class=\"clr-sr-only\">{{clrDgSingleSelectionAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for single row action; only displayType if we have at least one actionable row in datagrid -->\n @if (rowActionService.hasActionableRow) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-row-actions datagrid-fixed-column\"\n >\n <div class=\"clr-sr-only\">{{clrDgSingleActionableAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for carets; only displayType if we have at least one expandable row in datagrid -->\n @if (expandableRows.hasExpandableRow || detailService.enabled) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-expandable-caret datagrid-fixed-column\"\n >\n <div class=\"clr-sr-only\">{{clrDetailExpandableAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n </div>\n <div class=\"datagrid-row-scrollable\">\n <div class=\"datagrid-pinned-cells\">\n <ng-container #projectedStickyColumns></ng-container>\n </div>\n <ng-container #projectedDisplayColumns></ng-container>\n </div>\n @if (virtualScroll) {\n <div class=\"datagrid-row-sticky datagrid-row-sticky-scroll\">\n <div class=\"datagrid-column\"></div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <div class=\"datagrid-content\" [class.datagrid-content-virtual]=\"virtualScroll\" #contentWrapper>\n @if (virtualScroll) {\n <div class=\"datagrid-content-virtual-spacer\" [style.height]=\"virtualScroll?.totalContentHeight\"></div>\n }\n <div role=\"presentation\" #rowsWrapper class=\"datagrid-rows\">\n @if (loadingMoreItems) {\n <clr-dg-row class=\"datagrid-row-loading\">\n <clr-dg-cell>\n <clr-spinner clrMedium></clr-spinner>\n <span>{{ commonStrings.keys.loading }}</span>\n </clr-dg-cell>\n </clr-dg-row>\n }\n\n <ng-container #displayedRows></ng-container>\n\n @if (loadingMoreItems) {\n <clr-dg-row class=\"datagrid-row-loading\">\n <clr-dg-cell>\n <clr-spinner clrMedium></clr-spinner>\n <span>{{ commonStrings.keys.loading }}</span>\n </clr-dg-cell>\n </clr-dg-row>\n }\n\n <!-- Custom placeholder overrides the default empty one -->\n <ng-content select=\"clr-dg-placeholder\"></ng-content>\n @if (!placeholder) {\n <clr-dg-placeholder></clr-dg-placeholder>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <ng-content select=\"clr-dg-footer\"></ng-content>\n @if (loading) {\n <div class=\"datagrid-spinner\">\n <clr-spinner clrMedium>Loading</clr-spinner>\n </div>\n }\n </div>\n <ng-content select=\"[clrIfDetail],clr-dg-detail\"></ng-content>\n</div>\n\n<div class=\"datagrid-calculation-table\">\n <div class=\"datagrid-calculation-header\">\n <ng-container #projectedCalculationColumns></ng-container>\n </div>\n <ng-container #calculationRows></ng-container>\n</div>\n\n<ng-template #fixedColumnTemplate>\n <div class=\"datagrid-column datagrid-fixed-column\"></div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i14.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i14.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i14.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i15.ClrSpinner, selector: "clr-spinner", inputs: ["clrInline", "clrInverse", "clrSmall", "clrMedium"] }, { kind: "component", type: ClrDatagridCell, selector: "clr-dg-cell" }, { kind: "component", type: ClrDatagridPlaceholder, selector: "clr-dg-placeholder" }, { kind: "component", type: ClrDatagridRow, selector: "clr-dg-row", inputs: ["clrDgDetailDisabled", "clrDgDetailHidden", "clrDgSkeletonLoading", "clrDgItem", "clrDgSelectable", "clrDgSelected", "clrDgExpanded", "clrDgDetailOpenLabel", "clrDgDetailCloseLabel", "clrDgRowSelectionLabel"], outputs: ["clrDgSelectedChange", "clrDgExpandedChange"] }, { kind: "directive", type: ClrDatagridSelectionCellDirective, selector: ".datagrid-select" }, { kind: "directive", type: DatagridCellRenderer, selector: "clr-dg-cell" }, { kind: "directive", type: DatagridRowRenderer, selector: "clr-dg-row" }, { kind: "directive", type: ActionableOompaLoompa, selector: "clr-datagrid, clr-dg-row" }, { kind: "directive", type: ExpandableOompaLoompa, selector: "clr-datagrid, clr-dg-row" }] }); }
5316
5537
  }
5317
5538
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagrid, decorators: [{
5318
5539
  type: Component,
@@ -5335,12 +5556,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
5335
5556
  ], host: {
5336
5557
  '[class.datagrid-host]': 'true',
5337
5558
  '[class.datagrid-detail-open]': 'detailService.isOpen',
5559
+ '[class.datagrid-detail-overlay]': 'isDetailOverlay',
5338
5560
  '[class.datagrid-virtual-scroll]': '!!virtualScroll',
5339
- }, standalone: false, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<ng-content select=\"clr-dg-action-bar\"></ng-content>\n<div class=\"datagrid-outer-wrapper\">\n <div class=\"datagrid-inner-wrapper\">\n <div class=\"datagrid\" #datagrid [attr.aria-hidden]=\"detailService.isOpen ? true : null\">\n <div class=\"datagrid-table-wrapper\">\n <div role=\"grid\" class=\"datagrid-table\" tabindex=\"-1\" #datagridTable>\n <div role=\"rowgroup\" class=\"datagrid-header\" #datagridHeader>\n <div role=\"row\" class=\"datagrid-row\">\n <div class=\"datagrid-row-master datagrid-row-flex\">\n <div class=\"datagrid-row-sticky\">\n <!--header for datagrid where you can select multiple rows -->\n @if (selection.selectionType === SELECTION_TYPE.Multi) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-select datagrid-fixed-column\"\n (keydown.space)=\"toggleAllSelected($event)\"\n >\n @if (!virtualScroll || customSelectAllEnabled) {\n <div class=\"clr-checkbox-wrapper\">\n <!-- We need to move focus and space-key handling to the parent because of keyboard arrow key navigation,\n which is not able to transfer focus directly on the input when focused with the tab key -->\n <input\n #selectAllCheckbox\n type=\"checkbox\"\n [id]=\"selectAllId\"\n [(ngModel)]=\"allSelected\"\n [attr.aria-label]=\"commonStrings.keys.selectAll\"\n tabindex=\"-1\"\n [attr.disabled]=\"selectAllDisabled ? true : null\"\n />\n <!-- Usage of class clr-col-null here prevents clr-col-* classes from being added when a datagrid is wrapped inside clrForm -->\n <label [for]=\"selectAllId\" class=\"clr-control-label clr-col-null\">\n <span class=\"clr-sr-only\">{{commonStrings.keys.selectAll}}</span>\n </label>\n </div>\n }\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for datagrid where you can select one row only -->\n @if (selection.selectionType === SELECTION_TYPE.Single) {\n <div #stickyHeader role=\"columnheader\" class=\"datagrid-column datagrid-select datagrid-fixed-column\">\n <div class=\"clr-sr-only\">{{clrDgSingleSelectionAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for single row action; only displayType if we have at least one actionable row in datagrid -->\n @if (rowActionService.hasActionableRow) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-row-actions datagrid-fixed-column\"\n >\n <div class=\"clr-sr-only\">{{clrDgSingleActionableAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for carets; only displayType if we have at least one expandable row in datagrid -->\n @if (expandableRows.hasExpandableRow || detailService.enabled) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-expandable-caret datagrid-fixed-column\"\n >\n <div class=\"clr-sr-only\">{{clrDetailExpandableAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n </div>\n <div class=\"datagrid-row-scrollable\">\n <ng-container #projectedDisplayColumns></ng-container>\n </div>\n @if (virtualScroll) {\n <div class=\"datagrid-row-sticky datagrid-row-sticky-scroll\">\n <div class=\"datagrid-column\"></div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <div class=\"datagrid-content\" [class.datagrid-content-virtual]=\"virtualScroll\" #contentWrapper>\n @if (virtualScroll) {\n <div class=\"datagrid-content-virtual-spacer\" [style.height]=\"virtualScroll?.totalContentHeight\"></div>\n }\n <div role=\"presentation\" #rowsWrapper class=\"datagrid-rows\">\n @if (loadingMoreItems) {\n <clr-dg-row class=\"datagrid-row-loading\">\n <clr-dg-cell>\n <clr-spinner clrMedium></clr-spinner>\n <span>{{ commonStrings.keys.loading }}</span>\n </clr-dg-cell>\n </clr-dg-row>\n }\n\n <ng-container #displayedRows></ng-container>\n\n @if (loadingMoreItems) {\n <clr-dg-row class=\"datagrid-row-loading\">\n <clr-dg-cell>\n <clr-spinner clrMedium></clr-spinner>\n <span>{{ commonStrings.keys.loading }}</span>\n </clr-dg-cell>\n </clr-dg-row>\n }\n\n <!-- Custom placeholder overrides the default empty one -->\n <ng-content select=\"clr-dg-placeholder\"></ng-content>\n @if (!placeholder) {\n <clr-dg-placeholder></clr-dg-placeholder>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <ng-content select=\"clr-dg-footer\"></ng-content>\n @if (loading) {\n <div class=\"datagrid-spinner\">\n <clr-spinner clrMedium>Loading</clr-spinner>\n </div>\n }\n </div>\n <ng-content select=\"[clrIfDetail],clr-dg-detail\"></ng-content>\n</div>\n\n<div class=\"datagrid-calculation-table\">\n <div class=\"datagrid-calculation-header\">\n <ng-container #projectedCalculationColumns></ng-container>\n </div>\n <ng-container #calculationRows></ng-container>\n</div>\n\n<ng-template #fixedColumnTemplate>\n <div class=\"datagrid-column datagrid-fixed-column\"></div>\n</ng-template>\n" }]
5561
+ }, standalone: false, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<ng-content select=\"clr-dg-action-bar\"></ng-content>\n<div class=\"datagrid-outer-wrapper\">\n <div class=\"datagrid-inner-wrapper\">\n <div class=\"datagrid\" #datagrid [attr.aria-hidden]=\"detailService.isOpen ? true : null\">\n <div class=\"datagrid-table-wrapper\">\n <div role=\"grid\" class=\"datagrid-table\" tabindex=\"-1\" #datagridTable>\n <div role=\"rowgroup\" class=\"datagrid-header\" #datagridHeader>\n <div role=\"row\" class=\"datagrid-row\">\n <div class=\"datagrid-row-master datagrid-row-flex\">\n <div class=\"datagrid-row-sticky\" #rowControls>\n <!--header for datagrid where you can select multiple rows -->\n @if (selection.selectionType === SELECTION_TYPE.Multi) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-select datagrid-fixed-column\"\n (keydown.space)=\"toggleAllSelected($event)\"\n >\n @if (!virtualScroll || customSelectAllEnabled) {\n <div class=\"clr-checkbox-wrapper\">\n <!-- We need to move focus and space-key handling to the parent because of keyboard arrow key navigation,\n which is not able to transfer focus directly on the input when focused with the tab key -->\n <input\n #selectAllCheckbox\n type=\"checkbox\"\n [id]=\"selectAllId\"\n [(ngModel)]=\"allSelected\"\n [attr.aria-label]=\"commonStrings.keys.selectAll\"\n tabindex=\"-1\"\n [attr.disabled]=\"selectAllDisabled ? true : null\"\n />\n <!-- Usage of class clr-col-null here prevents clr-col-* classes from being added when a datagrid is wrapped inside clrForm -->\n <label [for]=\"selectAllId\" class=\"clr-control-label clr-col-null\">\n <span class=\"clr-sr-only\">{{commonStrings.keys.selectAll}}</span>\n </label>\n </div>\n }\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for datagrid where you can select one row only -->\n @if (selection.selectionType === SELECTION_TYPE.Single) {\n <div #stickyHeader role=\"columnheader\" class=\"datagrid-column datagrid-select datagrid-fixed-column\">\n <div class=\"clr-sr-only\">{{clrDgSingleSelectionAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for single row action; only displayType if we have at least one actionable row in datagrid -->\n @if (rowActionService.hasActionableRow) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-row-actions datagrid-fixed-column\"\n >\n <div class=\"clr-sr-only\">{{clrDgSingleActionableAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n <!-- header for carets; only displayType if we have at least one expandable row in datagrid -->\n @if (expandableRows.hasExpandableRow || detailService.enabled) {\n <div\n #stickyHeader\n role=\"columnheader\"\n class=\"datagrid-column datagrid-expandable-caret datagrid-fixed-column\"\n >\n <div class=\"clr-sr-only\">{{clrDetailExpandableAriaLabel}}</div>\n <div class=\"datagrid-column-separator\"></div>\n </div>\n }\n </div>\n <div class=\"datagrid-row-scrollable\">\n <div class=\"datagrid-pinned-cells\">\n <ng-container #projectedStickyColumns></ng-container>\n </div>\n <ng-container #projectedDisplayColumns></ng-container>\n </div>\n @if (virtualScroll) {\n <div class=\"datagrid-row-sticky datagrid-row-sticky-scroll\">\n <div class=\"datagrid-column\"></div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <div class=\"datagrid-content\" [class.datagrid-content-virtual]=\"virtualScroll\" #contentWrapper>\n @if (virtualScroll) {\n <div class=\"datagrid-content-virtual-spacer\" [style.height]=\"virtualScroll?.totalContentHeight\"></div>\n }\n <div role=\"presentation\" #rowsWrapper class=\"datagrid-rows\">\n @if (loadingMoreItems) {\n <clr-dg-row class=\"datagrid-row-loading\">\n <clr-dg-cell>\n <clr-spinner clrMedium></clr-spinner>\n <span>{{ commonStrings.keys.loading }}</span>\n </clr-dg-cell>\n </clr-dg-row>\n }\n\n <ng-container #displayedRows></ng-container>\n\n @if (loadingMoreItems) {\n <clr-dg-row class=\"datagrid-row-loading\">\n <clr-dg-cell>\n <clr-spinner clrMedium></clr-spinner>\n <span>{{ commonStrings.keys.loading }}</span>\n </clr-dg-cell>\n </clr-dg-row>\n }\n\n <!-- Custom placeholder overrides the default empty one -->\n <ng-content select=\"clr-dg-placeholder\"></ng-content>\n @if (!placeholder) {\n <clr-dg-placeholder></clr-dg-placeholder>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n <ng-content select=\"clr-dg-footer\"></ng-content>\n @if (loading) {\n <div class=\"datagrid-spinner\">\n <clr-spinner clrMedium>Loading</clr-spinner>\n </div>\n }\n </div>\n <ng-content select=\"[clrIfDetail],clr-dg-detail\"></ng-content>\n</div>\n\n<div class=\"datagrid-calculation-table\">\n <div class=\"datagrid-calculation-header\">\n <ng-container #projectedCalculationColumns></ng-container>\n </div>\n <ng-container #calculationRows></ng-container>\n</div>\n\n<ng-template #fixedColumnTemplate>\n <div class=\"datagrid-column datagrid-fixed-column\"></div>\n</ng-template>\n" }]
5340
5562
  }], ctorParameters: () => [{ type: DatagridRenderOrganizer }, { type: Items }, { type: ExpandableRowsCount }, { type: Selection }, { type: RowActionService }, { type: StateProvider }, { type: DisplayModeService }, { type: i0.Renderer2 }, { type: DetailService }, { type: undefined, decorators: [{
5341
5563
  type: Inject,
5342
5564
  args: [DOCUMENT]
5343
- }] }, { type: i0.ElementRef }, { type: Page }, { type: i2.ClrCommonStringsService }, { type: KeyNavigationGridController }, { type: i0.NgZone }], propDecorators: { loadingMoreItems: [{
5565
+ }] }, { type: i0.ElementRef }, { type: Page }, { type: i2.ClrCommonStringsService }, { type: KeyNavigationGridController }, { type: i0.NgZone }, { type: ColumnsService }], propDecorators: { loadingMoreItems: [{
5344
5566
  type: Input,
5345
5567
  args: ['clrLoadingMoreItems']
5346
5568
  }], clrDgSingleSelectionAriaLabel: [{
@@ -5402,6 +5624,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
5402
5624
  }], _projectedDisplayColumns: [{
5403
5625
  type: ViewChild,
5404
5626
  args: ['projectedDisplayColumns', { read: ViewContainerRef }]
5627
+ }], _projectedStickyColumns: [{
5628
+ type: ViewChild,
5629
+ args: ['projectedStickyColumns', { read: ViewContainerRef }]
5405
5630
  }], _projectedCalculationColumns: [{
5406
5631
  type: ViewChild,
5407
5632
  args: ['projectedCalculationColumns', { read: ViewContainerRef }]
@@ -5420,6 +5645,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
5420
5645
  }], selectAllCheckbox: [{
5421
5646
  type: ViewChild,
5422
5647
  args: ['selectAllCheckbox']
5648
+ }], rowControls: [{
5649
+ type: ViewChild,
5650
+ args: ['rowControls', { read: ElementRef }]
5423
5651
  }], loading: [{
5424
5652
  type: Input,
5425
5653
  args: ['clrDgLoading']
@@ -5804,7 +6032,7 @@ class ClrDatagridColumnToggle {
5804
6032
  <clr-dg-column-toggle-button (clrAllSelected)="allColumnsSelected()"></clr-dg-column-toggle-button>
5805
6033
  </div>
5806
6034
  </div>
5807
- `, isInline: true, dependencies: [{ kind: "directive", type: i9.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.CdkTrapFocusModule_CdkTrapFocus, selector: "[cdkTrapFocus]" }, { kind: "component", type: i5.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i7.ClrCheckbox, selector: "[clrCheckbox],[clrToggle]" }, { kind: "component", type: i7.ClrCheckboxWrapper, selector: "clr-checkbox-wrapper,clr-toggle-wrapper" }, { kind: "directive", type: i13.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.ClrPopoverOrigin, selector: "[clrPopoverOrigin]" }, { kind: "directive", type: i3.ClrPopoverCloseButton, selector: "[clrPopoverCloseButton]", outputs: ["clrPopoverOnCloseChange"] }, { kind: "directive", type: i3.ClrPopoverOpenCloseButton, selector: "[clrPopoverOpenCloseButton]", outputs: ["clrPopoverOpenCloseChange"] }, { kind: "directive", type: i3.ClrPopoverContent, selector: "[clrPopoverContent]", inputs: ["clrPopoverContent", "clrPopoverContentAt", "clrPopoverContentAvailablePositions", "clrPopoverContentType", "clrPopoverContentOutsideClickToClose", "clrPopoverContentScrollToClose", "clrPopoverContentOrigin"] }, { kind: "component", type: ClrDatagridColumnToggleButton, selector: "clr-dg-column-toggle-button", outputs: ["clrAllSelected"] }] }); }
6035
+ `, isInline: true, dependencies: [{ kind: "directive", type: i10.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.CdkTrapFocusModule_CdkTrapFocus, selector: "[cdkTrapFocus]" }, { kind: "component", type: i5.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i7.ClrCheckbox, selector: "[clrCheckbox],[clrToggle]" }, { kind: "component", type: i7.ClrCheckboxWrapper, selector: "clr-checkbox-wrapper,clr-toggle-wrapper" }, { kind: "directive", type: i14.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i14.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i14.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.ClrPopoverOrigin, selector: "[clrPopoverOrigin]" }, { kind: "directive", type: i3.ClrPopoverCloseButton, selector: "[clrPopoverCloseButton]", outputs: ["clrPopoverOnCloseChange"] }, { kind: "directive", type: i3.ClrPopoverOpenCloseButton, selector: "[clrPopoverOpenCloseButton]", outputs: ["clrPopoverOpenCloseChange"] }, { kind: "directive", type: i3.ClrPopoverContent, selector: "[clrPopoverContent]", inputs: ["clrPopoverContent", "clrPopoverContentAt", "clrPopoverContentAvailablePositions", "clrPopoverContentType", "clrPopoverContentOutsideClickToClose", "clrPopoverContentScrollToClose", "clrPopoverContentOrigin"] }, { kind: "component", type: ClrDatagridColumnToggleButton, selector: "clr-dg-column-toggle-button", outputs: ["clrAllSelected"] }] }); }
5808
6036
  }
5809
6037
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridColumnToggle, decorators: [{
5810
6038
  type: Component,
@@ -5957,6 +6185,32 @@ class ClrDatagridDetail {
5957
6185
  this.detailService = detailService;
5958
6186
  this.commonStrings = commonStrings;
5959
6187
  }
6188
+ get detailWidth() {
6189
+ return this.detailService.detailWidth;
6190
+ }
6191
+ set detailWidth(value) {
6192
+ if (value === null || value === undefined) {
6193
+ this.detailService.detailWidth = DEFAULT_DETAIL_WIDTH;
6194
+ return;
6195
+ }
6196
+ if (value < MIN_DETAIL_WIDTH) {
6197
+ this.detailService.detailWidth = MIN_DETAIL_WIDTH;
6198
+ return;
6199
+ }
6200
+ if (value > MAX_DETAIL_WIDTH) {
6201
+ this.detailService.detailWidth = MAX_DETAIL_WIDTH;
6202
+ return;
6203
+ }
6204
+ this.detailService.detailWidth = value;
6205
+ }
6206
+ get isCustomWidth() {
6207
+ return this.detailWidth !== DEFAULT_DETAIL_WIDTH;
6208
+ }
6209
+ get width() {
6210
+ // Skip the inline style at 100% so the .datagrid-detail-overlay CSS rule takes over
6211
+ // without needing !important to override an inline style.
6212
+ return this.detailWidth !== MAX_DETAIL_WIDTH ? this.detailWidth : null;
6213
+ }
5960
6214
  get labelledBy() {
5961
6215
  if (this.ariaLabelledBy) {
5962
6216
  return this.header ? `${this.header.titleId} ${this.ariaLabelledBy}` : this.ariaLabelledBy;
@@ -5981,7 +6235,7 @@ class ClrDatagridDetail {
5981
6235
  this.detailService.close();
5982
6236
  }
5983
6237
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridDetail, deps: [{ token: DetailService }, { token: i2.ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component }); }
5984
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagridDetail, isStandalone: false, selector: "clr-dg-detail", inputs: { ariaLabelledBy: ["clrDetailAriaLabelledBy", "ariaLabelledBy"], ariaLabel: ["clrDetailAriaLabel", "ariaLabel"] }, host: { properties: { "class.datagrid-detail-pane": "true" } }, queries: [{ propertyName: "header", first: true, predicate: ClrDatagridDetailHeader, descendants: true }], ngImport: i0, template: `
6238
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagridDetail, isStandalone: false, selector: "clr-dg-detail", inputs: { ariaLabelledBy: ["clrDetailAriaLabelledBy", "ariaLabelledBy"], ariaLabel: ["clrDetailAriaLabel", "ariaLabel"], detailWidth: ["clrDetailWidth", "detailWidth"] }, host: { properties: { "class.datagrid-detail-pane": "true", "class.datagrid-detail-custom-width": "isCustomWidth", "style.width.%": "width" } }, queries: [{ propertyName: "header", first: true, predicate: ClrDatagridDetailHeader, descendants: true }], ngImport: i0, template: `
5985
6239
  @if (detailService.isOpen) {
5986
6240
  <div
5987
6241
  cdkTrapFocus
@@ -6006,6 +6260,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
6006
6260
  selector: 'clr-dg-detail',
6007
6261
  host: {
6008
6262
  '[class.datagrid-detail-pane]': 'true',
6263
+ '[class.datagrid-detail-custom-width]': 'isCustomWidth',
6264
+ '[style.width.%]': 'width',
6009
6265
  },
6010
6266
  // We put the @if on the cdkTrapFocus so it doesn't always exist on the page
6011
6267
  // have to test for presence of header for aria-describedby because it was causing unit tests to crash
@@ -6038,6 +6294,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
6038
6294
  }], header: [{
6039
6295
  type: ContentChild,
6040
6296
  args: [ClrDatagridDetailHeader]
6297
+ }], detailWidth: [{
6298
+ type: Input,
6299
+ args: ['clrDetailWidth']
6041
6300
  }] } });
6042
6301
 
6043
6302
  /*
@@ -6070,23 +6329,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
6070
6329
  }]
6071
6330
  }] });
6072
6331
 
6073
- /*
6074
- * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
6075
- * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
6076
- * This software is released under MIT license.
6077
- * The full license information can be found in LICENSE in the root directory of this project.
6078
- */
6079
- const COLUMN_STATE = new InjectionToken('COLUMN_STATE');
6080
- function columnStateFactory() {
6081
- return new BehaviorSubject({
6082
- changes: [],
6083
- });
6084
- }
6085
- const COLUMN_STATE_PROVIDER = {
6086
- provide: COLUMN_STATE,
6087
- useFactory: columnStateFactory,
6088
- };
6089
-
6090
6332
  /*
6091
6333
  * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
6092
6334
  * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
@@ -6172,7 +6414,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
6172
6414
  selector: '[clrDgHideableColumn]',
6173
6415
  standalone: false,
6174
6416
  }]
6175
- }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: ColumnsService }, { type: i2$2.BehaviorSubject, decorators: [{
6417
+ }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: ColumnsService }, { type: i12.BehaviorSubject, decorators: [{
6176
6418
  type: Optional
6177
6419
  }, {
6178
6420
  type: Inject,
@@ -6293,16 +6535,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
6293
6535
  * For instance, it can help span a text over multiple rows in detail view.
6294
6536
  */
6295
6537
  class ClrDatagridRowDetail {
6296
- constructor(selection, rowActionService, expand, expandableRows, commonStrings) {
6538
+ constructor(selection, rowActionService, expand, expandableRows, commonStrings, columnsService, displayMode) {
6297
6539
  this.selection = selection;
6298
6540
  this.rowActionService = rowActionService;
6299
6541
  this.expand = expand;
6300
6542
  this.expandableRows = expandableRows;
6301
6543
  this.commonStrings = commonStrings;
6544
+ this.columnsService = columnsService;
6545
+ this.displayMode = displayMode;
6302
6546
  this.replacedRow = false;
6547
+ /**
6548
+ * Whether the detail lays its cells out in the same columns as the row, which it does when it
6549
+ * holds one cell per column and at least one of those columns is pinned. The cells of the pinned
6550
+ * columns are then static alongside the ones in the row above.
6551
+ */
6552
+ this.columnAligned = false;
6303
6553
  /* reference to the enum so that template can access it */
6304
6554
  this.SELECTION_TYPE = SelectionType;
6305
6555
  this.subscriptions = [];
6556
+ this.cellsMoved = false;
6306
6557
  }
6307
6558
  set replace(value) {
6308
6559
  this.expand.setReplace(!!value);
@@ -6318,19 +6569,93 @@ class ClrDatagridRowDetail {
6318
6569
  ngAfterContentInit() {
6319
6570
  this.subscriptions.push(this.expand.replace.subscribe(replaceChange => {
6320
6571
  this.replacedRow = replaceChange;
6321
- }));
6572
+ }),
6573
+ // New cells have to be moved even when the columns did not change.
6574
+ this.cells.changes.subscribe(() => this.syncColumnAlignment(true)));
6575
+ }
6576
+ ngAfterViewInit() {
6577
+ this.subscriptions.push(
6578
+ // The columns are set up after the detail's content, and pinning one happens later still.
6579
+ // Both land as a new render cycle, which is where the cells are moved.
6580
+ this.displayMode.view.subscribe(() => this.syncColumnAlignment()));
6581
+ this.syncColumnAlignment(true);
6582
+ this.hadCells = this.cells?.length > 0;
6583
+ }
6584
+ ngAfterViewChecked() {
6585
+ // The template holds the content in one of two places, depending on whether the detail has cells
6586
+ // of its own, and creating either of them projects the content into it - which takes the cells
6587
+ // back out of the containers they were moved into. That happens when a detail turns up with no
6588
+ // cells and receives them later, as it does when its content is behind a `clrLoading` template.
6589
+ // By this point the swap has happened, so the move can be repeated for good.
6590
+ const hasCells = this.cells?.length > 0;
6591
+ if (hasCells !== this.hadCells) {
6592
+ this.hadCells = hasCells;
6593
+ this.syncColumnAlignment(true);
6594
+ }
6322
6595
  }
6323
6596
  ngOnDestroy() {
6324
6597
  this.subscriptions.forEach(sub => sub.unsubscribe());
6325
6598
  }
6326
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridRowDetail, deps: [{ token: Selection }, { token: RowActionService }, { token: DatagridIfExpandService }, { token: ExpandableRowsCount }, { token: i2.ClrCommonStringsService }], target: i0.ɵɵFactoryTarget.Component }); }
6327
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagridRowDetail, isStandalone: false, selector: "clr-dg-row-detail", inputs: { _beginningOfExpandableContentAriaText: ["clrRowDetailBeginningAriaText", "_beginningOfExpandableContentAriaText"], _endOfExpandableContentAriaText: ["clrRowDetailEndAriaText", "_endOfExpandableContentAriaText"], replace: ["clrDgReplace", "replace"] }, host: { attributes: { "role": "row" }, properties: { "class.datagrid-row-flex": "true", "class.datagrid-row-detail": "true", "attr.id": "expand.expandableId" } }, queries: [{ propertyName: "cells", predicate: ClrDatagridCell }], ngImport: i0, template: `
6599
+ /**
6600
+ * Moves the cells into the containers that lay them out in the same columns as the row, or back
6601
+ * out of them once that stops making sense.
6602
+ */
6603
+ syncColumnAlignment(force = false) {
6604
+ if (!this._pinnedCells || !this._scrollableCells) {
6605
+ return;
6606
+ }
6607
+ const columns = this.columnsService.columnStates;
6608
+ const cells = this.cells ? this.cells.toArray() : [];
6609
+ // One cell per column is what makes the two comparable. Pinning is what makes the split worth
6610
+ // doing at all: with nothing pinned there is nothing to line up with, and the detail is left
6611
+ // exactly as it was authored.
6612
+ const columnAligned = cells.length > 0 && cells.length === columns.length && columns.some(column => column.pinned);
6613
+ const layout = columnAligned ? columns.map(column => (column.pinned ? '1' : '0')).join('') : '';
6614
+ if (!force && layout === this.lastLayout) {
6615
+ return;
6616
+ }
6617
+ this.lastLayout = layout;
6618
+ this.columnAligned = columnAligned;
6619
+ if (!columnAligned) {
6620
+ // Only worth doing if the cells were moved in the first place. Leaving an untouched detail
6621
+ // alone keeps whatever the author wrote between the cells where they wrote it.
6622
+ if (this.cellsMoved) {
6623
+ cells.forEach(cell => this.moveCell(this._plainCells, cell));
6624
+ this.cellsMoved = false;
6625
+ }
6626
+ return;
6627
+ }
6628
+ cells.forEach((cell, index) => this.moveCell(this.columnsService.isPinned(index) ? this._pinnedCells : this._scrollableCells, cell));
6629
+ this.cellsMoved = true;
6630
+ }
6631
+ moveCell(container, cell) {
6632
+ if (container && !cell._view.destroyed) {
6633
+ container.insert(cell._view);
6634
+ }
6635
+ }
6636
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridRowDetail, deps: [{ token: Selection }, { token: RowActionService }, { token: DatagridIfExpandService }, { token: ExpandableRowsCount }, { token: i2.ClrCommonStringsService }, { token: ColumnsService }, { token: DisplayModeService }], target: i0.ɵɵFactoryTarget.Component }); }
6637
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrDatagridRowDetail, isStandalone: false, selector: "clr-dg-row-detail", inputs: { _beginningOfExpandableContentAriaText: ["clrRowDetailBeginningAriaText", "_beginningOfExpandableContentAriaText"], _endOfExpandableContentAriaText: ["clrRowDetailEndAriaText", "_endOfExpandableContentAriaText"], replace: ["clrDgReplace", "replace"] }, host: { attributes: { "role": "row" }, properties: { "class.datagrid-row-flex": "true", "class.datagrid-row-detail": "true", "attr.id": "expand.expandableId" } }, queries: [{ propertyName: "cells", predicate: ClrDatagridCell }], viewQueries: [{ propertyName: "_pinnedCells", first: true, predicate: ["pinnedCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_scrollableCells", first: true, predicate: ["scrollableCells"], descendants: true, read: ViewContainerRef }, { propertyName: "_plainCells", first: true, predicate: ["plainCells"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: `
6328
6638
  <div class="clr-sr-only">
6329
6639
  {{ beginningOfExpandableContentAriaText }}
6330
6640
  {{ commonStrings.keys.datagridExpandableRowsHelperText }}
6331
6641
  </div>
6642
+ <!--
6643
+ These two containers mirror the ones in the row above and are only used while the detail is
6644
+ column aligned - see columnAligned. They are always rendered so the cells always have
6645
+ somewhere to be moved into, and the pinned one takes itself out of the layout while empty.
6646
+ -->
6647
+ <div class="datagrid-pinned-cells">
6648
+ <ng-container #pinnedCells></ng-container>
6649
+ </div>
6650
+ <ng-container #scrollableCells></ng-container>
6332
6651
  @if (this.cells?.length > 0) {
6333
6652
  <ng-container [ngTemplateOutlet]="noCells"></ng-container>
6653
+ <!--
6654
+ Where the cells go back to when the detail stops being column aligned, because a column was
6655
+ unpinned. A view that has been moved into a container cannot be handed back to the content
6656
+ projection it came from, so it needs a container of its own in the same place.
6657
+ -->
6658
+ <ng-container #plainCells></ng-container>
6334
6659
  }
6335
6660
  @if (this.cells?.length === 0) {
6336
6661
  <clr-dg-cell class="datagrid-container">
@@ -6342,7 +6667,7 @@ class ClrDatagridRowDetail {
6342
6667
  <ng-content></ng-content>
6343
6668
  </ng-template>
6344
6669
  <div class="clr-sr-only">{{ endOfExpandableContentAriaText }}</div>
6345
- `, isInline: true, dependencies: [{ kind: "directive", type: i9.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ClrDatagridCell, selector: "clr-dg-cell" }, { kind: "directive", type: DatagridCellRenderer, selector: "clr-dg-cell" }] }); }
6670
+ `, isInline: true, dependencies: [{ kind: "directive", type: i10.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ClrDatagridCell, selector: "clr-dg-cell" }, { kind: "directive", type: DatagridCellRenderer, selector: "clr-dg-cell" }] }); }
6346
6671
  }
6347
6672
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridRowDetail, decorators: [{
6348
6673
  type: Component,
@@ -6353,8 +6678,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
6353
6678
  {{ beginningOfExpandableContentAriaText }}
6354
6679
  {{ commonStrings.keys.datagridExpandableRowsHelperText }}
6355
6680
  </div>
6681
+ <!--
6682
+ These two containers mirror the ones in the row above and are only used while the detail is
6683
+ column aligned - see columnAligned. They are always rendered so the cells always have
6684
+ somewhere to be moved into, and the pinned one takes itself out of the layout while empty.
6685
+ -->
6686
+ <div class="datagrid-pinned-cells">
6687
+ <ng-container #pinnedCells></ng-container>
6688
+ </div>
6689
+ <ng-container #scrollableCells></ng-container>
6356
6690
  @if (this.cells?.length > 0) {
6357
6691
  <ng-container [ngTemplateOutlet]="noCells"></ng-container>
6692
+ <!--
6693
+ Where the cells go back to when the detail stops being column aligned, because a column was
6694
+ unpinned. A view that has been moved into a container cannot be handed back to the content
6695
+ projection it came from, so it needs a container of its own in the same place.
6696
+ -->
6697
+ <ng-container #plainCells></ng-container>
6358
6698
  }
6359
6699
  @if (this.cells?.length === 0) {
6360
6700
  <clr-dg-cell class="datagrid-container">
@@ -6375,7 +6715,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
6375
6715
  },
6376
6716
  standalone: false,
6377
6717
  }]
6378
- }], ctorParameters: () => [{ type: Selection }, { type: RowActionService }, { type: DatagridIfExpandService }, { type: ExpandableRowsCount }, { type: i2.ClrCommonStringsService }], propDecorators: { _beginningOfExpandableContentAriaText: [{
6718
+ }], ctorParameters: () => [{ type: Selection }, { type: RowActionService }, { type: DatagridIfExpandService }, { type: ExpandableRowsCount }, { type: i2.ClrCommonStringsService }, { type: ColumnsService }, { type: DisplayModeService }], propDecorators: { _beginningOfExpandableContentAriaText: [{
6379
6719
  type: Input,
6380
6720
  args: ['clrRowDetailBeginningAriaText']
6381
6721
  }], _endOfExpandableContentAriaText: [{
@@ -6384,6 +6724,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
6384
6724
  }], cells: [{
6385
6725
  type: ContentChildren,
6386
6726
  args: [ClrDatagridCell]
6727
+ }], _pinnedCells: [{
6728
+ type: ViewChild,
6729
+ args: ['pinnedCells', { read: ViewContainerRef }]
6730
+ }], _scrollableCells: [{
6731
+ type: ViewChild,
6732
+ args: ['scrollableCells', { read: ViewContainerRef }]
6733
+ }], _plainCells: [{
6734
+ type: ViewChild,
6735
+ args: ['plainCells', { read: ViewContainerRef }]
6387
6736
  }], replace: [{
6388
6737
  type: Input,
6389
6738
  args: ['clrDgReplace']
@@ -6491,7 +6840,7 @@ class ClrDatagridPageSize {
6491
6840
  }
6492
6841
  </select>
6493
6842
  </div>
6494
- `, isInline: true, dependencies: [{ kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i13.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i13.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i13.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i13.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i13.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
6843
+ `, isInline: true, dependencies: [{ kind: "directive", type: i4.ClrControlLabel, selector: "label", inputs: ["id", "for"] }, { kind: "directive", type: i14.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i14.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i14.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i14.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i14.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
6495
6844
  }
6496
6845
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridPageSize, decorators: [{
6497
6846
  type: Component,
@@ -7046,6 +7395,14 @@ class DatagridHeaderRenderer {
7046
7395
  this.renderer.removeClass(this.el.nativeElement, HIDDEN_COLUMN_CLASS);
7047
7396
  }
7048
7397
  }
7398
+ setPinned(state) {
7399
+ if (state.pinned) {
7400
+ this.renderer.addClass(this.el.nativeElement, PINNED_COLUMN_CLASS);
7401
+ }
7402
+ else {
7403
+ this.renderer.removeClass(this.el.nativeElement, PINNED_COLUMN_CLASS);
7404
+ }
7405
+ }
7049
7406
  clearWidth() {
7050
7407
  // remove the width only if we set it, and it is not changed by dragging.
7051
7408
  if (this.widthSet && !this.columnResizerService.resizedBy) {
@@ -7083,7 +7440,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
7083
7440
  providers: [ColumnResizerService, COLUMN_STATE_PROVIDER],
7084
7441
  standalone: false,
7085
7442
  }]
7086
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: DatagridRenderOrganizer }, { type: i2.DomAdapter }, { type: ColumnResizerService }, { type: ColumnsService }, { type: i2$2.BehaviorSubject, decorators: [{
7443
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: DatagridRenderOrganizer }, { type: i2.DomAdapter }, { type: ColumnResizerService }, { type: ColumnsService }, { type: i12.BehaviorSubject, decorators: [{
7087
7444
  type: Inject,
7088
7445
  args: [COLUMN_STATE]
7089
7446
  }] }], propDecorators: { resizeEmitter: [{
@@ -7237,13 +7594,27 @@ class DatagridMainRenderer {
7237
7594
  if (this.headers) {
7238
7595
  if (state && !this.columnsService.hasCache()) {
7239
7596
  this.columnsService.cache();
7240
- this.columnsService.visibleColumns.forEach((header, index) => {
7241
- if (index > 0) {
7597
+ // The pane leaves room for a single column, and it should be the one the user was already
7598
+ // looking at on the left of the row. Pinned columns are rendered before the rest, so that
7599
+ // is the first pinned column whenever anything is pinned, and the first declared one
7600
+ // otherwise.
7601
+ const keptColumn = this.columnsService.visibleColumns.find(column => column.pinned) || this.columnsService.visibleColumns[0];
7602
+ this.columnsService.visibleColumns.forEach(header => {
7603
+ if (header !== keptColumn) {
7242
7604
  this.columnsService.emitStateChangeAt(header.columnIndex, {
7243
7605
  changes: [DatagridColumnChanges.HIDDEN],
7244
7606
  hidden: state,
7245
7607
  });
7246
7608
  }
7609
+ // That single column is full width, so there is nothing to scroll and nothing to pin
7610
+ // against - the column that stays included. Pinning is suspended while the pane is open
7611
+ // and restored from the cache when it closes.
7612
+ if (header.pinned) {
7613
+ this.columnsService.emitStateChangeAt(header.columnIndex, {
7614
+ changes: [DatagridColumnChanges.PINNED],
7615
+ pinned: false,
7616
+ });
7617
+ }
7247
7618
  });
7248
7619
  }
7249
7620
  else if (!state) {
@@ -7336,15 +7707,47 @@ class DatagridMainRenderer {
7336
7707
  this.updateColumnSeparatorsVisibility();
7337
7708
  this.keyNavigation.resetKeyGrid();
7338
7709
  break;
7710
+ case DatagridColumnChanges.PINNED: {
7711
+ // Read before the class is updated below. The state can be re-emitted with the same
7712
+ // value, e.g. by resetToLastCache, and relocating the views then is pure overhead.
7713
+ const wasPinned = this.datagrid.columns.get(columnIndex)?.isPinned;
7714
+ this.headers.get(columnIndex).setPinned(state);
7715
+ this.rows.forEach(row => {
7716
+ if (row.cells && row.cells.length) {
7717
+ row.cells.get(columnIndex)?.setPinned(state);
7718
+ row.expandableRows.forEach(expandableRow => {
7719
+ expandableRow.cells.get(columnIndex)?.setPinned(state);
7720
+ });
7721
+ }
7722
+ });
7723
+ if (wasPinned !== !!state.pinned) {
7724
+ this.updateColumnSeparatorsVisibility();
7725
+ this.keyNavigation.resetKeyGrid();
7726
+ // The column and its cells have to move between the static and the scrollable
7727
+ // containers, which happens on the next CALCULATE/DISPLAY cycle. Widths are
7728
+ // recomputed there as well, since the flex context of the column changes.
7729
+ // Deferring to ngAfterViewChecked keeps us out of the change detection pass that
7730
+ // the `clrDgPinned` input setter runs in.
7731
+ this.columnsSizesStable = false;
7732
+ this.shouldStabilizeColumns = true;
7733
+ }
7734
+ break;
7735
+ }
7339
7736
  case DatagridColumnChanges.INITIALIZE:
7737
+ if (state.pinned) {
7738
+ this.headers.get(columnIndex).setPinned(state);
7739
+ }
7340
7740
  if (state.hideable && state.hidden) {
7341
7741
  this.headers.get(columnIndex).setHidden(state);
7742
+ }
7743
+ if (state.pinned || (state.hideable && state.hidden)) {
7342
7744
  this.rows.forEach(row => {
7343
7745
  row.setCellsState();
7344
7746
  row.expandableRows.forEach(expandableRow => {
7345
7747
  expandableRow.setCellsState();
7346
7748
  });
7347
7749
  });
7750
+ this.updateColumnSeparatorsVisibility();
7348
7751
  }
7349
7752
  break;
7350
7753
  default:
@@ -7369,12 +7772,15 @@ class DatagridMainRenderer {
7369
7772
  }
7370
7773
  updateColumnSeparatorsVisibility() {
7371
7774
  const visibleColumns = this.datagrid.columns.filter(column => !column.isHidden);
7372
- visibleColumns.forEach((column, index) => {
7373
- if (index === visibleColumns.length - 1) {
7374
- column.showSeparator = false;
7375
- }
7376
- else if (!column.showSeparator) {
7377
- column.showSeparator = true;
7775
+ // Pinned columns are rendered in the static container, so they always have something after
7776
+ // them and keep their separator - it is what marks the boundary with the scrollable columns.
7777
+ // Only the last scrollable column has nothing after it and must hide its separator.
7778
+ const scrollableColumns = visibleColumns.filter(column => !column.isPinned);
7779
+ const lastScrollableColumn = scrollableColumns[scrollableColumns.length - 1];
7780
+ visibleColumns.forEach(column => {
7781
+ const showSeparator = column !== lastScrollableColumn;
7782
+ if (column.showSeparator !== showSeparator) {
7783
+ column.showSeparator = showSeparator;
7378
7784
  }
7379
7785
  });
7380
7786
  }
@@ -7475,7 +7881,11 @@ const CLR_DATAGRID_STANDALONE_DIRECTIVES = [ClrDatagridSingleSelectionValueAcces
7475
7881
  const CLR_DATAGRID_SHARED_DIRECTIVES = [ClrIfExpanded];
7476
7882
  class ClrDatagridModule {
7477
7883
  constructor() {
7478
- ClarityIcons.addIcons(ellipsisVerticalIcon, viewColumnsIcon, windowCloseIcon, arrowIcon, timesIcon, twoWayArrowsIcon, stepForward2Icon, angleDoubleIcon, filterGridCircleIcon, filterGridIcon);
7884
+ ClarityIcons.addIcons(ellipsisVerticalIcon, viewColumnsIcon, windowCloseIcon, arrowIcon, timesIcon, twoWayArrowsIcon, stepForward2Icon, angleDoubleIcon, filterGridCircleIcon, filterGridIcon
7885
+ // Only the clrDgPinnable toggle used these, so they are disabled along with it.
7886
+ // pinIcon,
7887
+ // unpinIcon
7888
+ );
7479
7889
  }
7480
7890
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
7481
7891
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ClrDatagridModule, declarations: [