@alaarab/ogrid-react 2.1.5 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/esm/index.js +78 -38
  2. package/package.json +2 -2
package/dist/esm/index.js CHANGED
@@ -1312,6 +1312,13 @@ function validateRowIds(items, getRowId) {
1312
1312
  }
1313
1313
  var DEFAULT_DEBOUNCE_MS = 300;
1314
1314
  var PEOPLE_SEARCH_DEBOUNCE_MS = DEFAULT_DEBOUNCE_MS;
1315
+ function useLatestRef(value) {
1316
+ const ref = useRef(value);
1317
+ ref.current = value;
1318
+ return ref;
1319
+ }
1320
+
1321
+ // src/hooks/useFilterOptions.ts
1315
1322
  function fieldsEqual(a, b) {
1316
1323
  if (a.length !== b.length) return false;
1317
1324
  for (let i = 0; i < a.length; i++) {
@@ -1319,19 +1326,23 @@ function fieldsEqual(a, b) {
1319
1326
  }
1320
1327
  return true;
1321
1328
  }
1329
+ var EMPTY_FILTER_OPTIONS = {};
1330
+ var EMPTY_LOADING = {};
1322
1331
  function useFilterOptions(dataSource, fields) {
1323
1332
  const fieldsRef = useRef(fields);
1324
1333
  if (!fieldsEqual(fieldsRef.current, fields)) {
1325
1334
  fieldsRef.current = fields;
1326
1335
  }
1327
1336
  const stableFields = fieldsRef.current;
1328
- const [filterOptions, setFilterOptions] = useState({});
1329
- const [loadingOptions, setLoadingOptions] = useState({});
1337
+ const dataSourceRef = useLatestRef(dataSource);
1338
+ const [filterOptions, setFilterOptions] = useState(EMPTY_FILTER_OPTIONS);
1339
+ const [loadingOptions, setLoadingOptions] = useState(EMPTY_LOADING);
1330
1340
  const load = useCallback(async () => {
1331
- const fetcher = "fetchFilterOptions" in dataSource && typeof dataSource.fetchFilterOptions === "function" ? dataSource.fetchFilterOptions.bind(dataSource) : void 0;
1341
+ const ds = dataSourceRef.current;
1342
+ const fetcher = "fetchFilterOptions" in ds && typeof ds.fetchFilterOptions === "function" ? ds.fetchFilterOptions.bind(ds) : void 0;
1332
1343
  if (!fetcher) {
1333
- setFilterOptions({});
1334
- setLoadingOptions({});
1344
+ setFilterOptions(EMPTY_FILTER_OPTIONS);
1345
+ setLoadingOptions(EMPTY_LOADING);
1335
1346
  return;
1336
1347
  }
1337
1348
  const loading = {};
@@ -1350,8 +1361,8 @@ function useFilterOptions(dataSource, fields) {
1350
1361
  })
1351
1362
  );
1352
1363
  setFilterOptions(results);
1353
- setLoadingOptions({});
1354
- }, [dataSource, stableFields]);
1364
+ setLoadingOptions(EMPTY_LOADING);
1365
+ }, [stableFields]);
1355
1366
  useEffect(() => {
1356
1367
  load().catch(() => {
1357
1368
  });
@@ -1535,14 +1546,17 @@ function useOGridDataFetching(params) {
1535
1546
  const [serverLoading, setServerLoading] = useState(true);
1536
1547
  const fetchIdRef = useRef(0);
1537
1548
  const [refreshCounter, setRefreshCounter] = useState(0);
1549
+ const dataSourceRef = useLatestRef(dataSource);
1550
+ const onErrorRef = useLatestRef(onError);
1538
1551
  useEffect(() => {
1539
- if (!isServerSide || !dataSource) {
1552
+ if (!isServerSide || !dataSourceRef.current) {
1540
1553
  if (!isServerSide) setServerLoading(false);
1541
1554
  return;
1542
1555
  }
1556
+ const ds = dataSourceRef.current;
1543
1557
  const id = ++fetchIdRef.current;
1544
1558
  setServerLoading(true);
1545
- dataSource.fetchPage({
1559
+ ds.fetchPage({
1546
1560
  page,
1547
1561
  pageSize,
1548
1562
  sort: { field: sort.field, direction: sort.direction },
@@ -1553,22 +1567,23 @@ function useOGridDataFetching(params) {
1553
1567
  setServerTotalCount(res.totalCount);
1554
1568
  }).catch((err) => {
1555
1569
  if (id !== fetchIdRef.current) return;
1556
- onError?.(err);
1570
+ onErrorRef.current?.(err);
1557
1571
  setServerItems([]);
1558
1572
  setServerTotalCount(0);
1559
1573
  }).finally(() => {
1560
1574
  if (id === fetchIdRef.current) setServerLoading(false);
1561
1575
  });
1562
- }, [isServerSide, dataSource, page, pageSize, sort.field, sort.direction, stableFilters, onError, refreshCounter]);
1576
+ }, [isServerSide, page, pageSize, sort.field, sort.direction, stableFilters, refreshCounter]);
1563
1577
  const displayItems = isClientSide && clientItemsAndTotal ? clientItemsAndTotal.items : serverItems;
1564
1578
  const displayTotalCount = isClientSide && clientItemsAndTotal ? clientItemsAndTotal.totalCount : serverTotalCount;
1579
+ const onFirstDataRenderedRef = useLatestRef(onFirstDataRendered);
1565
1580
  const firstDataRenderedRef = useRef(false);
1566
1581
  useEffect(() => {
1567
1582
  if (!firstDataRenderedRef.current && displayItems.length > 0) {
1568
1583
  firstDataRenderedRef.current = true;
1569
- onFirstDataRendered?.();
1584
+ onFirstDataRenderedRef.current?.();
1570
1585
  }
1571
- }, [displayItems.length, onFirstDataRendered]);
1586
+ }, [displayItems.length]);
1572
1587
  return {
1573
1588
  displayItems,
1574
1589
  displayTotalCount,
@@ -1576,11 +1591,6 @@ function useOGridDataFetching(params) {
1576
1591
  refreshData: () => setRefreshCounter((prev) => prev + 1)
1577
1592
  };
1578
1593
  }
1579
- function useLatestRef(value) {
1580
- const ref = useRef(value);
1581
- ref.current = value;
1582
- return ref;
1583
- }
1584
1594
  var DEFAULT_PANELS = ["columns", "filters"];
1585
1595
  function useSideBarState(params) {
1586
1596
  const { config } = params;
@@ -1621,7 +1631,7 @@ var EMPTY_LOADING_OPTIONS2 = {};
1621
1631
  function useOGrid(props, ref) {
1622
1632
  const {
1623
1633
  columns: columnsProp,
1624
- getRowId,
1634
+ getRowId: getRowIdProp,
1625
1635
  data,
1626
1636
  dataSource,
1627
1637
  page: controlledPage,
@@ -1636,7 +1646,7 @@ function useOGrid(props, ref) {
1636
1646
  onFiltersChange,
1637
1647
  onVisibleColumnsChange,
1638
1648
  columnOrder,
1639
- onColumnOrderChange,
1649
+ onColumnOrderChange: onColumnOrderChangeProp,
1640
1650
  onColumnResized,
1641
1651
  onColumnPinned,
1642
1652
  defaultPageSize = DEFAULT_PAGE_SIZE,
@@ -1651,9 +1661,9 @@ function useOGrid(props, ref) {
1651
1661
  suppressHorizontalScroll,
1652
1662
  editable,
1653
1663
  cellSelection,
1654
- onCellValueChanged,
1655
- onUndo,
1656
- onRedo,
1664
+ onCellValueChanged: onCellValueChangedProp,
1665
+ onUndo: onUndoProp,
1666
+ onRedo: onRedoProp,
1657
1667
  canUndo,
1658
1668
  canRedo,
1659
1669
  rowSelection = "none",
@@ -1675,6 +1685,32 @@ function useOGrid(props, ref) {
1675
1685
  "aria-label": ariaLabel,
1676
1686
  "aria-labelledby": ariaLabelledBy
1677
1687
  } = props;
1688
+ const getRowIdStableRef = useLatestRef(getRowIdProp);
1689
+ const getRowId = useCallback((item) => getRowIdStableRef.current(item), [getRowIdStableRef]);
1690
+ const onColumnOrderChangeRef = useLatestRef(onColumnOrderChangeProp);
1691
+ const onColumnOrderChange = useMemo(
1692
+ () => onColumnOrderChangeProp ? (order) => onColumnOrderChangeRef.current?.(order) : void 0,
1693
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1694
+ [!!onColumnOrderChangeProp]
1695
+ );
1696
+ const onCellValueChangedRef = useLatestRef(onCellValueChangedProp);
1697
+ const onCellValueChanged = useMemo(
1698
+ () => onCellValueChangedProp ? (event) => onCellValueChangedRef.current?.(event) : void 0,
1699
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1700
+ [!!onCellValueChangedProp]
1701
+ );
1702
+ const onUndoRef = useLatestRef(onUndoProp);
1703
+ const onUndo = useMemo(
1704
+ () => onUndoProp ? () => onUndoRef.current?.() : void 0,
1705
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1706
+ [!!onUndoProp]
1707
+ );
1708
+ const onRedoRef = useLatestRef(onRedoProp);
1709
+ const onRedo = useMemo(
1710
+ () => onRedoProp ? () => onRedoRef.current?.() : void 0,
1711
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1712
+ [!!onRedoProp]
1713
+ );
1678
1714
  const columnChooserPlacement = columnChooserProp === false ? "none" : columnChooserProp === "sidebar" ? "sidebar" : "toolbar";
1679
1715
  const columns = useMemo(() => flattenColumns(columnsProp), [columnsProp]);
1680
1716
  const isServerSide = dataSource != null;
@@ -2855,6 +2891,7 @@ function useKeyboardNavigation(params) {
2855
2891
  }
2856
2892
  function useUndoRedo(params) {
2857
2893
  const { onCellValueChanged, maxUndoDepth = 100 } = params;
2894
+ const onCellValueChangedRef = useLatestRef(onCellValueChanged);
2858
2895
  const stackRef = useRef(null);
2859
2896
  if (stackRef.current === null) {
2860
2897
  stackRef.current = new UndoRedoStack(maxUndoDepth);
@@ -2868,16 +2905,17 @@ function useUndoRedo(params) {
2868
2905
  }, []);
2869
2906
  const wrapped = useCallback(
2870
2907
  (event) => {
2871
- if (!onCellValueChanged) return;
2908
+ if (!onCellValueChangedRef.current) return;
2872
2909
  const stack = getStack();
2873
2910
  stack.record(event);
2874
2911
  if (!stack.isBatching) {
2875
2912
  setHistoryLength(stack.historyLength);
2876
2913
  setRedoLength(stack.redoLength);
2877
2914
  }
2878
- onCellValueChanged(event);
2915
+ onCellValueChangedRef.current(event);
2879
2916
  },
2880
- [onCellValueChanged, getStack]
2917
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2918
+ [getStack]
2881
2919
  );
2882
2920
  const beginBatch = useCallback(() => {
2883
2921
  getStack().beginBatch();
@@ -2889,7 +2927,7 @@ function useUndoRedo(params) {
2889
2927
  setRedoLength(stack.redoLength);
2890
2928
  }, [getStack]);
2891
2929
  const undo = useCallback(() => {
2892
- if (!onCellValueChanged) return;
2930
+ if (!onCellValueChangedRef.current) return;
2893
2931
  const stack = getStack();
2894
2932
  const lastBatch = stack.undo();
2895
2933
  if (!lastBatch) return;
@@ -2897,24 +2935,24 @@ function useUndoRedo(params) {
2897
2935
  setRedoLength(stack.redoLength);
2898
2936
  for (let i = lastBatch.length - 1; i >= 0; i--) {
2899
2937
  const ev = lastBatch[i];
2900
- onCellValueChanged({
2938
+ onCellValueChangedRef.current({
2901
2939
  ...ev,
2902
2940
  oldValue: ev.newValue,
2903
2941
  newValue: ev.oldValue
2904
2942
  });
2905
2943
  }
2906
- }, [onCellValueChanged, getStack]);
2944
+ }, [getStack]);
2907
2945
  const redo = useCallback(() => {
2908
- if (!onCellValueChanged) return;
2946
+ if (!onCellValueChangedRef.current) return;
2909
2947
  const stack = getStack();
2910
2948
  const nextBatch = stack.redo();
2911
2949
  if (!nextBatch) return;
2912
2950
  setHistoryLength(stack.historyLength);
2913
2951
  setRedoLength(stack.redoLength);
2914
2952
  for (const ev of nextBatch) {
2915
- onCellValueChanged(ev);
2953
+ onCellValueChangedRef.current(ev);
2916
2954
  }
2917
- }, [onCellValueChanged, getStack]);
2955
+ }, [getStack]);
2918
2956
  return {
2919
2957
  onCellValueChanged: onCellValueChanged ? wrapped : void 0,
2920
2958
  undo,
@@ -2942,7 +2980,7 @@ function useFillHandle(params) {
2942
2980
  items,
2943
2981
  visibleCols,
2944
2982
  editable,
2945
- onCellValueChanged,
2983
+ onCellValueChanged: onCellValueChangedProp,
2946
2984
  selectionRange,
2947
2985
  setSelectionRange,
2948
2986
  setActiveCell,
@@ -2951,13 +2989,14 @@ function useFillHandle(params) {
2951
2989
  beginBatch,
2952
2990
  endBatch
2953
2991
  } = params;
2992
+ const onCellValueChangedRef = useLatestRef(onCellValueChangedProp);
2954
2993
  const [fillDrag, setFillDrag] = useState(null);
2955
2994
  const fillDragEndRef = useRef({ endRow: 0, endCol: 0 });
2956
2995
  const rafRef = useRef(0);
2957
2996
  const liveFillRangeRef = useRef(null);
2958
2997
  const colOffsetRef = useLatestRef(colOffset);
2959
2998
  useEffect(() => {
2960
- if (!fillDrag || editable === false || !onCellValueChanged || !wrapperRef.current) return;
2999
+ if (!fillDrag || editable === false || !onCellValueChangedRef.current || !wrapperRef.current) return;
2961
3000
  fillDragEndRef.current = { endRow: fillDrag.startRow, endCol: fillDrag.startCol };
2962
3001
  liveFillRangeRef.current = null;
2963
3002
  const markedCells = /* @__PURE__ */ new Set();
@@ -3071,7 +3110,7 @@ function useFillHandle(params) {
3071
3110
  const fillEvents = applyFillValues(norm, fillDrag.startRow, fillDrag.startCol, items, visibleCols);
3072
3111
  if (fillEvents.length > 0) {
3073
3112
  beginBatch?.();
3074
- for (const evt of fillEvents) onCellValueChanged(evt);
3113
+ for (const evt of fillEvents) onCellValueChangedRef.current?.(evt);
3075
3114
  endBatch?.();
3076
3115
  }
3077
3116
  setFillDrag(null);
@@ -3091,7 +3130,6 @@ function useFillHandle(params) {
3091
3130
  visibleCols,
3092
3131
  setSelectionRange,
3093
3132
  setActiveCell,
3094
- onCellValueChanged,
3095
3133
  beginBatch,
3096
3134
  endBatch,
3097
3135
  colOffsetRef,
@@ -3572,6 +3610,7 @@ function useDataGridEditing(params) {
3572
3610
  const [popoverAnchorEl, setPopoverAnchorEl] = useState(null);
3573
3611
  const visibleColsRef = useLatestRef(params.visibleCols);
3574
3612
  const itemsLengthRef = useLatestRef(params.itemsLength);
3613
+ const onCellValueChangedRef = useLatestRef(onCellValueChanged);
3575
3614
  const commitCellEdit = useCallback(
3576
3615
  (item, columnId, oldValue, newValue, rowIndex, globalColIndex) => {
3577
3616
  const col = visibleColsRef.current.find((c) => c.columnId === columnId);
@@ -3585,7 +3624,7 @@ function useDataGridEditing(params) {
3585
3624
  }
3586
3625
  newValue = result.value;
3587
3626
  }
3588
- onCellValueChanged?.({
3627
+ onCellValueChangedRef.current?.({
3589
3628
  item,
3590
3629
  columnId,
3591
3630
  oldValue,
@@ -3599,7 +3638,8 @@ function useDataGridEditing(params) {
3599
3638
  setActiveCell({ rowIndex: rowIndex + 1, columnIndex: globalColIndex });
3600
3639
  }
3601
3640
  },
3602
- [onCellValueChanged, setEditingCell, setPendingEditorValue, setActiveCell, visibleColsRef, itemsLengthRef]
3641
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3642
+ [setEditingCell, setPendingEditorValue, setActiveCell, visibleColsRef, itemsLengthRef]
3603
3643
  );
3604
3644
  const cancelPopoverEdit = useCallback(() => {
3605
3645
  setEditingCell(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-react",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
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.5",
42
+ "@alaarab/ogrid-core": "2.1.7",
43
43
  "@tanstack/react-virtual": "^3.0.0"
44
44
  },
45
45
  "peerDependencies": {