@alaarab/ogrid-react 2.1.14 → 2.1.15

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.
Files changed (2) hide show
  1. package/dist/esm/index.js +62 -40
  2. package/package.json +2 -2
package/dist/esm/index.js CHANGED
@@ -507,23 +507,39 @@ function processClientSideData(data, columns, filters, sortBy, sortDirection) {
507
507
  const trimmed = val.value.trim();
508
508
  if (trimmed) {
509
509
  const lower = trimmed.toLowerCase();
510
- predicates.push((r) => String(getCellValue(r, col) ?? "").toLowerCase().includes(lower));
510
+ const textCache = /* @__PURE__ */ new Map();
511
+ for (let j = 0; j < data.length; j++) {
512
+ textCache.set(data[j], String(getCellValue(data[j], col) ?? "").toLowerCase());
513
+ }
514
+ predicates.push((r) => (textCache.get(r) ?? "").includes(lower));
511
515
  }
512
516
  break;
513
517
  }
514
518
  case "people": {
515
519
  const email = val.value.email.toLowerCase();
516
- predicates.push((r) => String(getCellValue(r, col) ?? "").toLowerCase() === email);
520
+ const peopleCache = /* @__PURE__ */ new Map();
521
+ for (let j = 0; j < data.length; j++) {
522
+ peopleCache.set(data[j], String(getCellValue(data[j], col) ?? "").toLowerCase());
523
+ }
524
+ predicates.push((r) => (peopleCache.get(r) ?? "") === email);
517
525
  break;
518
526
  }
519
527
  case "date": {
520
528
  const dv = val.value;
521
529
  const fromTs = dv.from ? (/* @__PURE__ */ new Date(dv.from + "T00:00:00")).getTime() : NaN;
522
530
  const toTs = dv.to ? (/* @__PURE__ */ new Date(dv.to + "T23:59:59.999")).getTime() : NaN;
531
+ const dateCache = /* @__PURE__ */ new Map();
532
+ for (let j = 0; j < data.length; j++) {
533
+ const cellVal = getCellValue(data[j], col);
534
+ if (cellVal == null) {
535
+ dateCache.set(data[j], NaN);
536
+ } else {
537
+ const t = new Date(String(cellVal)).getTime();
538
+ dateCache.set(data[j], Number.isNaN(t) ? NaN : t);
539
+ }
540
+ }
523
541
  predicates.push((r) => {
524
- const cellVal = getCellValue(r, col);
525
- if (cellVal == null) return false;
526
- const cellTs = new Date(String(cellVal)).getTime();
542
+ const cellTs = dateCache.get(r) ?? NaN;
527
543
  if (Number.isNaN(cellTs)) return false;
528
544
  if (!Number.isNaN(fromTs) && cellTs < fromTs) return false;
529
545
  if (!Number.isNaN(toTs) && cellTs > toTs) return false;
@@ -1220,7 +1236,7 @@ function computeRowSelectionState(selectedIds, items, getRowId) {
1220
1236
  if (selectedIds.size === 0 || items.length === 0) {
1221
1237
  return { allSelected: false, someSelected: false };
1222
1238
  }
1223
- const allSelected = items.every((item) => selectedIds.has(getRowId(item)));
1239
+ const allSelected = selectedIds.size >= items.length && items.every((item) => selectedIds.has(getRowId(item)));
1224
1240
  const someSelected = !allSelected && selectedIds.size > 0;
1225
1241
  return { allSelected, someSelected };
1226
1242
  }
@@ -1541,7 +1557,7 @@ function useFilterOptions(dataSource, fields) {
1541
1557
  );
1542
1558
  setFilterOptions(results);
1543
1559
  setLoadingOptions(EMPTY_LOADING);
1544
- }, [stableFields]);
1560
+ }, [stableFields, dataSourceRef]);
1545
1561
  useEffect(() => {
1546
1562
  load().catch(() => {
1547
1563
  });
@@ -1752,7 +1768,7 @@ function useOGridDataFetching(params) {
1752
1768
  }).finally(() => {
1753
1769
  if (id === fetchIdRef.current) setServerLoading(false);
1754
1770
  });
1755
- }, [isServerSide, page, pageSize, sort.field, sort.direction, stableFilters, refreshCounter]);
1771
+ }, [isServerSide, page, pageSize, sort.field, sort.direction, stableFilters, refreshCounter, dataSourceRef, onErrorRef]);
1756
1772
  const displayItems = isClientSide && clientItemsAndTotal ? clientItemsAndTotal.items : serverItems;
