Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6af5d06
feat(column-pinning): Pinning both sides, initial api and collections.
Aug 5, 2025
ddf757a
chore(*): Name collections pinnedStartColumns/pinnedEndColumns.
Aug 11, 2025
d300ee5
chore(*): Initial template updates.
Aug 11, 2025
62804fb
chore(*): Update header templates.
Aug 11, 2025
b0de0c0
chore(*): Adjust getters for styles.
Aug 11, 2025
e84e0d0
chore(*): Remove and update all old checks for root grid pin direction.
Aug 11, 2025
7d30464
chore(*): Fix summary cell context.
Aug 11, 2025
04b444d
chore(*): Fix offset calcs.
Aug 11, 2025
cf316eb
chore(*): Fix overwrite position. Update generic pinned collection.
Aug 11, 2025
a93fc98
chore(*): Fix moving pinned cols via exc.style moving.
Aug 11, 2025
71dec02
chore(*): Update position when moving from one pinned area to another.
Aug 12, 2025
45b7baa
chore(*): Fix mrl scenarios with both pin.
Aug 12, 2025
4100594
chore(*): Fix circular dep.
Aug 12, 2025
e8e56d4
chore(*): Add some tests for pin on both sides.
Aug 12, 2025
3de7752
chore(*): Fix mrl col indexes.
Aug 13, 2025
5f572b8
chore(*): Fix pivot scrollbar placeholder size.
Aug 13, 2025
5db917c
chore(*): Update internal pinned with hidden columns.
Aug 13, 2025
cc8fdb0
chore(*): Split pinned widths calc in pivot.
Aug 13, 2025
70a5d23
Merge branch 'master' into mkirova/col-pin-directions
MayaKirova Aug 13, 2025
f56872d
chore(*): Add mch and mrl tests with pin on both sides.
Aug 13, 2025
6786884
Merge branch 'mkirova/col-pin-directions' of https://github.com/Ignit…
Aug 13, 2025
d74faa1
chore(*): Clean debuggers.
Aug 14, 2025
84afe14
chore(*): Update hgrid row template.
Aug 14, 2025
51faf9c
chore(*): Update tree row template.
Aug 14, 2025
417cc8c
chore(*): Update changelog.
Aug 14, 2025
160895a
chore(*): Minor api docs changes.
Aug 14, 2025
cf09a34
Update projects/igniteui-angular/src/lib/grids/columns/column-layout.…
MayaKirova Aug 20, 2025
b98f3bc
Update projects/igniteui-angular/src/lib/grids/columns/column-layout.…
MayaKirova Aug 20, 2025
8681bdf
Apply suggestions from code review
MayaKirova Aug 20, 2025
baf294e
chore(*): Remove duplicate method.
Aug 20, 2025
76e46c6
Merge branch 'master' into mkirova/col-pin-directions
Lipata Aug 21, 2025
bebc71a
Merge branch 'master' into mkirova/col-pin-directions
dkamburov Aug 21, 2025
3a97bb2
chore(*): Elements config update.
Aug 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ All notable changes for each version of this project will be documented in this
```ts
this.carousel.select(2, Direction.NEXT);
```
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
- Added ability to pin individual columns to a specific side (start or end of the grid), so that you can now have pinning from both sides. This can be done either declaratively by setting the `pinningPosition` property on the column:

```html
<igx-column [field]="'Col1'" [pinned]='true' [pinningPosition]='pinningPosition'>
</igx-column>
```

```ts
public pinningPosition = ColumnPinningPosition.End;
```

Or with the API, via optional parameter:

```ts
grid.pinColumn('Col1', 0, ColumnPinningPosition.End);
grid.pinColumn('Col2', 0, ColumnPinningPosition.Start);
```

If property `pinningPosition` is not set on a column, the column will default to the position specified on the grid's `pinning` options for `columns`.

