@alaarab/ogrid-angular 2.0.6 → 2.0.8

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.
@@ -3,7 +3,7 @@ import { DataGridStateService } from '../services/datagrid-state.service';
3
3
  import { ColumnReorderService } from '../services/column-reorder.service';
4
4
  import { VirtualScrollService } from '../services/virtual-scroll.service';
5
5
  import { buildHeaderRows, DEFAULT_MIN_COLUMN_WIDTH, } from '@alaarab/ogrid-core';
6
- import { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, } from '../utils';
6
+ import { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildPopoverEditorProps, } from '../utils';
7
7
  /**
8
8
  * Abstract base class containing all shared TypeScript logic for DataGridTable components.
9
9
  * Framework-specific UI packages extend this with their templates and style overrides.
@@ -23,6 +23,7 @@ export class BaseDataGridTableComponent {
23
23
  // --- Delegated state ---
24
24
  this.state = computed(() => this.stateService.getState());
25
25
  this.tableContainerEl = computed(() => this.getTableContainerRef()?.nativeElement ?? null);
26
+ this.allItems = computed(() => this.getProps()?.items ?? []);
26
27
  this.items = computed(() => this.getProps()?.items ?? []);
27
28
  this.getRowId = computed(() => this.getProps()?.getRowId ?? ((item) => item['id']));
28
29
  this.isLoading = computed(() => this.getProps()?.isLoading ?? false);
@@ -63,6 +64,36 @@ export class BaseDataGridTableComponent {
63
64
  this.showEmptyInGrid = computed(() => this.state().viewModels.showEmptyInGrid);
64
65
  this.headerFilterInput = computed(() => this.state().viewModels.headerFilterInput);
65
66
  this.cellDescriptorInput = computed(() => this.state().viewModels.cellDescriptorInput);
67
+ // Pinning state
68
+ this.pinnedColumnsMap = computed(() => this.state().pinning.pinnedColumns);
69
+ // Virtual scrolling
70
+ this.vsEnabled = computed(() => this.virtualScrollService.isActive());
71
+ this.vsVisibleRange = computed(() => this.virtualScrollService.visibleRange());
72
+ this.vsTopSpacerHeight = computed(() => {
73
+ if (!this.vsEnabled())
74
+ return 0;
75
+ return this.vsVisibleRange().offsetTop;
76
+ });
77
+ this.vsBottomSpacerHeight = computed(() => {
78
+ if (!this.vsEnabled())
79
+ return 0;
80
+ return this.vsVisibleRange().offsetBottom;
81
+ });
82
+ this.vsVisibleItems = computed(() => {
83
+ const items = this.allItems();
84
+ if (!this.vsEnabled())
85
+ return items;
86
+ const range = this.vsVisibleRange();
87
+ return items.slice(range.startIndex, Math.min(range.endIndex + 1, items.length));
88
+ });
89
+ this.vsStartIndex = computed(() => {
90
+ if (!this.vsEnabled())
91
+ return 0;
92
+ return this.vsVisibleRange().startIndex;
93
+ });
94
+ // Popover editing
95
+ this.popoverAnchorEl = computed(() => this.state().editing.popoverAnchorEl);
96
+ this.pendingEditorValueForPopover = computed(() => this.state().editing.pendingEditorValue);
66
97
  this.allowOverflowX = computed(() => {
67
98
  const p = this.getProps();
68
99
  if (p?.suppressHorizontalScroll)
@@ -142,6 +173,21 @@ export class BaseDataGridTableComponent {
142
173
  const p = this.getProps();
143
174
  if (p) {
144
175
  this.virtualScrollService.totalRows.set(p.items.length);
176
+ if (p.virtualScroll) {
177
+ this.virtualScrollService.updateConfig({
178
+ enabled: p.virtualScroll.enabled,
179
+ rowHeight: p.virtualScroll.rowHeight,
180
+ overscan: p.virtualScroll.overscan,
181
+ });
182
+ }
183
+ }
184
+ });
185
+ // Wire wrapper element to virtual scroll for scroll events + container height
186
+ effect(() => {
187
+ const el = this.getWrapperRef()?.nativeElement;
188
+ if (el) {
189
+ this.virtualScrollService.setContainer(el);
190
+ this.virtualScrollService.containerHeight.set(el.clientHeight);
145
191
  }
146
192
  });
147
193
  }
@@ -171,6 +217,13 @@ export class BaseDataGridTableComponent {
171
217
  resolveCellStyleFn(col, item) {
172
218
  return resolveCellStyle(col, item);
173
219
  }
220
+ buildPopoverEditorProps(item, col, descriptor) {
221
+ return buildPopoverEditorProps(item, col, descriptor, this.pendingEditorValue(), {
222
+ setPendingEditorValue: (value) => this.setPendingEditorValue(value),
223
+ commitCellEdit: (item, columnId, oldValue, newValue, rowIndex, globalColIndex) => this.commitEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex),
224
+ cancelPopoverEdit: () => this.cancelPopoverEdit(),
225
+ });
226
+ }
174
227
  getSelectValues(col) {
175
228
  const params = col.cellEditorParams;
176
229
  if (params && typeof params === 'object' && 'values' in params) {
@@ -186,6 +239,23 @@ export class BaseDataGridTableComponent {
186
239
  return '';
187
240
  return d.toISOString().split('T')[0];
188
241
  }
242
+ // --- Virtual scroll event handler ---
243
+ onWrapperScroll(event) {
244
+ this.virtualScrollService.onScroll(event);
245
+ }
246
+ // --- Popover editor helpers ---
247
+ setPopoverAnchorEl(el) {
248
+ this.state().editing.setPopoverAnchorEl(el);
249
+ }
250
+ setPendingEditorValue(value) {
251
+ this.state().editing.setPendingEditorValue(value);
252
+ }
253
+ cancelPopoverEdit() {
254
+ this.state().editing.cancelPopoverEdit();
255
+ }
256
+ commitPopoverEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex) {
257
+ this.state().editing.commitCellEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex);
258
+ }
189
259
  // --- Event handlers ---
190
260
  onWrapperMouseDown(event) {
191
261
  this.lastMouseShift = event.shiftKey;
@@ -287,16 +357,13 @@ export class BaseDataGridTableComponent {
287
357
  }
288
358
  // --- Column pinning methods ---
289
359
  onPinColumn(columnId, side) {
290
- const props = this.getProps();
291
- props?.onColumnPinned?.(columnId, side);
360
+ this.state().pinning.pinColumn(columnId, side);
292
361
  }
293
362
  onUnpinColumn(columnId) {
294
- const props = this.getProps();
295
- props?.onColumnPinned?.(columnId, null);
363
+ this.state().pinning.unpinColumn(columnId);
296
364
  }
297
365
  isPinned(columnId) {
298
- const props = this.getProps();
299
- return props?.pinnedColumns?.[columnId];
366
+ return this.state().pinning.isPinned(columnId);
300
367
  }
301
368
  getPinState(columnId) {
302
369
  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;
package/dist/esm/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // Re-export core types + utils
2
2
  export * from '@alaarab/ogrid-core';
3
+ // Explicitly re-export constants for test resolution
4
+ export { CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, PEOPLE_SEARCH_DEBOUNCE_MS, DEFAULT_DEBOUNCE_MS, SIDEBAR_TRANSITION_MS, Z_INDEX, } from '@alaarab/ogrid-core';
3
5
  export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
4
6
  // Services
5
7
  export { OGridService } from './services/ogrid.service';
@@ -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);
@@ -66,6 +67,7 @@ let OGridService = class OGridService {
66
67
  this.onFirstDataRendered = signal(undefined);
67
68
  this.onError = signal(undefined);
68
69
  this.columnChooserProp = signal(undefined);
70
+ this.virtualScroll = signal(undefined);
69
71
  this.ariaLabel = signal(undefined);
70
72
  this.ariaLabelledBy = signal(undefined);
71
73
  // --- Internal state signals ---
@@ -225,6 +227,7 @@ let OGridService = class OGridService {
225
227
  freezeCols: this.freezeCols(),
226
228
  editable: this.editable(),
227
229
  cellSelection: this.cellSelection(),
230
+ density: this.density(),
228
231
  onCellValueChanged: this.onCellValueChanged(),
229
232
  onUndo: this.onUndo(),
230
233
  onRedo: this.onRedo(),
@@ -243,6 +246,7 @@ let OGridService = class OGridService {
243
246
  getUserByEmail: this.dataSource()?.getUserByEmail?.bind(this.dataSource()),
244
247
  layoutMode: this.layoutMode(),
245
248
  suppressHorizontalScroll: this.suppressHorizontalScroll(),
249
+ virtualScroll: this.virtualScroll(),
246
250
  'aria-label': this.ariaLabel(),
247
251
  'aria-labelledby': this.ariaLabelledBy(),
248
252
  emptyState: {
@@ -428,7 +432,7 @@ let OGridService = class OGridService {
428
432
  handleColumnPinned(columnId, pinned) {
429
433
  this.pinnedOverrides.update((prev) => {
430
434
  if (pinned === null) {
431
- const { [columnId]: _, ...rest } = prev;
435
+ const { [columnId]: _removed, ...rest } = prev;
432
436
  return rest;
433
437
  }
434
438
  return { ...prev, [columnId]: pinned };
@@ -487,6 +491,8 @@ let OGridService = class OGridService {
487
491
  this.editable.set(props.editable);
488
492
  if (props.cellSelection !== undefined)
489
493
  this.cellSelection.set(props.cellSelection);
494
+ if (props.density !== undefined)
495
+ this.density.set(props.density);
490
496
  if (props.onCellValueChanged)
491
497
  this.onCellValueChanged.set(props.onCellValueChanged);
492
498
  if (props.onUndo)
@@ -515,6 +521,8 @@ let OGridService = class OGridService {
515
521
  this.onError.set(props.onError);
516
522
  if (props.columnChooser !== undefined)
517
523
  this.columnChooserProp.set(props.columnChooser);
524
+ if (props.virtualScroll !== undefined)
525
+ this.virtualScroll.set(props.virtualScroll);
518
526
  if (props.entityLabelPlural !== undefined)
519
527
  this.entityLabelPlural.set(props.entityLabelPlural);
520
528
  if (props.className !== undefined)
@@ -28,7 +28,7 @@ let VirtualScrollService = class VirtualScrollService {
28
28
  // Scrollable container reference for programmatic scrolling
29
29
  this.containerEl = null;
30
30
  // --- Derived computed signals ---
31
- this.rowHeight = computed(() => this.config().rowHeight);
31
+ this.rowHeight = computed(() => this.config().rowHeight ?? 36);
32
32
  this.overscan = computed(() => this.config().overscan ?? 5);
33
33
  this.enabled = computed(() => this.config().enabled !== false);
34
34
  /** Whether virtual scrolling is actually active (enabled + enough rows). */