1757
1773
  const displayTotalCount = isClientSide && clientItemsAndTotal ? clientItemsAndTotal.totalCount : serverTotalCount;
1758
1774
  const onFirstDataRenderedRef = useLatestRef(onFirstDataRendered);
@@ -1762,7 +1778,7 @@ function useOGridDataFetching(params) {
1762
1778
  firstDataRenderedRef.current = true;
1763
1779
  onFirstDataRenderedRef.current?.();
1764
1780
  }
1765
- }, [displayItems.length]);
1781
+ }, [displayItems.length, onFirstDataRenderedRef]);
1766
1782
  return {
1767
1783
  displayItems,
1768
1784
  displayTotalCount,
@@ -1867,28 +1883,28 @@ function useOGrid(props, ref) {
1867
1883
  const getRowIdStableRef = useLatestRef(getRowIdProp);
1868
1884
  const getRowId = useCallback((item) => getRowIdStableRef.current(item), [getRowIdStableRef]);
1869
1885
  const onColumnOrderChangeRef = useLatestRef(onColumnOrderChangeProp);
1886
+ const hasColumnOrderChange = onColumnOrderChangeProp != null;
1870
1887
  const onColumnOrderChange = useMemo(
1871
- () => onColumnOrderChangeProp ? (order) => onColumnOrderChangeRef.current?.(order) : void 0,
1872
- // eslint-disable-next-line react-hooks/exhaustive-deps
1873
- [!!onColumnOrderChangeProp]
1888
+ () => hasColumnOrderChange ? (order) => onColumnOrderChangeRef.current?.(order) : void 0,
1889
+ [hasColumnOrderChange, onColumnOrderChangeRef]
1874
1890
  );
1875
1891
  const onCellValueChangedRef = useLatestRef(onCellValueChangedProp);
1892
+ const hasCellValueChanged = onCellValueChangedProp != null;
1876
1893
  const onCellValueChanged = useMemo(
1877
- () => onCellValueChangedProp ? (event) => onCellValueChangedRef.current?.(event) : void 0,
1878
- // eslint-disable-next-line react-hooks/exhaustive-deps
1879
- [!!onCellValueChangedProp]
1894
+ () => hasCellValueChanged ? (event) => onCellValueChangedRef.current?.(event) : void 0,
1895
+ [hasCellValueChanged, onCellValueChangedRef]
1880
1896
  );
1881
1897
  const onUndoRef = useLatestRef(onUndoProp);
1898
+ const hasUndo = onUndoProp != null;
1882
1899
  const onUndo = useMemo(
1883
- () => onUndoProp ? () => onUndoRef.current?.() : void 0,
1884
- // eslint-disable-next-line react-hooks/exhaustive-deps
1885
- [!!onUndoProp]
1900
+ () => hasUndo ? () => onUndoRef.current?.() : void 0,
1901
+ [hasUndo, onUndoRef]
1886
1902
  );
1887
1903
  const onRedoRef = useLatestRef(onRedoProp);
1904
+ const hasRedo = onRedoProp != null;
1888
1905
  const onRedo = useMemo(
1889
- () => onRedoProp ? () => onRedoRef.current?.() : void 0,
1890
- // eslint-disable-next-line react-hooks/exhaustive-deps
1891
- [!!onRedoProp]
1906
+ () => hasRedo ? () => onRedoRef.current?.() : void 0,
1907
+ [hasRedo, onRedoRef]
1892
1908
  );
1893
1909
  const columnChooserPlacement = columnChooserProp === false ? "none" : columnChooserProp === "sidebar" ? "sidebar" : "toolbar";
1894
1910
  const columns = useMemo(() => flattenColumns(columnsProp), [columnsProp]);
@@ -1896,7 +1912,7 @@ function useOGrid(props, ref) {
1896
1912
  const rowIdsValidatedRef = useRef(false);
1897
1913
  useEffect(() => {
1898
1914
  validateColumns(columns);
1899
- }, []);
1915
+ }, [columns]);
1900
1916
  const defaultSortField = defaultSortBy ?? columns[0]?.columnId ?? "";
1901
1917
  const [internalData, setInternalData] = useState([]);
1902
1918
  const [internalLoading, setInternalLoading] = useState(false);
@@ -2518,15 +2534,17 @@ function useCellSelection(params) {
2518
2534
  }
2519
2535
  }
