@alaarab/ogrid-angular 2.1.14 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, validateColumns, validateRowIds, computeNextSortState, mergeFilter, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, parseValue, UndoRedoStack, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, getCellValue, computeTabNavigation, applyFillValues, computeAggregations, getDataGridStatusBarConfig, computeVisibleRange, computeTotalHeight, getScrollTopForRow, validateVirtualScrollConfig, GRID_BORDER_RADIUS, getStatusBarParts, GRID_CONTEXT_MENU_ITEMS, formatShortcut, injectGlobalStyles, buildHeaderRows, getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildPopoverEditorProps, measureColumnContentWidth, getPaginationViewModel, ROW_NUMBER_COLUMN_WIDTH, reorderColumnArray, findCtrlArrowTarget, measureRange } from '@alaarab/ogrid-core';
1
+ import { flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, validateColumns, processClientSideDataAsync, validateRowIds, computeNextSortState, mergeFilter, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, parseValue, UndoRedoStack, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, getCellValue, computeTabNavigation, applyFillValues, computeAggregations, getDataGridStatusBarConfig, computeVisibleRange, computeTotalHeight, computeVisibleColumnRange, getScrollTopForRow, validateVirtualScrollConfig, GRID_BORDER_RADIUS, getStatusBarParts, GRID_CONTEXT_MENU_ITEMS, formatShortcut, injectGlobalStyles, partitionColumnsForVirtualization, buildHeaderRows, getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildPopoverEditorProps, measureColumnContentWidth, getPaginationViewModel, ROW_NUMBER_COLUMN_WIDTH, reorderColumnArray, findCtrlArrowTarget, measureRange } from '@alaarab/ogrid-core';
2
2
  export * from '@alaarab/ogrid-core';
3
3
  export { CELL_PADDING, CHECKBOX_COLUMN_WIDTH, DEFAULT_DEBOUNCE_MS, DEFAULT_MIN_COLUMN_WIDTH, GRID_BORDER_RADIUS, PEOPLE_SEARCH_DEBOUNCE_MS, ROW_NUMBER_COLUMN_WIDTH, SIDEBAR_TRANSITION_MS, Z_INDEX, debounce, getCellRenderDescriptor, getHeaderFilterConfig, isInSelectionRange, normalizeSelectionRange, resolveCellDisplayContent, resolveCellStyle, toUserLike } from '@alaarab/ogrid-core';
4
4
  import { Injectable, Input, Component, ChangeDetectionStrategy, ViewEncapsulation, Output, ViewChild, inject, DestroyRef, signal, computed, effect, NgZone, EventEmitter, Injector, EnvironmentInjector, createComponent } from '@angular/core';