@@ -1,163 +1,6 @@
1
1
  /**
2
- * View model helpers for Angular DataGridTable. Core owns the logic; UI packages only render.
3
- * Ported from React's dataGridViewModel.ts to eliminate duplication in Angular Material and PrimeNG packages.
2
+ * View model helpers for Angular DataGridTable.
3
+ * Pure functions live in @alaarab/ogrid-core. This file re-exports them.
4
4
  */
5
- import { getCellValue, isInSelectionRange } from '@alaarab/ogrid-core';
6
- /**
7
- * Returns ColumnHeaderFilter props from column def and grid filter/sort state.
8
- * Use in Angular Material and PrimeNG DataGridTableComponent instead of inline logic.
9
- */
10
- export function getHeaderFilterConfig(col, input) {
11
- const filterable = col.filterable && typeof col.filterable === 'object' ? col.filterable : null;
12
- const filterType = (filterable?.type ?? 'none');
13
- const filterField = filterable?.filterField ?? col.columnId;
14
- const sortable = col.sortable !== false;
15
- const filterValue = input.filters[filterField];
16
- const base = {
17
- columnKey: col.columnId,
18
- columnName: col.name,
19
- filterType,
20
- isSorted: input.sortBy === col.columnId,
21
- isSortedDescending: input.sortBy === col.columnId && input.sortDirection === 'desc',
22
- onSort: sortable ? () => input.onColumnSort(col.columnId) : undefined,
23
- };
24
- if (filterType === 'text') {
25
- return {
26
- ...base,
27
- textValue: filterValue?.type === 'text' ? filterValue.value : '',
28
- onTextChange: (v) => input.onFilterChange(filterField, v.trim() ? { type: 'text', value: v } : undefined),
29
- };
30
- }
31
- if (filterType === 'people') {
32
- return {
33
- ...base,
34
- selectedUser: filterValue?.type === 'people' ? filterValue.value : undefined,
35
- onUserChange: (u) => input.onFilterChange(filterField, u ? { type: 'people', value: u } : undefined),
36
- peopleSearch: input.peopleSearch,
37
- };
38
- }
39
- if (filterType === 'multiSelect') {
40
- return {
41
- ...base,
42
- options: input.filterOptions[filterField] ?? [],
43
- isLoadingOptions: input.loadingFilterOptions[filterField] ?? false,
44
- selectedValues: filterValue?.type === 'multiSelect' ? filterValue.value : [],
45
- onFilterChange: (values) => input.onFilterChange(filterField, values.length ? { type: 'multiSelect', value: values } : undefined),
46
- };
47
- }
48
- if (filterType === 'date') {
49
- return {
50
- ...base,
51
- dateValue: filterValue?.type === 'date' ? filterValue.value : undefined,
52
- onDateChange: (v) => input.onFilterChange(filterField, v ? { type: 'date', value: v } : undefined),
53
- };
54
- }
55
- return base;
56
- }
57
- /**
58
- * Returns a descriptor for rendering a cell. UI uses this to decide editing-inline vs editing-popover vs display
59
- * and to apply isActive, isInRange, etc. without duplicating the boolean logic.
60
- */
61
- export function getCellRenderDescriptor(item, col, rowIndex, colIdx, input) {
62
- const rowId = input.getRowId(item);
63
- const globalColIndex = colIdx + input.colOffset;
64
- const colEditable = col.editable === true ||
65
- (typeof col.editable === 'function' && col.editable(item));
66
- const canEditInline = input.editable !== false &&
67
- !!colEditable &&
68
- !!input.onCellValueChanged &&
69
- typeof col.cellEditor !== 'function';
70
- const canEditPopup = input.editable !== false &&
71
- !!colEditable &&
72
- !!input.onCellValueChanged &&
73
- typeof col.cellEditor === 'function';
74
- const canEditAny = canEditInline || canEditPopup;
75
- const isEditing = input.editingCell?.rowId === rowId &&
76
- input.editingCell?.columnId === col.columnId;
77
- const isActive = input.activeCell?.rowIndex === rowIndex &&
78
- input.activeCell?.columnIndex === globalColIndex;
79
- const isInRange = input.selectionRange != null &&
80
- isInSelectionRange(input.selectionRange, rowIndex, colIdx);
81
- const isInCutRange = input.cutRange != null &&
82
- isInSelectionRange(input.cutRange, rowIndex, colIdx);
83
- const isInCopyRange = input.copyRange != null &&
84
- isInSelectionRange(input.copyRange, rowIndex, colIdx);
85
- const isSelectionEndCell = !input.isDragging &&
86
- input.copyRange == null &&
87
- input.cutRange == null &&
88
- input.selectionRange != null &&
89
- rowIndex === input.selectionRange.endRow &&
90
- colIdx === input.selectionRange.endCol;
91
- let mode = 'display';
92
- let editorType;
93
- const value = getCellValue(item, col);
94
- if (isEditing && canEditInline) {
95
- mode = 'editing-inline';
96
- if (col.cellEditor === 'text' ||
97
- col.cellEditor === 'select' ||
98
- col.cellEditor === 'checkbox' ||
99
- col.cellEditor === 'richSelect' ||
100
- col.cellEditor === 'date') {
101
- editorType = col.cellEditor;
102
- }
103
- else if (col.type === 'date') {
104
- editorType = 'date';
105
- }
106
- else if (col.type === 'boolean') {
107
- editorType = 'checkbox';
108
- }
109
- else {
110
- editorType = 'text';
111
- }
112
- }
113
- else if (isEditing && canEditPopup) {
114
- mode = 'editing-popover';
115
- }
116
- return {
117
- mode,
118
- editorType,
119
- value,
120
- isActive,
121
- isInRange,
122
- isInCutRange,
123
- isInCopyRange,
124
- isSelectionEndCell,
125
- canEditAny,
126
- globalColIndex,
127
- rowId,
128
- rowIndex,
129
- displayValue: value,
130
- };
131
- }
132
- // --- Cell rendering helpers (reduce DataGridTable view-layer duplication) ---
133
- /**
134
- * Resolves display content for a cell in display mode.
135
- * Handles the renderCell → valueFormatter → String() fallback chain.
136
- */
137
- export function resolveCellDisplayContent(col, item, displayValue) {
138
- if (col.renderCell && typeof col.renderCell === 'function') {
139
- const result = col.renderCell(item);
140
- return result != null ? String(result) : '';
141
- }
142
- if (col.valueFormatter)
143
- return String(col.valueFormatter(displayValue, item) ?? '');
144
- if (displayValue == null)
145
- return '';
146
- if (col.type === 'date') {
147
- const d = new Date(String(displayValue));
148
- if (!Number.isNaN(d.getTime()))
149
- return d.toLocaleDateString();
150
- }
151
- if (col.type === 'boolean') {
152
- return displayValue ? 'True' : 'False';
153
- }
154
- return String(displayValue);
155
- }
156
- /**
157
- * Resolves the cellStyle from a column def, handling both function and static values.
158
- */
159
- export function resolveCellStyle(col, item) {
160
- if (!col.cellStyle)
161
- return undefined;
162
- return typeof col.cellStyle === 'function' ? col.cellStyle(item) : col.cellStyle;
163
- }
5
+ // Re-export everything from core's dataGridViewModel
6
+ export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from '@alaarab/ogrid-core';
@@ -64,42 +64,5 @@ export function createDebouncedCallback(fn, delayMs) {
64
64
  }, delayMs);