2520
2536
  if (!cellIndex) cellIndex = buildCellIndex(wrapperRef.current);
2537
+ let rebuilt = false;
2521
2538
  for (let r = minR; r <= maxR; r++) {
2522
2539
  for (let c = minC; c <= maxC; c++) {
2523
2540
  const key = `${r},${c + colOff}`;
2524
2541
  let el = cellIndex?.get(key);
2525
- if (el && !el.isConnected) {
2542
+ if (el && !el.isConnected && !rebuilt) {
2543
+ rebuilt = true;
2526
2544
  cellIndex = buildCellIndex(wrapperRef.current);
2527
2545
  el = cellIndex?.get(key);
2528
2546
  }
2529
- if (el) {
2547
+ if (el && el.isConnected) {
2530
2548
  styleCellInRange(el, r, c, minR, maxR, minC, maxC, anchor);
2531
2549
  }
2532
2550
  }
@@ -2661,10 +2679,13 @@ function useCellSelection(params) {
2661
2679
  const finalRange = liveDragRangeRef.current;
2662
2680
  if (finalRange) {
2663
2681
  setSelectionRange(finalRange);
2664
- setActiveCell({
2665
- rowIndex: finalRange.endRow,
2666
- columnIndex: finalRange.endCol + colOffsetRef.current
2667
- });
2682
+ const anchor = dragStartRef.current;
2683
+ if (anchor) {
2684
+ setActiveCell({
2685
+ rowIndex: anchor.row,
2686
+ columnIndex: anchor.col + colOffsetRef.current
2687
+ });
2688
+ }
2668
2689
  }
2669
2690
  }
2670
2691
  clearDragAttrs();
@@ -3129,8 +3150,7 @@ function useUndoRedo(params) {
3129
3150
  }
3130
3151
  onCellValueChangedRef.current(event);
3131
3152
  },
3132
- // eslint-disable-next-line react-hooks/exhaustive-deps
3133
- [getStack]
3153
+ [getStack, onCellValueChangedRef]
3134
3154
  );
3135
3155
  const beginBatch = useCallback(() => {
3136
3156
  getStack().beginBatch();
@@ -3156,7 +3176,7 @@ function useUndoRedo(params) {
3156
3176
  newValue: ev.oldValue
3157
3177
  });
3158
3178
  }
3159
- }, [getStack]);
3179
+ }, [getStack, onCellValueChangedRef]);
3160
3180
  const redo = useCallback(() => {
3161
3181
  if (!onCellValueChangedRef.current) return;
3162
3182
  const stack = getStack();
@@ -3167,7 +3187,7 @@ function useUndoRedo(params) {
3167
3187
  for (const ev of nextBatch) {
3168
3188
  onCellValueChangedRef.current(ev);
3169
3189
  }
3170
- }, [getStack]);
3190
+ }, [getStack, onCellValueChangedRef]);
3171
3191
  return {
3172
3192
  onCellValueChanged: onCellValueChanged ? wrapped : void 0,
3173
3193
  undo,
@@ -3307,7 +3327,7 @@ function useFillHandle(params) {
3307
3327
  endCol: end.endCol
3308
3328
  });
3309
3329
  setSelectionRange(norm);
3310
- setActiveCell({ rowIndex: end.endRow, columnIndex: end.endCol + colOffsetRef.current });
3330
+ setActiveCell({ rowIndex: fillDrag.startRow, columnIndex: fillDrag.startCol + colOffsetRef.current });
3311
3331
  const fillEvents = applyFillValues(norm, fillDrag.startRow, fillDrag.startCol, items, visibleCols);
3312
3332
  if (fillEvents.length > 0) {
3313
3333
  beginBatch?.();
@@ -3334,7 +3354,8 @@ function useFillHandle(params) {
3334
3354
  beginBatch,
3335
3355
  endBatch,
3336
3356
  colOffsetRef,
3337
- wrapperRef
3357
+ wrapperRef,
3358
+ onCellValueChangedRef
3338
3359
  ]);
