@alaarab/ogrid-angular 2.0.2 → 2.0.4

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.
@@ -0,0 +1,163 @@
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.
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
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Debounce utilities for Angular using signals.
3
+ * Provides functional parity with React's useDebounce and useDebouncedCallback.
4
+ */
5
+ import { signal, effect } from '@angular/core';
6
+ /**
7
+ * Creates a debounced signal that updates after the specified delay when the source value changes.
8
+ *
9
+ * @param source - The signal to debounce
10
+ * @param delayMs - Delay in milliseconds
11
+ * @returns A signal containing the debounced value
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const searchQuery = signal('');
16
+ * const debouncedQuery = createDebouncedSignal(searchQuery, 300);
17
+ *
18
+ * effect(() => {
19
+ * console.log('Debounced search:', debouncedQuery());
20
+ * });
21
+ * ```
22
+ */
23
+ export function createDebouncedSignal(source, delayMs) {
24
+ const debouncedValue = signal(source());
25
+ effect((onCleanup) => {
26
+ const currentValue = source();
27
+ const timeoutId = setTimeout(() => {
28
+ debouncedValue.set(currentValue);
29
+ }, delayMs);
30
+ onCleanup(() => clearTimeout(timeoutId));
31
+ });
32
+ return debouncedValue;
33
+ }
34
+ /**
35
+ * Creates a debounced function that delays invoking the provided function
36
+ * until after `delayMs` milliseconds have elapsed since the last time it was invoked.
37
+ *
38
+ * @param fn - The function to debounce
39
+ * @param delayMs - Delay in milliseconds
40
+ * @returns A debounced version of the function
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const saveData = (value: string) => {
45
+ * console.log('Saving:', value);
46
+ * };
47
+ *
48
+ * const debouncedSave = createDebouncedCallback(saveData, 500);
49
+ *
50
+ * // Multiple rapid calls will only trigger once after 500ms
51
+ * debouncedSave('hello');
52
+ * debouncedSave('world'); // Only this will execute after 500ms
53
+ * ```
54
+ */
55
+ export function createDebouncedCallback(fn, delayMs) {
56
+ let timeoutId = null;
57
+ return ((...args) => {
58
+ if (timeoutId !== null) {
59
+ clearTimeout(timeoutId);
60
+ }
61
+ timeoutId = setTimeout(() => {
62
+ fn(...args);
63
+ timeoutId = null;
64
+ }, delayMs);
65
+ });
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
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Shared utilities for Angular DataGridTable view layer.
3
+ */
4
+ export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, } from './dataGridViewModel';
5
+ // Debounce utilities
6
+ export { createDebouncedSignal, createDebouncedCallback, debounce, } from './debounce';
7
+ // Latest ref utilities
8
+ export { createLatestRef, createLatestCallback, } from './latestRef';
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Latest ref utility for Angular using signals.
3
+ * Provides functional parity with React's useLatestRef.
4
+ */
5
+ /**
6
+ * Creates a stable wrapper function that always calls the latest version of the provided function.
7
+ * Useful for event handlers and callbacks where you want a stable reference but need to call
8
+ * the latest implementation.
9
+ *
10
+ * @param fn - Signal containing the function to wrap
11
+ * @returns A stable function that always invokes the latest version
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * class MyService {
16
+ * readonly onSave = signal<(value: string) => void>((val) => console.log('Default:', val));
17
+ * readonly stableOnSave = createLatestCallback(this.onSave);
18
+ *
19
+ * constructor() {
20
+ * // Setup event listener with stable reference
21
+ * effect((onCleanup) => {
22
+ * // stableOnSave never changes, so this effect only runs once
23
+ * const callback = () => this.stableOnSave('data');
24
+ * window.addEventListener('click', callback);
25
+ * onCleanup(() => window.removeEventListener('click', callback));
26
+ * });
27
+ * }
28
+ *
29
+ * updateHandler(newFn: (value: string) => void) {
30
+ * // Even though we change the function, the callback reference stays stable
31
+ * this.onSave.set(newFn);
32
+ * }
33
+ * }
34
+ * ```
35
+ */
36
+ export function createLatestCallback(fn) {
37
+ // Return a stable function that always calls the current value of the signal
38
+ return ((...args) => {
39
+ return fn()(...args);
40
+ });
41
+ }
42
+ /**
43
+ * Alias for createLatestCallback for consistency with React/Vue naming.
44
+ * @deprecated Use createLatestCallback instead
45
+ */
46
+ export const createLatestRef = createLatestCallback;
@@ -12,6 +12,7 @@ export declare class MarchingAntsOverlayComponent {
12
12
  readonly copyRange: import("@angular/core").InputSignal<ISelectionRange | null>;
13
13
  readonly cutRange: import("@angular/core").InputSignal<ISelectionRange | null>;
14
14
  readonly colOffset: import("@angular/core").InputSignal<number>;
15
+ readonly columnSizingVersion: import("@angular/core").InputSignal<number>;
15
16
  readonly selRect: import("@angular/core").WritableSignal<OverlayRect | null>;
16
17
  readonly clipRect: import("@angular/core").WritableSignal<OverlayRect | null>;
17
18
  private rafId;
@@ -7,6 +7,9 @@ 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
9
  export type { DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridStateResult, } from './services/datagrid-state.service';
10
+ export { ColumnReorderService } from './services/column-reorder.service';
11
+ export { VirtualScrollService } from './services/virtual-scroll.service';
12
+ export type { IVirtualScrollConfig } from './services/virtual-scroll.service';
10
13
  export { OGridLayoutComponent } from './components/ogrid-layout.component';
11
14
  export { StatusBarComponent } from './components/status-bar.component';
12
15
  export { GridContextMenuComponent } from './components/grid-context-menu.component';
@@ -14,3 +17,5 @@ export { SideBarComponent } from './components/sidebar.component';
14
17
  export type { SideBarProps, SideBarFilterColumn } from './components/sidebar.component';
15
18
  export { MarchingAntsOverlayComponent } from './components/marching-ants-overlay.component';
16
19
  export { EmptyStateComponent } from './components/empty-state.component';
20
+ export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from './utils';
21
+ export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, createDebouncedSignal, createDebouncedCallback, debounce, createLatestRef, createLatestCallback, } from './utils';
@@ -0,0 +1,30 @@
1
+ import type { IColumnDef } from '../types';
2
+ /**
3
+ * Manages column reorder drag interactions with RAF-throttled updates.
4
+ * Angular signals-based port of React's useColumnReorder hook.
5
+ */
6
+ export declare class ColumnReorderService<T> {
7
+ private destroyRef;
8
+ readonly columns: import("@angular/core").WritableSignal<IColumnDef<T>[]>;
9
+ readonly columnOrder: import("@angular/core").WritableSignal<string[] | undefined>;
10
+ readonly onColumnOrderChange: import("@angular/core").WritableSignal<((order: string[]) => void) | undefined>;
11
+ readonly enabled: import("@angular/core").WritableSignal<boolean>;
12
+ readonly wrapperEl: import("@angular/core").WritableSignal<HTMLElement | null>;
13
+ readonly isDragging: import("@angular/core").WritableSignal<boolean>;
14
+ readonly dropIndicatorX: import("@angular/core").WritableSignal<number | null>;
15
+ private rafId;
16
+ private cleanupFn;
17
+ private latestDropTargetIndex;
18
+ constructor();
19
+ /**
20
+ * Call this from the header cell's mousedown handler.
21
+ * @param columnId - The column being dragged
22
+ * @param event - The native MouseEvent
23
+ */
24
+ handleHeaderMouseDown(columnId: string, event: MouseEvent): void;
25
+ /**
26
+ * Calculate drop target from mouse position and header cell rects.
27
+ * Same logic as React's useColumnReorder inline calculation.
28
+ */
29
+ private calculateDrop;
30
+ }
@@ -9,6 +9,7 @@ export interface DataGridLayoutState<T> {
9
9
  totalColCount: number;
10
10
  colOffset: number;
11
11
  hasCheckboxCol: boolean;
12
+ hasRowNumbersCol: boolean;
12
13
  rowIndexByRowId: Map<RowId, number>;
13
14
  containerWidth: number;
14
15
  minTableWidth: number;
@@ -159,7 +160,6 @@ export declare class DataGridStateService<T> {
159
160
  private rafId;
160
161
  private lastMousePos;
161
162
  private autoScrollInterval;
162
- private windowCleanups;
163
163
  private resizeObserver;
164
164
  private readonly propsResolved;
165
165
  readonly cellSelection: import("@angular/core").Signal<boolean>;
@@ -169,8 +169,10 @@ export declare class DataGridStateService<T> {
169
169
  readonly visibleCols: import("@angular/core").Signal<IColumnDef<T>[]>;
170
170
  readonly visibleColumnCount: import("@angular/core").Signal<number>;
171
171
  readonly hasCheckboxCol: import("@angular/core").Signal<boolean>;
172
+ readonly hasRowNumbersCol: import("@angular/core").Signal<boolean>;
173
+ readonly specialColsCount: import("@angular/core").Signal<number>;
172
174
  readonly totalColCount: import("@angular/core").Signal<number>;
173
- readonly colOffset: import("@angular/core").Signal<0 | 1>;
175
+ readonly colOffset: import("@angular/core").Signal<number>;
174
176
  readonly rowIndexByRowId: import("@angular/core").Signal<Map<RowId, number>>;
175
177
  readonly selectedRowIds: import("@angular/core").Signal<Set<RowId>>;
176
178
  readonly allSelected: import("@angular/core").Signal<boolean>;
@@ -105,10 +105,10 @@ export declare class OGridService<T> {
105
105
  private readonly internalData;
106
106
  private readonly internalLoading;
107
107
  private readonly internalPage;
108
- private readonly internalPageSize;
109
- private readonly internalSort;
108
+ private readonly internalPageSizeOverride;
109
+ private readonly internalSortOverride;
110
110
  private readonly internalFilters;
111
- private readonly internalVisibleColumns;
111
+ private readonly internalVisibleColumnsOverride;
112
112
  private readonly internalSelectedRows;
113
113
  private readonly columnWidthOverrides;
114
114
  private readonly pinnedOverrides;
@@ -121,9 +121,6 @@ export declare class OGridService<T> {
121
121
  private readonly sideBarActivePanel;
122
122
  private readonly serverFilterOptions;
123
123
  private readonly loadingFilterOptions;
124
- private sortInitialized;
125
- private visibleColumnsInitialized;
126
- private pageSizeInitialized;
127
124
  readonly columns: import("@angular/core").Signal<IColumnDef<T>[]>;
128
125
  readonly isServerSide: import("@angular/core").Signal<boolean>;
129
126
  readonly isClientSide: import("@angular/core").Signal<boolean>;
@@ -187,5 +184,31 @@ export declare class OGridService<T> {
187
184
  handleColumnResized(columnId: string, width: number): void;
188
185
  handleColumnPinned(columnId: string, pinned: 'left' | 'right' | null): void;
189
186
  configure(props: IOGridProps<T>): void;
187
+ /**
188
+ * Pin a column to the left or right edge.
189
+ */
190
+ pinColumn(columnId: string, side: 'left' | 'right'): void;
191
+ /**
192
+ * Unpin a column (remove sticky positioning).
193
+ */
194
+ unpinColumn(columnId: string): void;
195
+ /**
196
+ * Check if a column is pinned and which side.
197
+ */
198
+ isPinned(columnId: string): 'left' | 'right' | undefined;
199
+ /**
200
+ * Compute sticky left offsets for left-pinned columns.
201
+ * Returns a map of columnId -> left offset in pixels.
202
+ */
203
+ computeLeftOffsets(visibleCols: {
204
+ columnId: string;
205
+ }[], columnWidths: Record<string, number>, defaultWidth: number, hasCheckboxColumn: boolean, checkboxColumnWidth: number): Record<string, number>;
206
+ /**
207
+ * Compute sticky right offsets for right-pinned columns.
208
+ * Returns a map of columnId -> right offset in pixels.
209
+ */
210
+ computeRightOffsets(visibleCols: {
211
+ columnId: string;
212
+ }[], columnWidths: Record<string, number>, defaultWidth: number): Record<string, number>;
190
213
  getApi(): IOGridApi<T>;
191
214
  }
@@ -0,0 +1,54 @@
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
+ }
10
+ /**
11
+ * Manages virtual scrolling state using Angular signals.
12
+ * Port of React's useVirtualScroll hook.
13
+ *
14
+ * Uses core's pure-TS `computeVisibleRange` and `getScrollTopForRow` utilities.
15
+ * The UI layer (Angular Material / PrimeNG) provides the scrollable container
16
+ * and calls `onScroll()` / sets `containerHeight`.
17
+ */
18
+ export declare class VirtualScrollService {
19
+ private destroyRef;
20
+ readonly totalRows: import("@angular/core").WritableSignal<number>;
21
+ readonly config: import("@angular/core").WritableSignal<IVirtualScrollConfig>;
22
+ readonly containerHeight: import("@angular/core").WritableSignal<number>;
23
+ readonly scrollTop: import("@angular/core").WritableSignal<number>;
24
+ private containerEl;
25
+ readonly rowHeight: import("@angular/core").Signal<number>;
26
+ readonly overscan: import("@angular/core").Signal<number>;
27
+ readonly enabled: import("@angular/core").Signal<boolean>;
28
+ /** Whether virtual scrolling is actually active (enabled + enough rows). */
29
+ readonly isActive: import("@angular/core").Signal<boolean>;
30
+ /** The visible range of rows with spacer offsets. */
31
+ readonly visibleRange: import("@angular/core").Signal<IVisibleRange>;
32
+ /** Total scrollable height in pixels. */
33
+ readonly totalHeight: import("@angular/core").Signal<number>;
34
+ constructor();
35
+ /**
36
+ * Set the scrollable container element.
37
+ * Used for programmatic scrolling (scrollToRow).
38
+ */
39
+ setContainer(el: HTMLElement | null): void;
40
+ /**
41
+ * Call this from the container's scroll event handler.
42
+ */
43
+ onScroll(event: Event): void;
44
+ /**
45
+ * Scroll to a specific row index.
46
+ * @param index - The row index to scroll to.
47
+ * @param align - Where to position the row: 'start' (top), 'center', or 'end' (bottom). Default: 'start'.
48
+ */
49
+ scrollToRow(index: number, align?: 'start' | 'center' | 'end'): void;
50
+ /**
51
+ * Update the virtual scroll configuration.
52
+ */
53
+ updateConfig(updates: Partial<IVirtualScrollConfig>): void;
54
+ }
@@ -107,6 +107,9 @@ export interface IOGridDataGridProps<T> {
107
107
  rowSelection?: RowSelectionMode;
108
108
  selectedRows?: Set<RowId>;
109
109
  onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
110
+ showRowNumbers?: boolean;
111
+ currentPage?: number;
112
+ pageSize?: number;
110
113
  statusBar?: IStatusBarProps;
111
114
  filters: IFilters;
112
115
  onFilterChange: (key: string, value: FilterValue | undefined) => void;
@@ -0,0 +1,107 @@
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.
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;