- `IgxDateRangePicker`
- Added new properties:
Expand Down
23 changes: 19 additions & 4 deletions projects/igniteui-angular-elements/src/analyzer/elements.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ export var registerConfig = [
{ name: "defaultHeaderGroupMinWidth" },
{ name: "columns" },
{ name: "pinnedColumns" },
{ name: "pinnedStartColumns" },
{ name: "pinnedEndColumns" },
{ name: "pinnedRows" },
{ name: "unpinnedColumns" },
{ name: "visibleColumns" },
Expand Down Expand Up @@ -327,7 +329,8 @@ export var registerConfig = [
"findPrev",
"refreshSearch",
"clearSearch",
"getPinnedWidth",
"getPinnedStartWidth",
"getPinnedEndWidth",
"selectRows",
"deselectRows",
"selectAllRows",
Expand Down Expand Up @@ -568,6 +571,8 @@ export var registerConfig = [
{ name: "defaultHeaderGroupMinWidth" },
{ name: "columns" },
{ name: "pinnedColumns" },
{ name: "pinnedStartColumns" },
{ name: "pinnedEndColumns" },
{ name: "pinnedRows" },
{ name: "unpinnedColumns" },
{ name: "visibleColumns" },
Expand Down Expand Up @@ -613,7 +618,8 @@ export var registerConfig = [
"findPrev",
"refreshSearch",
"clearSearch",
"getPinnedWidth",
"getPinnedStartWidth",
"getPinnedEndWidth",
"selectRows",
"deselectRows",
"selectAllRows",
Expand Down Expand Up @@ -748,6 +754,8 @@ export var registerConfig = [
{ name: "defaultRowHeight" },
{ name: "defaultHeaderGroupMinWidth" },
{ name: "columns" },
{ name: "pinnedStartColumns" },
{ name: "pinnedEndColumns" },
{ name: "visibleColumns" },
{ name: "dataView" },
],
Expand Down Expand Up @@ -780,6 +788,7 @@ export var registerConfig = [
"clearFilter",
"clearSort",
"reflow",
"getPinnedEndWidth",
"selectRows",
"deselectRows",
"selectAllRows",
Expand Down Expand Up @@ -872,6 +881,8 @@ export var registerConfig = [
{ name: "defaultRowHeight" },
{ name: "defaultHeaderGroupMinWidth" },
{ name: "columns" },
{ name: "pinnedStartColumns" },
{ name: "pinnedEndColumns" },
{ name: "pinnedRows" },
],
methods: [
Expand Down Expand Up @@ -908,7 +919,8 @@ export var registerConfig = [
"findPrev",
"refreshSearch",
"clearSearch",
"getPinnedWidth",
"getPinnedStartWidth",
"getPinnedEndWidth",
"selectRows",
"deselectRows",
"selectAllRows",
Expand Down Expand Up @@ -1022,6 +1034,8 @@ export var registerConfig = [
{ name: "defaultHeaderGroupMinWidth" },
{ name: "columns" },
{ name: "pinnedColumns" },
{ name: "pinnedStartColumns" },
{ name: "pinnedEndColumns" },
{ name: "pinnedRows" },
{ name: "unpinnedColumns" },
{ name: "visibleColumns" },
Expand Down Expand Up @@ -1069,7 +1083,8 @@ export var registerConfig = [
"findPrev",
"refreshSearch",
"clearSearch",
"getPinnedWidth",
"getPinnedStartWidth",
"getPinnedEndWidth",
"selectRows",
"deselectRows",
"selectAllRows",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,10 @@ export class IgxColumnLayoutComponent extends IgxColumnGroupComponent implements
}

const unpinnedColumns = this.grid.unpinnedColumns.filter(c => c.columnLayout && !c.hidden);
const pinnedColumns = this.grid.pinnedColumns.filter(c => c.columnLayout && !c.hidden);
let vIndex = -1;

if (!this.pinned) {
const indexInCollection = unpinnedColumns.indexOf(this);
vIndex = indexInCollection === -1 ? -1 : pinnedColumns.length + indexInCollection;
} else {
vIndex = pinnedColumns.indexOf(this);
}
const pinnedStart = this.grid.pinnedStartColumns.filter(c => c.columnLayout && !c.hidden);
const pinnedEndColumns = this.grid.pinnedEndColumns.filter(c => c.columnLayout && !c.hidden);
const ordered = pinnedStart.concat(unpinnedColumns, pinnedEndColumns);
let vIndex = ordered.indexOf(this);
this._vIndex = vIndex;
return vIndex;
}
Expand Down Expand Up @@ -158,7 +153,7 @@ export class IgxColumnLayoutComponent extends IgxColumnGroupComponent implements
public override populateVisibleIndexes() {
this.childrenVisibleIndexes = [];
const columns = this.grid?.pinnedColumns && this.grid?.unpinnedColumns
? this.grid.pinnedColumns.concat(this.grid.unpinnedColumns)
? this.grid.pinnedStartColumns.concat(this.grid.unpinnedColumns, this.grid.pinnedEndColumns)
: [];
const orderedCols = columns
.filter(x => !x.columnGroup && !x.hidden)
Expand Down
104 changes: 73 additions & 31 deletions projects/igniteui-angular/src/lib/grids/columns/column.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import { IColumnVisibilityChangingEventArgs, IPinColumnCancellableEventArgs, IPi
import { isConstructor, PlatformUtil } from '../../core/utils';
import { IgxGridCell } from '../grid-public-cell';
import { NG_VALIDATORS, Validator } from '@angular/forms';
import { Size } from '../common/enums';
import { ColumnPinningPosition, Size } from '../common/enums';
import { ExpressionsTreeUtil } from '../../data-operations/expressions-tree-util';

const DEFAULT_DATE_FORMAT = 'mediumDate';
Expand Down Expand Up @@ -1041,6 +1041,28 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
return (this.grid as any)._columns.indexOf(this);
}

/**
* Gets the pinning position of the column.
* ```typescript
* let pinningPosition = this.column.pinningPosition;
*/
@WatchColumnChanges()
@Input()
public get pinningPosition(): ColumnPinningPosition {
const userSet = this._pinningPosition !== null && this._pinningPosition !== undefined;
return userSet ? this._pinningPosition : this.grid.pinning.columns;
}

/**
* Sets the pinning position of the column.
*```html
* <igx-column [pinningPosition]="1"></igx-column>
* ```
*/
public set pinningPosition(value: ColumnPinningPosition) {
this._pinningPosition = value;
}

/**
* Gets whether the column is `pinned`.
* ```typescript
Expand Down Expand Up @@ -1487,7 +1509,8 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
return this._vIndex;
}
const unpinnedColumns = this.grid.unpinnedColumns.filter(c => !c.columnGroup);
const pinnedColumns = this.grid.pinnedColumns.filter(c => !c.columnGroup);
const pinnedStartColumns = this.grid.pinnedStartColumns.filter(c => !c.columnGroup);
const pinnedEndColumns = this.grid.pinnedEndColumns.filter(c => !c.columnGroup);

let col = this;
let vIndex = -1;
Expand All @@ -1502,15 +1525,13 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
if (!this.pinned) {
const indexInCollection = unpinnedColumns.indexOf(col);
vIndex = indexInCollection === -1 ?
-1 :
(this.grid.isPinningToStart ?
pinnedColumns.length + indexInCollection :
indexInCollection);
-1 : pinnedStartColumns.length + indexInCollection;
} else {
const indexInCollection = pinnedColumns.indexOf(col);
vIndex = this.grid.isPinningToStart ?
const indexInCollection = this.pinningPosition === ColumnPinningPosition.Start ?
pinnedStartColumns.indexOf(col) : pinnedEndColumns.indexOf(col);
vIndex = this.pinningPosition === ColumnPinningPosition.Start ?
indexInCollection :
unpinnedColumns.length + indexInCollection;
pinnedStartColumns.length + unpinnedColumns.length + indexInCollection;
}
this._vIndex = vIndex;
return vIndex;
Expand Down Expand Up @@ -1588,20 +1609,20 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy

/** @hidden @internal **/
public get isLastPinned(): boolean {
return this.grid.isPinningToStart &&
this.grid.pinnedColumns[this.grid.pinnedColumns.length - 1] === this;
return this.pinningPosition === ColumnPinningPosition.Start &&
this.grid.pinnedStartColumns[this.grid.pinnedStartColumns.length - 1] === this;
}

/** @hidden @internal **/
public get isFirstPinned(): boolean {
const pinnedCols = this.grid.pinnedColumns.filter(x => !x.columnGroup);
return !this.grid.isPinningToStart && pinnedCols[0] === this;
const pinnedCols = this.grid.pinnedEndColumns.filter(x => !x.columnGroup);
return this.pinningPosition === ColumnPinningPosition.End && pinnedCols[0] === this;
}

/** @hidden @internal **/
public get rightPinnedOffset(): string {
return this.pinned && !this.grid.isPinningToStart ?
- this.grid.pinnedWidth - this.grid.headerFeaturesWidth + 'px' :
return this.pinned && this.pinningPosition === ColumnPinningPosition.End ?
- this.grid.pinnedEndWidth - this.grid.pinnedStartWidth + 'px' :
null;
}

Expand Down Expand Up @@ -1794,6 +1815,7 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
protected _applySelectableClass = false;

protected _vIndex = NaN;
protected _pinningPosition = null;
/**
* @hidden
*/
Expand Down Expand Up @@ -2187,20 +2209,19 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
}

/**
* Pins the column at the provided index in the pinned area.
* Pins the column in the specified position at the provided index in that pinned area.
* Defaults to index `0` if not provided, or to the initial index in the pinned area.
* Returns `true` if the column is successfully pinned. Returns `false` if the column cannot be pinned.
* Column cannot be pinned if:
* - Is already pinned
* - index argument is out of range
* - The pinned area exceeds 80% of the grid width
* ```typescript
* let success = this.column.pin();
* ```
*
* @memberof IgxColumnComponent
*/
public pin(index?: number): boolean {
public pin(index?: number, pinningPosition?: ColumnPinningPosition): boolean {
// TODO: Probably should the return type of the old functions
// should be moved as a event parameter.
const grid = (this.grid as any);
Expand All @@ -2209,19 +2230,23 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
}

if (this.parent && !this.parent.pinned) {
return this.topLevelParent.pin(index);
}

const hasIndex = index !== undefined;
if (hasIndex && (index < 0 || index > grid.pinnedColumns.length)) {
return this.topLevelParent.pin(index, pinningPosition);
}
const targetPinPosition = pinningPosition !== null && pinningPosition !== undefined ? pinningPosition : this.pinningPosition;
const pinningVisibleCollection = targetPinPosition === ColumnPinningPosition.Start ?
grid.pinnedStartColumns : grid.pinnedEndColumns;
const pinningCollection = targetPinPosition === ColumnPinningPosition.Start ?
grid._pinnedStartColumns : grid._pinnedEndColumns;
const hasIndex = index !== undefined && index !== null;
if (hasIndex && (index < 0 || index > pinningVisibleCollection.length)) {
return false;
}

if (!this.parent && !this.pinnable) {
return false;
}

const rootPinnedCols = grid._pinnedColumns.filter((c) => c.level === 0);
const rootPinnedCols = pinningCollection.filter((c) => c.level === 0);
index = hasIndex ? index : rootPinnedCols.length;
const args: IPinColumnCancellableEventArgs = { column: this, insertAtIndex: index, isPinned: false, cancel: false };
this.grid.columnPin.emit(args);
Expand All @@ -2233,14 +2258,20 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
this.grid.crudService.endEdit(false);

this._pinned = true;
if (pinningPosition !== null && pinningPosition !== undefined) {
// if user has set some position in the params, overwrite the column's position.
this._pinningPosition = pinningPosition;
}

this.pinnedChange.emit(this._pinned);
// it is possible that index is the last position, so will need to find target column by [index-1]
const targetColumn = args.insertAtIndex === grid._pinnedColumns.length ?
grid._pinnedColumns[args.insertAtIndex - 1] : grid._pinnedColumns[args.insertAtIndex];
const targetColumn = args.insertAtIndex === pinningCollection.length ?
pinningCollection[args.insertAtIndex - 1] : pinningCollection[args.insertAtIndex];

if (grid._pinnedColumns.indexOf(this) === -1) {
if (pinningCollection.indexOf(this) === -1) {
if (!grid.hasColumnGroups) {
grid._pinnedColumns.splice(args.insertAtIndex, 0, this);
pinningCollection.splice(args.insertAtIndex, 0, this);
grid._pinnedColumns = grid._pinnedStartColumns.concat(grid._pinnedEndColumns);
} else {
// insert based only on root collection
if (this.level === 0) {
Expand All @@ -2254,6 +2285,11 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
allPinned = allPinned.concat(group.allChildren);
});
grid._pinnedColumns = allPinned;
if (this.pinningPosition === ColumnPinningPosition.Start) {
grid._pinnedStartColumns = allPinned;
} else {
grid._pinnedEndColumns = allPinned;
}
}

if (grid._unpinnedColumns.indexOf(this) !== -1) {
Expand All @@ -2263,12 +2299,12 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
}

if (hasIndex) {
index === grid._pinnedColumns.length - 1 ?
index === pinningCollection.length - 1 ?
grid._moveColumns(this, targetColumn, DropPosition.AfterDropTarget) : grid._moveColumns(this, targetColumn, DropPosition.BeforeDropTarget);
}

if (this.columnGroup) {
this.allChildren.forEach(child => child.pin());
this.allChildren.forEach(child => child.pin(null, targetPinPosition));
grid.reinitPinStates();
}

Expand Down Expand Up @@ -2304,7 +2340,7 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
if (this.parent && this.parent.pinned) {
return this.topLevelParent.unpin(index);
}
const hasIndex = index !== undefined;
const hasIndex = index !== undefined && index !== null;
if (hasIndex && (index < 0 || index > grid._unpinnedColumns.length)) {
return false;
}
Expand Down Expand Up @@ -2339,6 +2375,12 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
if (grid._pinnedColumns.indexOf(this) !== -1) {
grid._pinnedColumns.splice(grid._pinnedColumns.indexOf(this), 1);
}
if (this.pinningPosition === ColumnPinningPosition.Start && grid._pinnedStartColumns.indexOf(this) !== -1) {
grid._pinnedStartColumns.splice(grid._pinnedStartColumns.indexOf(this), 1);
}
if (this.pinningPosition === ColumnPinningPosition.End && grid._pinnedEndColumns.indexOf(this) !== -1) {
grid._pinnedEndColumns.splice(grid._pinnedEndColumns.indexOf(this), 1);
}
}

if (hasIndex) {
Expand Down
Loading
Loading