65
65
  });
66
66
  }
67
- /**
68
- * Simple debounce function (non-Angular-specific, can be used anywhere).
69
- * Returns a debounced version of the provided function.
70
- *
71
- * @param fn - The function to debounce
72
- * @param delayMs - Delay in milliseconds
73
- * @returns A debounced version of the function with a `cancel()` method
74
- *
75
- * @example
76
- * ```typescript
77
- * const handleResize = debounce(() => {
78
- * console.log('Window resized');
79
- * }, 200);
80
- *
81
- * window.addEventListener('resize', handleResize);
82
- *
83
- * // Later, cancel pending execution
84
- * handleResize.cancel();
85
- * ```
86
- */
87
- export function debounce(fn, delayMs) {
88
- let timeoutId = null;
89
- const debounced = ((...args) => {
90
- if (timeoutId !== null) {
91
- clearTimeout(timeoutId);
92
- }
93
- timeoutId = setTimeout(() => {
94
- fn(...args);
95
- timeoutId = null;
96
- }, delayMs);
97
- });
98
- debounced.cancel = () => {
99
- if (timeoutId !== null) {
100
- clearTimeout(timeoutId);
101
- timeoutId = null;
102
- }
103
- };
104
- return debounced;
105
- }
67
+ // Re-export simple debounce from core
68
+ export { debounce } from '@alaarab/ogrid-core';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Shared utilities for Angular DataGridTable view layer.
3
3
  */
