@alaarab/ogrid-angular 2.0.6 → 2.0.7

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.
@@ -63,6 +63,8 @@ export class BaseDataGridTableComponent {
63
63
  this.showEmptyInGrid = computed(() => this.state().viewModels.showEmptyInGrid);
64
64
  this.headerFilterInput = computed(() => this.state().viewModels.headerFilterInput);
65
65
  this.cellDescriptorInput = computed(() => this.state().viewModels.cellDescriptorInput);
66
+ // Pinning state
67
+ this.pinnedColumnsMap = computed(() => this.state().pinning.pinnedColumns);
66
68
  this.allowOverflowX = computed(() => {
67
69
  const p = this.getProps();
68
70
  if (p?.suppressHorizontalScroll)
@@ -287,16 +289,13 @@ export class BaseDataGridTableComponent {
287
289
  }
288
290
  // --- Column pinning methods ---
289
291
  onPinColumn(columnId, side) {
290
- const props = this.getProps();
291
- props?.onColumnPinned?.(columnId, side);
292
+ this.state().pinning.pinColumn(columnId, side);
292
293
  }
293
294
  onUnpinColumn(columnId) {
294
- const props = this.getProps();
295
- props?.onColumnPinned?.(columnId, null);
295
+ this.state().pinning.unpinColumn(columnId);
296
296
  }
297
297
  isPinned(columnId) {
298
- const props = this.getProps();
299
- return props?.pinnedColumns?.[columnId];
298
+ return this.state().pinning.isPinned(columnId);
300
299
  }
301
300
  getPinState(columnId) {
302
301
  const pinned = this.isPinned(columnId);
@@ -52,7 +52,7 @@ let MarchingAntsOverlayComponent = class MarchingAntsOverlayComponent {
52
52
  const selRange = this.selectionRange();
53
53
  const clipRange = this.copyRange() ?? this.cutRange();
54
54
  const colOff = this.colOffset();
55
- const _version = this.columnSizingVersion(); // Track column resize changes
55
+ void this.columnSizingVersion(); // Track column resize changes
56
56
  if (this.resizeObserver) {
57
57
  this.resizeObserver.disconnect();
58
58
  this.resizeObserver = null;
@@ -53,6 +53,10 @@ let DataGridStateService = class DataGridStateService {
53
53
  this.autoScrollInterval = null;
54
54
  // ResizeObserver
55
55
  this.resizeObserver = null;
56
+ // Header menu state (for column pinning UI)
57
+ this.headerMenuIsOpenSig = signal(false);
58
+ this.headerMenuOpenForColumnSig = signal(null);
59
+ this.headerMenuAnchorElementSig = signal(null);
56
60
  // --- Derived computed ---
57
61
  this.propsResolved = computed(() => this.props());
58
62
  this.cellSelection = computed(() => {
@@ -949,6 +953,59 @@ let DataGridStateService = class DataGridStateService {
949
953
  this.fillDragStart = { startRow: range.startRow, startCol: range.startCol };
950
954
  this.setupFillHandleDrag();
951
955
  }
956
+ // --- Column pinning ---
957
+ pinColumn(columnId, side) {
958
+ const props = this.props();
959
+ props?.onColumnPinned?.(columnId, side);
960
+ }
961
+ unpinColumn(columnId) {
962
+ const props = this.props();
963
+ props?.onColumnPinned?.(columnId, null);
964
+ }
965
+ isPinned(columnId) {
966
+ const props = this.props();
967
+ return props?.pinnedColumns?.[columnId];
968
+ }
969
+ getPinState(columnId) {
970
+ const pinned = this.isPinned(columnId);
971
+ return {
972
+ canPinLeft: pinned !== 'left',
973
+ canPinRight: pinned !== 'right',
974
+ canUnpin: !!pinned,
975
+ };
976
+ }
977
+ // --- Header menu ---
978
+ openHeaderMenu(columnId, anchorEl) {
979
+ this.headerMenuOpenForColumnSig.set(columnId);
980
+ this.headerMenuAnchorElementSig.set(anchorEl);
981
+ this.headerMenuIsOpenSig.set(true);
982
+ }
983
+ closeHeaderMenu() {
984
+ this.headerMenuIsOpenSig.set(false);
985
+ this.headerMenuOpenForColumnSig.set(null);
986
+ this.headerMenuAnchorElementSig.set(null);
987
+ }
988
+ headerMenuPinLeft() {
989
+ const col = this.headerMenuOpenForColumnSig();
990
+ if (col && this.isPinned(col) !== 'left') {
991
+ this.pinColumn(col, 'left');
992
+ this.closeHeaderMenu();
993
+ }
994
+ }
995
+ headerMenuPinRight() {
996
+ const col = this.headerMenuOpenForColumnSig();
997
+ if (col && this.isPinned(col) !== 'right') {
998
+ this.pinColumn(col, 'right');
999
+ this.closeHeaderMenu();
1000
+ }
1001
+ }
1002
+ headerMenuUnpin() {
1003
+ const col = this.headerMenuOpenForColumnSig();
1004
+ if (col && this.isPinned(col)) {
1005
+ this.unpinColumn(col);
1006
+ this.closeHeaderMenu();
1007
+ }
1008
+ }
952
1009
  // --- Get state result ---
953
1010
  getState() {
954
1011
  const p = this.props();
@@ -1048,7 +1105,29 @@ let DataGridStateService = class DataGridStateService {
1048
1105
  showEmptyInGrid: this.showEmptyInGrid(),
1049
1106
  onCellError: p?.onCellError,
1050
1107
  };
1051
- return { layout, rowSelection, editing, interaction, contextMenu, viewModels };
1108
+ // --- Pinning ---
1109
+ const openForColumn = this.headerMenuOpenForColumnSig();
1110
+ const currentPinState = openForColumn ? (p?.pinnedColumns?.[openForColumn]) : undefined;
1111
+ const pinning = {
1112
+ pinnedColumns: p?.pinnedColumns ?? {},
1113
+ pinColumn: (columnId, side) => this.pinColumn(columnId, side),
1114
+ unpinColumn: (columnId) => this.unpinColumn(columnId),
1115
+ isPinned: (columnId) => this.isPinned(columnId),
1116
+ headerMenu: {
1117
+ isOpen: this.headerMenuIsOpenSig(),
1118
+ openForColumn,
1119
+ anchorElement: this.headerMenuAnchorElementSig(),
1120
+ open: (columnId, anchorEl) => this.openHeaderMenu(columnId, anchorEl),
1121
+ close: () => this.closeHeaderMenu(),
1122
+ handlePinLeft: () => this.headerMenuPinLeft(),
1123
+ handlePinRight: () => this.headerMenuPinRight(),
1124
+ handleUnpin: () => this.headerMenuUnpin(),
1125
+ canPinLeft: currentPinState !== 'left',
1126
+ canPinRight: currentPinState !== 'right',
1127
+ canUnpin: !!currentPinState,
1128
+ },
1129
+ };
1130
+ return { layout, rowSelection, editing, interaction, contextMenu, viewModels, pinning };
1052
1131
  }
1053
1132
  // --- Private helpers ---
1054
1133
  getEffectiveRange() {
@@ -52,6 +52,7 @@ let OGridService = class OGridService {
52
52
  this.suppressHorizontalScroll = signal(undefined);
53
53
  this.editable = signal(undefined);
54
54
  this.cellSelection = signal(undefined);
55
+ this.density = signal('normal');
55
56
  this.onCellValueChanged = signal(undefined);
56
57
  this.onUndo = signal(undefined);
57
58
  this.onRedo = signal(undefined);
@@ -225,6 +226,7 @@ let OGridService = class OGridService {
225
226
  freezeCols: this.freezeCols(),
226
227
  editable: this.editable(),
227
228
  cellSelection: this.cellSelection(),
229
+ density: this.density(),
228
230
  onCellValueChanged: this.onCellValueChanged(),
229
231
  onUndo: this.onUndo(),
230
232
  onRedo: this.onRedo(),
@@ -428,7 +430,7 @@ let OGridService = class OGridService {
428
430
  handleColumnPinned(columnId, pinned) {
429
431
  this.pinnedOverrides.update((prev) => {
430
432
  if (pinned === null) {
431
- const { [columnId]: _, ...rest } = prev;
433
+ const { [columnId]: _removed, ...rest } = prev;
432
434
  return rest;
433
435
  }
434
436
  return { ...prev, [columnId]: pinned };
@@ -487,6 +489,8 @@ let OGridService = class OGridService {
487
489
  this.editable.set(props.editable);
488
490
  if (props.cellSelection !== undefined)
489
491
  this.cellSelection.set(props.cellSelection);
492
+ if (props.density !== undefined)
493
+ this.density.set(props.density);
490
494
  if (props.onCellValueChanged)
491
495
  this.onCellValueChanged.set(props.onCellValueChanged);
492
496
  if (props.onUndo)
@@ -13,7 +13,7 @@ import type { HeaderFilterConfig, CellRenderDescriptor } from '../utils';
13
13
  * 2. Call `initBase()` in the constructor (effects require injection context)
14
14
  * 3. Implement abstract accessors for propsInput, wrapperRef, and tableContainerRef
15
15
  */
16
- export declare abstract class BaseDataGridTableComponent<T = any> {
16
+ export declare abstract class BaseDataGridTableComponent<T = unknown> {
17
17
  readonly stateService: DataGridStateService<T>;
18
18
  readonly columnReorderService: ColumnReorderService<T>;
19
19
  readonly virtualScrollService: VirtualScrollService;
@@ -103,6 +103,7 @@ export declare abstract class BaseDataGridTableComponent<T = any> {
103
103
  onCellValueChanged?: ((event: import("@alaarab/ogrid-core").ICellValueChangedEvent<T>) => void) | undefined;
104
104
  isDragging: boolean;
105
105
  }>;
106
+ readonly pinnedColumnsMap: import("@angular/core").Signal<Record<string, "left" | "right">>;
106
107
  readonly allowOverflowX: import("@angular/core").Signal<boolean>;
107
108
  readonly selectionCellCount: import("@angular/core").Signal<number | undefined>;
108
109
  readonly headerRows: import("@angular/core").Signal<import("@alaarab/ogrid-core").HeaderRow<T>[]>;
@@ -6,7 +6,7 @@ export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types
6
6
  export { OGridService } from './services/ogrid.service';
7
7
  export type { ColumnChooserPlacement, OGridPagination, OGridColumnChooser, OGridFilters, OGridSideBarState, } from './services/ogrid.service';
8
8
  export { DataGridStateService } from './services/datagrid-state.service';
9
- export type { DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridStateResult, } from './services/datagrid-state.service';
9
+ export type { DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, DataGridStateResult, } from './services/datagrid-state.service';
10
10
  export { ColumnReorderService } from './services/column-reorder.service';
11
11
  export { VirtualScrollService } from './services/virtual-scroll.service';
12
12
  export type { IVirtualScrollConfig } from './services/virtual-scroll.service';
@@ -115,6 +115,26 @@ export interface DataGridViewModelState<T> {
115
115
  showEmptyInGrid: boolean;
116
116
  onCellError?: (error: Error) => void;
117
117
  }
118
+ /** Column pinning state and column header menu. */
119
+ export interface DataGridPinningState {
120
+ pinnedColumns: Record<string, 'left' | 'right'>;
121
+ pinColumn: (columnId: string, side: 'left' | 'right') => void;
122
+ unpinColumn: (columnId: string) => void;
123
+ isPinned: (columnId: string) => 'left' | 'right' | undefined;
124
+ headerMenu: {
125
+ isOpen: boolean;
126
+ openForColumn: string | null;
127
+ anchorElement: HTMLElement | null;
128
+ open: (columnId: string, anchorEl: HTMLElement) => void;
129
+ close: () => void;
130
+ handlePinLeft: () => void;
131
+ handlePinRight: () => void;
132
+ handleUnpin: () => void;
133
+ canPinLeft: boolean;
134
+ canPinRight: boolean;
135
+ canUnpin: boolean;
136
+ };
137
+ }
118
138
  export interface DataGridStateResult<T> {
119
139
  layout: DataGridLayoutState<T>;
120
140
  rowSelection: DataGridRowSelectionState;
@@ -122,6 +142,7 @@ export interface DataGridStateResult<T> {
122
142
  interaction: DataGridCellInteractionState;
123
143
  contextMenu: DataGridContextMenuState;
124
144
  viewModels: DataGridViewModelState<T>;
145
+ pinning: DataGridPinningState;
125
146
  }
126
147
  /**
127
148
  * Single orchestration service for DataGridTable. Takes grid props,
@@ -161,6 +182,9 @@ export declare class DataGridStateService<T> {
161
182
  private lastMousePos;
162
183
  private autoScrollInterval;
163
184
  private resizeObserver;
185
+ private readonly headerMenuIsOpenSig;
186
+ private readonly headerMenuOpenForColumnSig;
187
+ private readonly headerMenuAnchorElementSig;
164
188
  private readonly propsResolved;
165
189
  readonly cellSelection: import("@angular/core").Signal<boolean>;
166
190
  private readonly wrappedOnCellValueChanged;
@@ -227,6 +251,19 @@ export declare class DataGridStateService<T> {
227
251
  redo(): void;
228
252
  handleGridKeyDown(e: KeyboardEvent): void;
229
253
  handleFillHandleMouseDown(e: MouseEvent): void;
254
+ pinColumn(columnId: string, side: 'left' | 'right'): void;
255
+ unpinColumn(columnId: string): void;
256
+ isPinned(columnId: string): 'left' | 'right' | undefined;
257
+ getPinState(columnId: string): {
258
+ canPinLeft: boolean;
259
+ canPinRight: boolean;
260
+ canUnpin: boolean;
261
+ };
262
+ openHeaderMenu(columnId: string, anchorEl: HTMLElement): void;
263
+ closeHeaderMenu(): void;
264
+ headerMenuPinLeft(): void;
265
+ headerMenuPinRight(): void;
266
+ headerMenuUnpin(): void;
230
267
  getState(): DataGridStateResult<T>;
231
268
  private getEffectiveRange;
232
269
  private onWindowMouseMove;
@@ -86,6 +86,7 @@ export declare class OGridService<T> {
86
86
  readonly suppressHorizontalScroll: import("@angular/core").WritableSignal<boolean | undefined>;
87
87
  readonly editable: import("@angular/core").WritableSignal<boolean | undefined>;
88
88
  readonly cellSelection: import("@angular/core").WritableSignal<boolean | undefined>;
89
+ readonly density: import("@angular/core").WritableSignal<"compact" | "normal" | "comfortable">;
89
90
  readonly onCellValueChanged: import("@angular/core").WritableSignal<((event: ICellValueChangedEvent<T>) => void) | undefined>;
90
91
  readonly onUndo: import("@angular/core").WritableSignal<(() => void) | undefined>;
91
92
  readonly onRedo: import("@angular/core").WritableSignal<(() => void) | undefined>;
@@ -32,6 +32,7 @@ interface IOGridBaseProps<T> {
32
32
  freezeCols?: number;
33
33
  editable?: boolean;
34
34
  cellSelection?: boolean;
35
+ density?: 'compact' | 'normal' | 'comfortable';
35
36
  onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
36
37
  onUndo?: () => void;
37
38
  onRedo?: () => void;
@@ -99,6 +100,7 @@ export interface IOGridDataGridProps<T> {
99
100
  loadingMessage?: string;
100
101
  editable?: boolean;
101
102
  cellSelection?: boolean;
103
+ density?: 'compact' | 'normal' | 'comfortable';
102
104
  onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
103
105
  onUndo?: () => void;
104
106
  onRedo?: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-angular",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "description": "OGrid Angular – Angular services, signals, and headless components for OGrid data grids.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -22,7 +22,7 @@
22
22
  "files": ["dist", "README.md", "LICENSE"],
23
23
  "engines": { "node": ">=18" },
24
24
  "dependencies": {
25
- "@alaarab/ogrid-core": "2.0.6"
25
+ "@alaarab/ogrid-core": "2.0.7"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@angular/core": "^21.0.0",