@@ -75,6 +75,7 @@ var OGridService = class {
75
75
  this.virtualScroll = signal(void 0);
76
76
  this.ariaLabel = signal(void 0);
77
77
  this.ariaLabelledBy = signal(void 0);
78
+ this.workerSort = signal(false);
78
79
  // --- Internal state signals ---
79
80
  this.internalData = signal([]);
80
81
  this.internalLoading = signal(false);
@@ -94,6 +95,9 @@ var OGridService = class {
94
95
  this.filterAbortController = null;
95
96
  this.refreshCounter = signal(0);
96
97
  this.firstDataRendered = signal(false);
98
+ // Worker sort async state
99
+ this.asyncClientItems = signal(null);
100
+ this.workerSortAbortId = 0;
97
101
  // Side bar state
98
102
  this.sideBarActivePanel = signal(null);
99
103
  // Filter options state
@@ -134,8 +138,9 @@ var OGridService = class {
134
138
  if (this.hasServerFilterOptions()) return this.serverFilterOptions();
135
139
  return deriveFilterOptionsFromData(this.displayData(), this.columns());
136
140
  });
141
+ /** Sync path: used when workerSort is off. */
137
142
  this.clientItemsAndTotal = computed(() => {
138
- if (!this.isClientSide()) return null;
143
+ if (!this.isClientSide() || this.workerSort()) return null;
139
144
  const rows = processClientSideData(
140
145
  this.displayData(),
141
146
  this.columns(),
@@ -148,12 +153,18 @@ var OGridService = class {
148
153
  const paged = rows.slice(start, start + this.pageSize());
149
154
  return { items: paged, totalCount: total };
150
155
  });
156
+ /** Resolved client items — sync or async depending on workerSort. */
157
+ this.resolvedClientItems = computed(() => {
158
+ const syncResult = this.clientItemsAndTotal();
159
+ if (syncResult) return syncResult;
160
+ return this.asyncClientItems();
161
+ });
151
162
  this.displayItems = computed(() => {
152
- const cit = this.clientItemsAndTotal();
163
+ const cit = this.resolvedClientItems();
153
164
  return this.isClientSide() && cit ? cit.items : this.serverItems();
154
165
  });
155
166
  this.displayTotalCount = computed(() => {
156
- const cit = this.clientItemsAndTotal();
167
+ const cit = this.resolvedClientItems();
157
168
  return this.isClientSide() && cit ? cit.totalCount : this.serverTotalCount();
158
169
  });
159
170
  this.hasActiveFilters = computed(() => {
@@ -323,6 +334,34 @@ var OGridService = class {
323
334
  validateColumns(cols);
324
335
  }
325
336
  });
337
+ effect((onCleanup) => {
338
+ if (!this.isClientSide() || !this.workerSort()) return;
339
+ const data = this.displayData();
340
+ const cols = this.columns();
341
+ const filters = this.filters();
342
+ const sortField = this.sort().field;
343
+ const sortDir = this.sort().direction;
344
+ const page = this.page();
345
+ const ps = this.pageSize();
346
+ const abortId = ++this.workerSortAbortId;
347
+ processClientSideDataAsync(data, cols, filters, sortField, sortDir).then((rows) => {
348
+ if (abortId !== this.workerSortAbortId) return;
349
+ const total = rows.length;
350
+ const start = (page - 1) * ps;
351
+ const paged = rows.slice(start, start + ps);
352
+ this.asyncClientItems.set({ items: paged, totalCount: total });
353
+ }).catch(() => {
354
+ if (abortId !== this.workerSortAbortId) return;
355
+ const rows = processClientSideData(data, cols, filters, sortField, sortDir);
356
+ const total = rows.length;
357
+ const start = (page - 1) * ps;
358
+ const paged = rows.slice(start, start + ps);
359
+ this.asyncClientItems.set({ items: paged, totalCount: total });
360
+ });
361
+ onCleanup(() => {
362
+ this.workerSortAbortId++;
363
+ });
364
+ });
326
365
  effect((onCleanup) => {
327
366
  const ds = this.dataSource();
328
367
  if (!this.isServerSide() || !ds) {
@@ -486,6 +525,13 @@ var OGridService = class {
486
525
  // --- Configure from props ---
487
526
  configure(props) {
488
527
  this.columnsProp.set(props.columns);
528
+ if (Object.keys(this.pinnedOverrides()).length === 0) {
529
+ const initial = {};
530
+ for (const col of flattenColumns(props.columns)) {
531
+ if (col.pinned) initial[col.columnId] = col.pinned;
532
+ }
533
+ if (Object.keys(initial).length > 0) this.pinnedOverrides.set(initial);
534
+ }
489
535
  this.getRowId.set(props.getRowId);
490
536
  if ("data" in props && props.data !== void 0) this.data.set(props.data);
491
537
  if ("dataSource" in props && props.dataSource !== void 0) this.dataSource.set(props.dataSource);
@@ -528,6 +574,7 @@ var OGridService = class {
528
574
  if (props.columnChooser !== void 0) this.columnChooserProp.set(props.columnChooser);
529
575
  if (props.columnReorder !== void 0) this.columnReorder.set(props.columnReorder);
530
576
  if (props.virtualScroll !== void 0) this.virtualScroll.set(props.virtualScroll);
577
+ if (props.workerSort !== void 0) this.workerSort.set(props.workerSort);
531
578
  if (props.entityLabelPlural !== void 0) this.entityLabelPlural.set(props.entityLabelPlural);
532
579
  if (props.className !== void 0) this.className.set(props.className);
533
580
  if (props.layoutMode !== void 0) this.layoutMode.set(props.layoutMode);
@@ -1371,10 +1418,13 @@ var DataGridInteractionHelper = class {
1371
1418
  const finalRange = this.liveDragRange;
1372
1419
  if (finalRange) {
1373
1420
  this.setSelectionRange(finalRange);
1374
- this.setActiveCell({
1375
- rowIndex: finalRange.endRow,
1376
- columnIndex: finalRange.endCol + colOffset
1377
- });
1421
+ const anchor = this.dragStartPos;
1422
+ if (anchor) {
1423
+ this.setActiveCell({
1424
+ rowIndex: anchor.row,
1425
+ columnIndex: anchor.col + colOffset
1426
+ });
1427
+ }
1378
1428
  }
1379
1429
  }
1380
1430
  this.clearDragAttrs(wrapperEl);
@@ -1530,6 +1580,7 @@ var DataGridStateService = class {
1530
1580
  const p = this.props();
1531
1581
  if (!p || p.items.length === 0) return false;
1532
1582
  const selected = this.selectedRowIds();
1583
+ if (selected.size !== p.items.length) return false;
1533
1584
  return p.items.every((item) => selected.has(p.getRowId(item)));
1534
1585
  });
1535
1586
  this.someSelected = computed(() => {
@@ -2035,7 +2086,7 @@ var DataGridStateService = class {
2035
2086
  endCol: fillDragEnd.endCol
2036
2087
  });
2037
2088
  this.setSelectionRange(norm);
2038
- this.setActiveCell({ rowIndex: fillDragEnd.endRow, columnIndex: fillDragEnd.endCol + colOff });
2089
+ this.setActiveCell({ rowIndex: fillStart.startRow, columnIndex: fillStart.startCol + colOff });
2039
2090
  if (!p) return;
2040
2091
  const items = p.items;
2041
2092
  const visibleCols = this.visibleCols();
@@ -2225,6 +2276,10 @@ var VirtualScrollService = class {
2225
2276
  this.containerHeight = signal(0);
2226
2277
  // --- Internal state ---
2227
2278
  this.scrollTop = signal(0);
2279
+ this.scrollLeft = signal(0);
2280
+ // --- Column virtualization inputs ---
2281
+ this.columnWidths = signal([]);
2282
+ this.containerWidth = signal(0);
2228
2283
  // Scrollable container reference for programmatic scrolling
2229
2284
  this.containerEl = null;
2230
2285
  // --- Derived computed signals ---
@@ -2254,6 +2309,21 @@ var VirtualScrollService = class {
2254
2309
  });
2255
2310
  /** Total scrollable height in pixels. */
2256
2311
  this.totalHeight = computed(() => computeTotalHeight(this.totalRows(), this.rowHeight()));
2312
+ // --- Column virtualization ---
2313
+ this.columnsEnabled = computed(() => this.config().columns === true);
2314
+ this.columnOverscan = computed(() => this.config().columnOverscan ?? 2);
2315
+ /** The visible column range with spacer widths, or null when column virtualization is off. */
2316
+ this.columnRange = computed(() => {
2317
+ if (!this.columnsEnabled()) return null;
2318
+ const widths = this.columnWidths();
2319
+ if (widths.length === 0) return null;
2320
+ return computeVisibleColumnRange(
2321
+ this.scrollLeft(),
2322
+ widths,
2323
+ this.containerWidth(),
2324
+ this.columnOverscan()
2325
+ );
2326
+ });
2257
2327
  this.destroyRef.onDestroy(() => {
2258
2328
  this.containerEl = null;
2259
2329
  });
@@ -2267,10 +2337,12 @@ var VirtualScrollService = class {
2267
2337
  }
2268
2338
  /**
2269
2339
  * Call this from the container's scroll event handler.
2340
+ * Tracks both vertical and horizontal scroll positions.
2270
2341
  */
2271
2342
  onScroll(event) {
2272
2343
  const target = event.target;
2273
2344
  this.scrollTop.set(target.scrollTop);
2345
+ this.scrollLeft.set(target.scrollLeft);
2274
2346
  }
2275
2347
  /**
2276
2348
  * Scroll to a specific row index.
@@ -3105,6 +3177,10 @@ var MarchingAntsOverlayComponent = class {
3105
3177
  this.resizeObserver.observe(container);
3106
3178
  }
3107
3179
  }
3180
+ isSingleCellSelection() {
3181
+ const r = this.selectionRange;
3182
+ return r != null && r.startRow === r.endRow && r.startCol === r.endCol;
3183
+ }
3108
3184
  clipRangeMatchesSel() {
3109
3185
  const selRange = this.selectionRange;
3110
3186
  const clipRange = this.copyRange ?? this.cutRange;
@@ -3152,7 +3228,7 @@ MarchingAntsOverlayComponent = __decorateClass([
3152
3228
  .ogrid-marching-ants-svg--clip { z-index: 5; }
3153
3229
  `],
3154
3230
  template: `
3155
- @if (selRect() && !clipRangeMatchesSel()) {
3231
+ @if (selRect() && !clipRangeMatchesSel() && !isSingleCellSelection()) {
3156
3232
  <svg
3157
3233
  class="ogrid-marching-ants-svg ogrid-marching-ants-svg--selection"
3158
3234
  [style.top.px]="selRect()!.top"
@@ -3396,6 +3472,53 @@ var BaseDataGridTableComponent = class {
3396
3472
  if (!this.vsEnabled()) return 0;
3397
3473
  return this.vsVisibleRange().startIndex;
3398
3474
  });
3475
+ // Column virtualization
3476
+ this.vsColumnsEnabled = computed(() => this.virtualScrollService.columnsEnabled());
3477
+ this.vsColumnRange = computed(() => this.virtualScrollService.columnRange());
3478
+ /** Partitioned columns for column virtualization: pinned-left, virtualized-unpinned, pinned-right, with spacer widths. */
3479
+ this.vsColumnPartition = computed(() => {
3480
+ if (!this.vsColumnsEnabled()) return null;
3481
+ const cols = this.visibleCols();
3482
+ const range = this.vsColumnRange();
3483
+ const props = this.getProps();
3484
+ const pinnedCols = props?.pinnedColumns;
3485
+ return partitionColumnsForVirtualization(cols, range, pinnedCols);
3486
+ });
3487
+ /** Column layouts filtered by column virtualization range (or all if column virt is off). */
3488
+ this.vsColumnLayouts = computed(() => {
3489
+ const allLayouts = this.columnLayouts();
3490
+ const partition = this.vsColumnPartition();
3491
+ if (!partition) return allLayouts;
3492
+ const visibleIds = /* @__PURE__ */ new Set();
3493
+ for (const col of partition.pinnedLeft) visibleIds.add(col.columnId);
3494
+ for (const col of partition.virtualizedUnpinned) visibleIds.add(col.columnId);
3495
+ for (const col of partition.pinnedRight) visibleIds.add(col.columnId);
3496
+ return allLayouts.filter((layout) => visibleIds.has(layout.col.columnId));
3497
+ });
3498
+ /** Visible columns filtered by column virtualization range (or all if column virt is off). */
3499
+ this.vsVisibleCols = computed(() => {
3500
+ const allCols = this.visibleCols();
3501
+ const partition = this.vsColumnPartition();
3502
+ if (!partition) return allCols;
3503
+ const visibleIds = /* @__PURE__ */ new Set();
3504
+ for (const col of partition.pinnedLeft) visibleIds.add(col.columnId);
3505
+ for (const col of partition.virtualizedUnpinned) visibleIds.add(col.columnId);
3506
+ for (const col of partition.pinnedRight) visibleIds.add(col.columnId);
3507
+ return allCols.filter((col) => visibleIds.has(col.columnId));
3508
+ });
3509
+ /** Left spacer width for column virtualization (in pixels). */
3510
+ this.vsLeftSpacerWidth = computed(() => this.vsColumnPartition()?.leftSpacerWidth ?? 0);
3511
+ /** Right spacer width for column virtualization (in pixels). */
3512
+ this.vsRightSpacerWidth = computed(() => this.vsColumnPartition()?.rightSpacerWidth ?? 0);
3513
+ /** Map from columnId to its global index in visibleCols. Used to maintain correct colIdx for cell descriptors during column virtualization. */
3514
+ this.globalColIndexMap = computed(() => {
3515
+ const cols = this.visibleCols();
3516
+ const map = /* @__PURE__ */ new Map();
3517
+ for (let i = 0; i < cols.length; i++) {
3518
+ map.set(cols[i].columnId, i);
3519
+ }
3520
+ return map;
3521
+ });
3399
3522
  // Popover editing
3400
3523
  this.popoverAnchorEl = computed(() => this.editingState().popoverAnchorEl);
3401
3524
  this.pendingEditorValueForPopover = computed(() => this.editingState().pendingEditorValue);
@@ -3519,6 +3642,10 @@ var BaseDataGridTableComponent = class {
3519
3642
  this.measuredColumnWidths.set(measured);
3520
3643
  }
3521
3644
  }
3645
+ /** Get global column index for a column (correct even during column virtualization). */
3646
+ getGlobalColIndex(col) {
3647
+ return this.globalColIndexMap().get(col.columnId) ?? 0;
3648
+ }
3522
3649
  /**
3523
3650
  * Initialize base wiring effects. Must be called from subclass constructor.
3524
3651
  *
@@ -3569,16 +3696,26 @@ var BaseDataGridTableComponent = class {
3569
3696
  this.virtualScrollService.updateConfig({
3570
3697
  enabled: p.virtualScroll.enabled,
3571
3698
  rowHeight: p.virtualScroll.rowHeight,
3572
- overscan: p.virtualScroll.overscan
3699
+ overscan: p.virtualScroll.overscan,
3700
+ columns: p.virtualScroll.columns,
3701
+ columnOverscan: p.virtualScroll.columnOverscan
3573
3702
  });
3574
3703
  }
3575
3704
  }
3576
3705
  });
3706
+ effect(() => {
3707
+ const layouts = this.columnLayouts();
3708
+ const props = this.getProps();
3709
+ const pinnedCols = props?.pinnedColumns ?? {};
3710
+ const unpinnedWidths = layouts.filter((l) => !l.pinnedLeft && !l.pinnedRight && !pinnedCols[l.col.columnId]).map((l) => l.width);
3711
+ this.virtualScrollService.columnWidths.set(unpinnedWidths);
3712
+ });
3577
3713
  effect(() => {
3578
3714
  const el = this.wrapperElSignal();
3579
3715
  if (el) {
3580
3716
  this.virtualScrollService.setContainer(el);
3581
3717
  this.virtualScrollService.containerHeight.set(el.clientHeight);
3718
+ this.virtualScrollService.containerWidth.set(el.clientWidth);
3582
3719
  }
3583
3720
  });
3584
3721
  }
@@ -134,6 +134,34 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
134
134
  readonly vsBottomSpacerHeight: import("@angular/core").Signal<number>;
135
135
  readonly vsVisibleItems: import("@angular/core").Signal<T[]>;
136
136
  readonly vsStartIndex: import("@angular/core").Signal<number>;
137
+ readonly vsColumnsEnabled: import("@angular/core").Signal<boolean>;
138
+ readonly vsColumnRange: import("@angular/core").Signal<import("@alaarab/ogrid-core").IVisibleColumnRange | null>;
139
+ /** Partitioned columns for column virtualization: pinned-left, virtualized-unpinned, pinned-right, with spacer widths. */
140
+ readonly vsColumnPartition: import("@angular/core").Signal<{
141
+ pinnedLeft: import("@alaarab/ogrid-core").IColumnDef<T>[];
142
+ virtualizedUnpinned: import("@alaarab/ogrid-core").IColumnDef<T>[];
143
+ pinnedRight: import("@alaarab/ogrid-core").IColumnDef<T>[];
144
+ leftSpacerWidth: number;
145
+ rightSpacerWidth: number;
146
+ } | null>;
147
+ /** Column layouts filtered by column virtualization range (or all if column virt is off). */
148
+ readonly vsColumnLayouts: import("@angular/core").Signal<{
149
+ col: IColumnDef<T>;
150
+ pinnedLeft: boolean;
151
+ pinnedRight: boolean;
152
+ minWidth: number;
153
+ width: number;
154
+ }[]>;
155
+ /** Visible columns filtered by column virtualization range (or all if column virt is off). */
156
+ readonly vsVisibleCols: import("@angular/core").Signal<IColumnDef<T>[]>;
157
+ /** Left spacer width for column virtualization (in pixels). */
158
+ readonly vsLeftSpacerWidth: import("@angular/core").Signal<number>;
159
+ /** Right spacer width for column virtualization (in pixels). */
160
+ readonly vsRightSpacerWidth: import("@angular/core").Signal<number>;
161
+ /** Map from columnId to its global index in visibleCols. Used to maintain correct colIdx for cell descriptors during column virtualization. */
162
+ readonly globalColIndexMap: import("@angular/core").Signal<Map<string, number>>;
163
+ /** Get global column index for a column (correct even during column virtualization). */
164
+ getGlobalColIndex(col: IColumnDef<T>): number;
137
165
  readonly popoverAnchorEl: import("@angular/core").Signal<HTMLElement | null>;
138
166
  readonly pendingEditorValueForPopover: import("@angular/core").Signal<unknown>;
139
167
  readonly allowOverflowX: import("@angular/core").Signal<boolean>;
@@ -19,6 +19,7 @@ export declare class MarchingAntsOverlayComponent implements OnChanges {
19
19
  constructor();
20
20
  ngOnChanges(_changes: SimpleChanges): void;
21
21
  private recalculate;
22
+ isSingleCellSelection(): boolean;
22
23
  clipRangeMatchesSel(): boolean;
23
24
  max0(n: number): number;
24
25
  }
@@ -108,6 +108,7 @@ export declare class OGridService<T> {
108
108
  readonly virtualScroll: import("@angular/core").WritableSignal<IVirtualScrollConfig | undefined>;
109
109
  readonly ariaLabel: import("@angular/core").WritableSignal<string | undefined>;
110
110
  readonly ariaLabelledBy: import("@angular/core").WritableSignal<string | undefined>;
111
+ readonly workerSort: import("@angular/core").WritableSignal<boolean>;
111
112
  private readonly internalData;
112
113
  private readonly internalLoading;
113
114
  private readonly internalPage;
@@ -125,6 +126,8 @@ export declare class OGridService<T> {
125
126
  private filterAbortController;
126
127
  private readonly refreshCounter;
127
128
  private readonly firstDataRendered;
129
+ private readonly asyncClientItems;
130
+ private workerSortAbortId;
128
131
  private readonly sideBarActivePanel;
129
132
  private readonly serverFilterOptions;
130
133
  private readonly loadingFilterOptions;
@@ -147,10 +150,16 @@ export declare class OGridService<T> {
147
150
  readonly multiSelectFilterFields: import("@angular/core").Signal<string[]>;
148
151
  readonly hasServerFilterOptions: import("@angular/core").Signal<boolean>;
149
152
  readonly clientFilterOptions: import("@angular/core").Signal<Record<string, string[]>>;
153
+ /** Sync path: used when workerSort is off. */
150
154
  readonly clientItemsAndTotal: import("@angular/core").Signal<{
151
155
  items: T[];
152
156
  totalCount: number;
153
157
  } | null>;
158
+ /** Resolved client items — sync or async depending on workerSort. */
159
+ readonly resolvedClientItems: import("@angular/core").Signal<{
160
+ items: T[];
161
+ totalCount: number;
162
+ } | null>;
154
163
  readonly displayItems: import("@angular/core").Signal<T[]>;
155
164
  readonly displayTotalCount: import("@angular/core").Signal<number>;
156
165
  readonly hasActiveFilters: import("@angular/core").Signal<boolean>;
@@ -1,4 +1,4 @@
1
- import type { IVisibleRange, IVirtualScrollConfig } from '@alaarab/ogrid-core';
1
+ import type { IVisibleRange, IVisibleColumnRange, IVirtualScrollConfig } from '@alaarab/ogrid-core';
2
2
  /**
3
3
  * Manages virtual scrolling state using Angular signals.
4
4
  * Port of React's useVirtualScroll hook.
@@ -13,6 +13,9 @@ export declare class VirtualScrollService {
13
13
  readonly config: import("@angular/core").WritableSignal<IVirtualScrollConfig>;
14
14
  readonly containerHeight: import("@angular/core").WritableSignal<number>;
15
15
  readonly scrollTop: import("@angular/core").WritableSignal<number>;
16
+ readonly scrollLeft: import("@angular/core").WritableSignal<number>;
17
+ readonly columnWidths: import("@angular/core").WritableSignal<number[]>;
18
+ readonly containerWidth: import("@angular/core").WritableSignal<number>;
16
19
  private containerEl;
17
20
  readonly rowHeight: import("@angular/core").Signal<number>;
18
21
  readonly overscan: import("@angular/core").Signal<number>;
@@ -24,6 +27,10 @@ export declare class VirtualScrollService {
24
27
  readonly visibleRange: import("@angular/core").Signal<IVisibleRange>;
25
28
  /** Total scrollable height in pixels. */
26
29
  readonly totalHeight: import("@angular/core").Signal<number>;
30
+ readonly columnsEnabled: import("@angular/core").Signal<boolean>;
31
+ readonly columnOverscan: import("@angular/core").Signal<number>;
32
+ /** The visible column range with spacer widths, or null when column virtualization is off. */
33
+ readonly columnRange: import("@angular/core").Signal<IVisibleColumnRange | null>;
27
34
  constructor();
28
35
  /**
29
36
  * Set the scrollable container element.
@@ -32,6 +39,7 @@ export declare class VirtualScrollService {
32
39
  setContainer(el: HTMLElement | null): void;
33
40
  /**
34
41
  * Call this from the container's scroll event handler.
42
+ * Tracks both vertical and horizontal scroll positions.
35
43
  */
36
44
  onScroll(event: Event): void;
37
45
  /**
@@ -63,6 +63,8 @@ interface IOGridBaseProps<T> {
63
63
  sideBar?: boolean | ISideBarDef;
64
64
  columnReorder?: boolean;
65
65
  virtualScroll?: IVirtualScrollConfig;
66
+ /** Offload sort/filter to a Web Worker for large datasets. Falls back to sync when sort column has a custom compare. */
67
+ workerSort?: boolean;
66
68
  /** Fixed row height in pixels. Overrides default row height (36px). */
67
69
  rowHeight?: number;
68
70
  pageSizeOptions?: number[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-angular",
3
- "version": "2.1.14",
3
+ "version": "2.2.0",
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",
@@ -35,7 +35,7 @@
35
35
  "node": ">=18"
36
36
  },
37
37
  "dependencies": {
38
- "@alaarab/ogrid-core": "2.1.14"
38
+ "@alaarab/ogrid-core": "2.2.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@angular/core": "^21.0.0",