4
- export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, } from './dataGridViewModel';
4
+ export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './dataGridViewModel';
5
5
  // Debounce utilities
6
6
  export { createDebouncedSignal, createDebouncedCallback, debounce, } from './debounce';
7
7
  // Latest ref utilities
@@ -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;
@@ -27,6 +27,7 @@ export declare abstract class BaseDataGridTableComponent<T = any> {
27
27
  protected abstract getTableContainerRef(): ElementRef<HTMLElement> | undefined;
28
28
  readonly state: import("@angular/core").Signal<import("../services/datagrid-state.service").DataGridStateResult<T>>;
29
29
  readonly tableContainerEl: import("@angular/core").Signal<HTMLElement | null>;
30
+ readonly allItems: import("@angular/core").Signal<T[]>;
30
31
  readonly items: import("@angular/core").Signal<T[]>;
31
32
  readonly getRowId: import("@angular/core").Signal<(item: T) => RowId>;
32
33
  readonly isLoading: import("@angular/core").Signal<boolean>;
@@ -103,6 +104,15 @@ export declare abstract class BaseDataGridTableComponent<T = any> {
103
104
  onCellValueChanged?: ((event: import("@alaarab/ogrid-core").ICellValueChangedEvent<T>) => void) | undefined;
104
105
  isDragging: boolean;
105
106
  }>;
107
+ readonly pinnedColumnsMap: import("@angular/core").Signal<Record<string, "left" | "right">>;
108
+ readonly vsEnabled: import("@angular/core").Signal<boolean>;
109
+ readonly vsVisibleRange: import("@angular/core").Signal<import("@alaarab/ogrid-core").IVisibleRange>;
110
+ readonly vsTopSpacerHeight: import("@angular/core").Signal<number>;
111
+ readonly vsBottomSpacerHeight: import("@angular/core").Signal<number>;
112
+ readonly vsVisibleItems: import("@angular/core").Signal<T[]>;
113
+ readonly vsStartIndex: import("@angular/core").Signal<number>;
114
+ readonly popoverAnchorEl: import("@angular/core").Signal<HTMLElement | null>;
115
+ readonly pendingEditorValueForPopover: import("@angular/core").Signal<unknown>;
106
116
  readonly allowOverflowX: import("@angular/core").Signal<boolean>;
107
117
  readonly selectionCellCount: import("@angular/core").Signal<number | undefined>;
108
118
  readonly headerRows: import("@angular/core").Signal<import("@alaarab/ogrid-core").HeaderRow<T>[]>;
@@ -123,10 +133,16 @@ export declare abstract class BaseDataGridTableComponent<T = any> {
123
133
  getColumnWidth(col: IColumnDef<T>): number;
124
134
  getFilterConfig(col: IColumnDef<T>): HeaderFilterConfig;
125
135
  getCellDescriptor(item: T, col: IColumnDef<T>, rowIndex: number, colIdx: number): CellRenderDescriptor;
126
- resolveCellContent(col: IColumnDef<T>, item: T, displayValue: unknown): string;
136
+ resolveCellContent(col: IColumnDef<T>, item: T, displayValue: unknown): unknown;
127
137
  resolveCellStyleFn(col: IColumnDef<T>, item: T): Record<string, string> | undefined;
138
+ buildPopoverEditorProps(item: T, col: IColumnDef<T>, descriptor: CellRenderDescriptor): unknown;
128
139
  getSelectValues(col: IColumnDef<T>): string[];
129
140
  formatDateForInput(value: unknown): string;
141
+ onWrapperScroll(event: Event): void;
142
+ setPopoverAnchorEl(el: HTMLElement | null): void;
143
+ setPendingEditorValue(value: unknown): void;
144
+ cancelPopoverEdit(): void;
145
+ commitPopoverEdit(item: T, columnId: string, oldValue: unknown, newValue: unknown, rowIndex: number, globalColIndex: number): void;
130
146
  onWrapperMouseDown(event: MouseEvent): void;
131
147
  onGridKeyDown(event: KeyboardEvent): void;
132
148
  onCellMouseDown(event: MouseEvent, rowIndex: number, globalColIndex: number): void;
@@ -1,4 +1,5 @@
1
1
  export * from '@alaarab/ogrid-core';
2
+ export { CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, PEOPLE_SEARCH_DEBOUNCE_MS, DEFAULT_DEBOUNCE_MS, SIDEBAR_TRANSITION_MS, Z_INDEX, } from '@alaarab/ogrid-core';
2
3
  export type { IColumnDef, IColumnGroupDef, IColumnDefinition, ICellEditorProps, } from './types';
3
4
  export type { IOGridProps, IOGridClientProps, IOGridServerProps, IOGridDataGridProps, } from './types';
4
5
  export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, ICellValueChangedEvent, CellEditorParams, IValueParserParams, IDateFilterValue, HeaderCell, HeaderRow, RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, } from './types';
@@ -6,10 +7,9 @@ export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types
6
7
  export { OGridService } from './services/ogrid.service';
7
8
  export type { ColumnChooserPlacement, OGridPagination, OGridColumnChooser, OGridFilters, OGridSideBarState, } from './services/ogrid.service';
8
9
  export { DataGridStateService } from './services/datagrid-state.service';
9
- export type { DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridStateResult, } from './services/datagrid-state.service';
10
+ export type { DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, DataGridStateResult, } from './services/datagrid-state.service';
10
11
  export { ColumnReorderService } from './services/column-reorder.service';
11
12
  export { VirtualScrollService } from './services/virtual-scroll.service';
12
- export type { IVirtualScrollConfig } from './services/virtual-scroll.service';
13
13
  export { OGridLayoutComponent } from './components/ogrid-layout.component';
14
14
  export { StatusBarComponent } from './components/status-bar.component';
15
15
  export { GridContextMenuComponent } from './components/grid-context-menu.component';
@@ -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;
@@ -1,4 +1,4 @@
1
- import type { RowId, IOGridApi, IFilters, FilterValue, IRowSelectionChangeEvent, IStatusBarProps, IColumnDefinition, IDataSource, ISideBarDef, SideBarPanelId } from '../types';
1
+ import type { RowId, IOGridApi, IFilters, FilterValue, IRowSelectionChangeEvent, IStatusBarProps, IColumnDefinition, IDataSource, ISideBarDef, IVirtualScrollConfig, SideBarPanelId } from '../types';
2
2
  import type { IOGridProps, IOGridDataGridProps } from '../types';
3
3
  import type { IColumnDef, IColumnGroupDef, ICellValueChangedEvent } from '../types';
4
4
  /** Resolved column chooser placement. */
@@ -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>;
@@ -100,6 +101,7 @@ export declare class OGridService<T> {
100
101
  readonly onFirstDataRendered: import("@angular/core").WritableSignal<(() => void) | undefined>;
101
102
  readonly onError: import("@angular/core").WritableSignal<((error: unknown) => void) | undefined>;
102
103
  readonly columnChooserProp: import("@angular/core").WritableSignal<boolean | "toolbar" | "sidebar" | undefined>;
104
+ readonly virtualScroll: import("@angular/core").WritableSignal<IVirtualScrollConfig | undefined>;
103
105
  readonly ariaLabel: import("@angular/core").WritableSignal<string | undefined>;
104
106
  readonly ariaLabelledBy: import("@angular/core").WritableSignal<string | undefined>;
105
107
  private readonly internalData;
@@ -1,12 +1,4 @@
1
- import type { IVisibleRange } from '@alaarab/ogrid-core';
2
- export interface IVirtualScrollConfig {
3
- /** Enable virtual scrolling. Default: true when provided. */
4
- enabled?: boolean;
5
- /** Row height in pixels (required for virtualization). */
6
- rowHeight: number;
7
- /** Number of rows to render outside the visible area. Default: 5. */
8
- overscan?: number;
9
- }
1
+ import type { IVisibleRange, IVirtualScrollConfig } from '@alaarab/ogrid-core';
10
2
  /**
11
3
  * Manages virtual scrolling state using Angular signals.
12
4
  * Port of React's useVirtualScroll hook.
@@ -1,8 +1,8 @@
1
1
  import type { TemplateRef } from '@angular/core';
2
2
  import type { IColumnDef, IColumnGroupDef, ICellValueChangedEvent } from './columnTypes';
3
- export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IOGridApi, } from '@alaarab/ogrid-core';
3
+ export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IVirtualScrollConfig, IOGridApi, } from '@alaarab/ogrid-core';
4
4
  export { toUserLike, isInSelectionRange, normalizeSelectionRange } from '@alaarab/ogrid-core';
5
- import type { RowId, UserLike, IFilters, FilterValue, RowSelectionMode, IRowSelectionChangeEvent, IStatusBarProps, IDataSource, ISideBarDef } from '@alaarab/ogrid-core';
5
+ import type { RowId, UserLike, IFilters, FilterValue, RowSelectionMode, IRowSelectionChangeEvent, IStatusBarProps, IDataSource, ISideBarDef, IVirtualScrollConfig } from '@alaarab/ogrid-core';
6
6
  /** Base props shared by both client-side and server-side OGrid modes. */
7
7
  interface IOGridBaseProps<T> {
8
8
  columns: (IColumnDef<T> | IColumnGroupDef<T>)[];
@@ -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;
@@ -56,6 +57,7 @@ interface IOGridBaseProps<T> {
56
57
  layoutMode?: 'content' | 'fill';
57
58
  suppressHorizontalScroll?: boolean;
58
59
  sideBar?: boolean | ISideBarDef;
60
+ virtualScroll?: IVirtualScrollConfig;
59
61
  pageSizeOptions?: number[];
60
62
  onFirstDataRendered?: () => void;
61
63
  onError?: (error: unknown) => void;
@@ -99,6 +101,7 @@ export interface IOGridDataGridProps<T> {
99
101
  loadingMessage?: string;
100
102
  editable?: boolean;
101
103
  cellSelection?: boolean;
104
+ density?: 'compact' | 'normal' | 'comfortable';
102
105
  onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
103
106
  onUndo?: () => void;
104
107
  onRedo?: () => void;
@@ -117,6 +120,7 @@ export interface IOGridDataGridProps<T> {
117
120
  loadingFilterOptions: Record<string, boolean>;
118
121
  peopleSearch?: (query: string) => Promise<UserLike[]>;
119
122
  getUserByEmail?: (email: string) => Promise<UserLike | undefined>;
123
+ virtualScroll?: IVirtualScrollConfig;
120
124
  emptyState?: {
121
125
  onClearAll: () => void;
122
126
  hasActiveFilters: boolean;
@@ -1,3 +1,3 @@
1
1
  export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnDef, IColumnGroupDef, IColumnDefinition, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IValueParserParams, IDateFilterValue, HeaderCell, HeaderRow, } from './columnTypes';
2
- export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridClientProps, IOGridServerProps, IOGridDataGridProps, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, } from './dataGridTypes';
2
+ export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridClientProps, IOGridServerProps, IOGridDataGridProps, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IVirtualScrollConfig, } from './dataGridTypes';
3
3
  export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './dataGridTypes';
@@ -1,107 +1,6 @@
1
1
  /**
2
- * View model helpers for Angular DataGridTable. Core owns the logic; UI packages only render.
3
- * Ported from React's dataGridViewModel.ts to eliminate duplication in Angular Material and PrimeNG packages.
2
+ * View model helpers for Angular DataGridTable.
3
+ * Pure functions live in @alaarab/ogrid-core. This file re-exports them.
4
4
  */
5
- import type { ColumnFilterType, IDateFilterValue } from '../types/columnTypes';
6
- import type { IColumnDef } from '../types/columnTypes';
7
- import type { RowId, UserLike, IFilters, FilterValue } from '../types/dataGridTypes';
8
- export interface HeaderFilterConfigInput {
9
- sortBy?: string;
10
- sortDirection: 'asc' | 'desc';
11
- onColumnSort: (columnKey: string) => void;
12
- filters: IFilters;
13
- onFilterChange: (key: string, value: FilterValue | undefined) => void;
14
- filterOptions: Record<string, string[]>;
15
- loadingFilterOptions: Record<string, boolean>;
16
- peopleSearch?: (query: string) => Promise<UserLike[]>;
17
- }
18
- /** Props to pass to ColumnHeaderFilter. */
19
- export interface HeaderFilterConfig {
20
- columnKey: string;
21
- columnName: string;
22
- filterType: ColumnFilterType;
23
- isSorted?: boolean;
24
- isSortedDescending?: boolean;
25
- onSort?: () => void;
26
- selectedValues?: string[];
27
- onFilterChange?: (values: string[]) => void;
28
- options?: string[];
29
- isLoadingOptions?: boolean;
30
- textValue?: string;
31
- onTextChange?: (value: string) => void;
32
- selectedUser?: UserLike;
33
- onUserChange?: (user: UserLike | undefined) => void;
34
- peopleSearch?: (query: string) => Promise<UserLike[]>;
35
- dateValue?: IDateFilterValue;
36
- onDateChange?: (value: IDateFilterValue | undefined) => void;
37
- }
38
- /**
39
- * Returns ColumnHeaderFilter props from column def and grid filter/sort state.
40
- * Use in Angular Material and PrimeNG DataGridTableComponent instead of inline logic.
41
- */
42
- export declare function getHeaderFilterConfig<T>(col: IColumnDef<T>, input: HeaderFilterConfigInput): HeaderFilterConfig;
43
- export type CellRenderMode = 'editing-inline' | 'editing-popover' | 'display';
44
- export interface CellRenderDescriptorInput<T> {
45
- editingCell: {
46
- rowId: RowId;
47
- columnId: string;
48
- } | null;
49
- activeCell: {
50
- rowIndex: number;
51
- columnIndex: number;
52
- } | null;
53
- selectionRange: {
54
- startRow: number;
55
- startCol: number;
56
- endRow: number;
57
- endCol: number;
58
- } | null;
59
- cutRange: {
60
- startRow: number;
61
- startCol: number;
62
- endRow: number;
63
- endCol: number;
64
- } | null;
65
- copyRange: {
66
- startRow: number;
67
- startCol: number;
68
- endRow: number;
69
- endCol: number;
70
- } | null;
71
- colOffset: number;
72
- getRowId: (item: T) => RowId;
73
- editable?: boolean;
74
- onCellValueChanged?: unknown;
75
- /** True while user is drag-selecting cells — hides fill handle during drag. */
76
- isDragging?: boolean;
77
- }
78
- export interface CellRenderDescriptor {
79
- mode: CellRenderMode;
80
- editorType?: 'text' | 'select' | 'checkbox' | 'richSelect' | 'date';
81
- value?: unknown;
82
- isActive: boolean;
83
- isInRange: boolean;
84
- isInCutRange: boolean;
85
- isInCopyRange: boolean;
86
- isSelectionEndCell: boolean;
87
- canEditAny: boolean;
88
- globalColIndex: number;
89
- rowId: RowId;
90
- rowIndex: number;
91
- /** Raw value for display (when mode === 'display'). */
92
- displayValue?: unknown;
93
- }
94
- /**
95
- * Returns a descriptor for rendering a cell. UI uses this to decide editing-inline vs editing-popover vs display
96
- * and to apply isActive, isInRange, etc. without duplicating the boolean logic.
97
- */
98
- export declare function getCellRenderDescriptor<T>(item: T, col: IColumnDef<T>, rowIndex: number, colIdx: number, input: CellRenderDescriptorInput<T>): CellRenderDescriptor;
99
- /**
100
- * Resolves display content for a cell in display mode.
101
- * Handles the renderCell → valueFormatter → String() fallback chain.
102
- */
103
- export declare function resolveCellDisplayContent<T>(col: IColumnDef<T>, item: T, displayValue: unknown): string;
104
- /**
105
- * Resolves the cellStyle from a column def, handling both function and static values.
106
- */
107
- export declare function resolveCellStyle<T>(col: IColumnDef<T>, item: T): Record<string, string> | undefined;
5
+ export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from '@alaarab/ogrid-core';
6
+ export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from '@alaarab/ogrid-core';
@@ -43,26 +43,4 @@ export declare function createDebouncedSignal<T>(source: Signal<T>, delayMs: num
43
43
  * ```
44
44
  */
45
45
  export declare function createDebouncedCallback<T extends (...args: unknown[]) => void>(fn: T, delayMs: number): T;
46
- /**
47
- * Simple debounce function (non-Angular-specific, can be used anywhere).
48
- * Returns a debounced version of the provided function.
49
- *
50
- * @param fn - The function to debounce
51
- * @param delayMs - Delay in milliseconds
52
- * @returns A debounced version of the function with a `cancel()` method
53
- *
54
- * @example
55
- * ```typescript
56
- * const handleResize = debounce(() => {
57
- * console.log('Window resized');
58
- * }, 200);
59
- *
60
- * window.addEventListener('resize', handleResize);
61
- *
62
- * // Later, cancel pending execution
63
- * handleResize.cancel();
64
- * ```
65
- */
66
- export declare function debounce<T extends (...args: unknown[]) => void>(fn: T, delayMs: number): T & {
67
- cancel: () => void;
68
- };
46
+ export { debounce } from '@alaarab/ogrid-core';
@@ -2,6 +2,6 @@
2
2
  * Shared utilities for Angular DataGridTable view layer.
3
3
  */
4
4
  export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from './dataGridViewModel';
5
- export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, } from './dataGridViewModel';
5
+ export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './dataGridViewModel';
6
6
  export { createDebouncedSignal, createDebouncedCallback, debounce, } from './debounce';
7
7
  export { createLatestRef, createLatestCallback, } from './latestRef';
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.8",
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",
@@ -16,13 +16,26 @@
16
16
  "build": "rimraf dist && tsc -p tsconfig.build.json",
17
17
  "test": "jest --passWithNoTests"
18
18
  },
19
- "keywords": ["ogrid", "angular", "datatable", "typescript", "grid", "signals"],
19
+ "keywords": [
20
+ "ogrid",
21
+ "angular",
22
+ "datatable",
23
+ "typescript",
24
+ "grid",
25
+ "signals"
26
+ ],
20
27
  "author": "Ala Arab",
21
28
  "license": "MIT",
22
- "files": ["dist", "README.md", "LICENSE"],
23
- "engines": { "node": ">=18" },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
24
37
  "dependencies": {
25
- "@alaarab/ogrid-core": "2.0.6"
38
+ "@alaarab/ogrid-core": "2.0.7"
26
39
  },
27
40
  "peerDependencies": {
28
41
  "@angular/core": "^21.0.0",
@@ -39,5 +52,7 @@
39
52
  "typescript": "^5.9.3"
40
53
  },
41
54
  "sideEffects": false,
42
- "publishConfig": { "access": "public" }
55
+ "publishConfig": {
56
+ "access": "public"
57
+ }
43
58
  }