3339
3360
  const selectionRangeRef = useRef(selectionRange);
3340
3361
  selectionRangeRef.current = selectionRange;
@@ -3369,7 +3390,7 @@ function useFillHandle(params) {
3369
3390
  for (const evt of fillEvents) onCellValueChangedRef.current(evt);
3370
3391
  endBatch?.();
3371
3392
  }
3372
- }, [editable, beginBatch, endBatch]);
3393
+ }, [editable, beginBatch, endBatch, onCellValueChangedRef, itemsRef, visibleColsRef]);
3373
3394
  return { fillDrag, setFillDrag, handleFillHandleMouseDown, fillDown };
3374
3395
  }
3375
3396
  function useTableLayout(params) {
@@ -3869,8 +3890,7 @@ function useDataGridEditing(params) {
3869
3890
  setSelectionRange({ startRow: newRow, startCol: localCol, endRow: newRow, endCol: localCol });
3870
3891
  }
3871
3892
  },
3872
- // eslint-disable-next-line react-hooks/exhaustive-deps
3873
- [setEditingCell, setPendingEditorValue, setActiveCell, setSelectionRange, colOffset, visibleColsRef, itemsLengthRef]
3893
+ [setEditingCell, setPendingEditorValue, setActiveCell, setSelectionRange, colOffset, visibleColsRef, itemsLengthRef, onCellValueChangedRef]
3874
3894
  );
3875
3895
  const cancelPopoverEdit = useCallback(() => {
3876
3896
  setEditingCell(null);
@@ -6007,7 +6027,7 @@ function useVirtualScroll(params) {
6007
6027
  } = params;
6008
6028
  useEffect(() => {
6009
6029
  validateVirtualScrollConfig({ enabled, rowHeight });
6010
- }, []);
6030
+ }, [enabled, rowHeight]);
6011
6031
  const isActive = enabled && totalRows >= threshold;
6012
6032
  const getScrollElement = useCallback(
6013
6033
  () => containerRef.current,
@@ -6201,8 +6221,10 @@ function useDataGridTableOrchestration(params) {
6201
6221
  const currentVersion = CellDescriptorCache.computeVersion(cellDescriptorInput);
6202
6222
  cellDescriptorCacheRef.current.updateVersion(currentVersion);
6203
6223
  const prevItemsRef = useRef(items);
6204
- if (prevItemsRef.current !== items) {
6224
+ const prevVisibleColsRef = useRef(visibleCols);
6225
+ if (prevItemsRef.current !== items || prevVisibleColsRef.current !== visibleCols) {
6205
6226
  prevItemsRef.current = items;
6227
+ prevVisibleColsRef.current = visibleCols;
6206
6228
  cellDescriptorCacheRef.current.clear();
6207
6229
  }
6208
6230
  const handleSingleRowClick = useCallback((e) => {
@@ -7137,7 +7159,7 @@ function MarchingAntsOverlay({
7137
7159
  const selR = selRect ? roundRect(selRect) : null;
7138
7160
  const clipR = clipRect ? roundRect(clipRect) : null;
7139
7161
  return /* @__PURE__ */ jsxs(Fragment, { children: [
7140
- selR && !isDragging && !clipRangeMatchesSel && /* @__PURE__ */ jsx(
7162
+ selR && !isDragging && !clipRangeMatchesSel && !(selectionRange && selectionRange.startRow === selectionRange.endRow && selectionRange.startCol === selectionRange.endCol) && /* @__PURE__ */ jsx(
7141
7163
  "svg",
7142
7164
  {
7143
7165
  style: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-react",
3
- "version": "2.1.14",
3
+ "version": "2.1.15",
4
4
  "description": "OGrid React – React hooks, headless components, and utilities for OGrid data grids.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -39,7 +39,7 @@
39
39
  "node": ">=18"
40
40
  },
41
41
  "dependencies": {
42
- "@alaarab/ogrid-core": "2.1.14",
42
+ "@alaarab/ogrid-core": "2.1.15",
43
43
  "@tanstack/react-virtual": "^3.0.0"
44
44
  },
45
45
  "peerDependencies": {