@alaarab/ogrid-react 2.0.2 → 2.0.3
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/hooks/index.js +2 -0
- package/dist/esm/hooks/useColumnReorder.js +136 -0
- package/dist/esm/hooks/useOGrid.js +13 -2
- package/dist/esm/hooks/useVirtualScroll.js +69 -0
- package/dist/esm/index.js +1 -1
- package/dist/types/hooks/index.d.ts +4 -0
- package/dist/types/hooks/useColumnReorder.d.ts +22 -0
- package/dist/types/hooks/useVirtualScroll.d.ts +33 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/types/dataGridTypes.d.ts +10 -2
- package/dist/types/types/index.d.ts +1 -1
- package/package.json +3 -2
package/dist/esm/hooks/index.js
CHANGED
|
@@ -22,4 +22,6 @@ export { useColumnResize } from './useColumnResize';
|
|
|
22
22
|
export { useRichSelectState } from './useRichSelectState';
|
|
23
23
|
export { useSideBarState } from './useSideBarState';
|
|
24
24
|
export { useTableLayout } from './useTableLayout';
|
|
25
|
+
export { useColumnReorder } from './useColumnReorder';
|
|
26
|
+
export { useVirtualScroll } from './useVirtualScroll';
|
|
25
27
|
export { useLatestRef } from './useLatestRef';
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
|
+
import { calculateDropTarget, reorderColumnArray, getPinStateForColumn, } from '@alaarab/ogrid-core';
|
|
3
|
+
/** Width of the resize handle zone on the right edge of each header cell. */
|
|
4
|
+
const RESIZE_HANDLE_ZONE = 8;
|
|
5
|
+
/**
|
|
6
|
+
* Convert Record<string, 'left' | 'right'> to the { left?, right? } shape core expects.
|
|
7
|
+
*/
|
|
8
|
+
function toPinnedColumnsShape(pinned) {
|
|
9
|
+
if (!pinned)
|
|
10
|
+
return undefined;
|
|
11
|
+
const left = [];
|
|
12
|
+
const right = [];
|
|
13
|
+
for (const [id, side] of Object.entries(pinned)) {
|
|
14
|
+
if (side === 'left')
|
|
15
|
+
left.push(id);
|
|
16
|
+
else if (side === 'right')
|
|
17
|
+
right.push(id);
|
|
18
|
+
}
|
|
19
|
+
if (left.length === 0 && right.length === 0)
|
|
20
|
+
return undefined;
|
|
21
|
+
return {
|
|
22
|
+
...(left.length > 0 ? { left } : {}),
|
|
23
|
+
...(right.length > 0 ? { right } : {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Manages column reorder drag interactions with RAF-throttled updates.
|
|
28
|
+
* @param params - Columns, order, change callback, enabled flag, and wrapper ref.
|
|
29
|
+
* @returns Drag state and mousedown handler for header cells.
|
|
30
|
+
*/
|
|
31
|
+
export function useColumnReorder(params) {
|
|
32
|
+
const { columns, columnOrder, onColumnOrderChange, enabled = true, pinnedColumns, wrapperRef, } = params;
|
|
33
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
34
|
+
const [dropIndicatorX, setDropIndicatorX] = useState(null);
|
|
35
|
+
const rafRef = useRef(0);
|
|
36
|
+
// Refs for latest values so the window listeners capture current state
|
|
37
|
+
const columnsRef = useRef(columns);
|
|
38
|
+
columnsRef.current = columns;
|
|
39
|
+
const columnOrderRef = useRef(columnOrder);
|
|
40
|
+
columnOrderRef.current = columnOrder;
|
|
41
|
+
const onColumnOrderChangeRef = useRef(onColumnOrderChange);
|
|
42
|
+
onColumnOrderChangeRef.current = onColumnOrderChange;
|
|
43
|
+
const pinnedColumnsRef = useRef(pinnedColumns);
|
|
44
|
+
pinnedColumnsRef.current = pinnedColumns;
|
|
45
|
+
// Track active drag state for cleanup on unmount
|
|
46
|
+
const cleanupRef = useRef(null);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
return () => {
|
|
49
|
+
if (cleanupRef.current) {
|
|
50
|
+
cleanupRef.current();
|
|
51
|
+
cleanupRef.current = null;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}, []);
|
|
55
|
+
const handleHeaderMouseDown = useCallback((columnId, event) => {
|
|
56
|
+
if (!enabled)
|
|
57
|
+
return;
|
|
58
|
+
if (!onColumnOrderChangeRef.current)
|
|
59
|
+
return;
|
|
60
|
+
// Gate on left-click only
|
|
61
|
+
if (event.button !== 0)
|
|
62
|
+
return;
|
|
63
|
+
// Skip if in resize handle zone (right 8px of the header cell)
|
|
64
|
+
const target = event.currentTarget;
|
|
65
|
+
const rect = target.getBoundingClientRect();
|
|
66
|
+
if (event.clientX > rect.right - RESIZE_HANDLE_ZONE)
|
|
67
|
+
return;
|
|
68
|
+
// Skip column groups — only reorder leaf columns
|
|
69
|
+
const cols = columnsRef.current;
|
|
70
|
+
const colIndex = cols.findIndex((c) => c.columnId === columnId);
|
|
71
|
+
if (colIndex === -1)
|
|
72
|
+
return;
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
const startX = event.clientX;
|
|
75
|
+
let hasMoved = false;
|
|
76
|
+
let latestDropTargetIndex = null;
|
|
77
|
+
// Determine pin state of the dragged column
|
|
78
|
+
const pinnedShape = toPinnedColumnsShape(pinnedColumnsRef.current);
|
|
79
|
+
const draggedPinState = getPinStateForColumn(columnId, pinnedShape);
|
|
80
|
+
// Lock text selection and set grabbing cursor during drag
|
|
81
|
+
const prevUserSelect = document.body.style.userSelect;
|
|
82
|
+
const prevCursor = document.body.style.cursor;
|
|
83
|
+
document.body.style.userSelect = 'none';
|
|
84
|
+
document.body.style.cursor = 'grabbing';
|
|
85
|
+
const onMove = (moveEvent) => {
|
|
86
|
+
// Require a small minimum drag distance before activating
|
|
87
|
+
if (!hasMoved && Math.abs(moveEvent.clientX - startX) < 5)
|
|
88
|
+
return;
|
|
89
|
+
if (!hasMoved) {
|
|
90
|
+
hasMoved = true;
|
|
91
|
+
setIsDragging(true);
|
|
92
|
+
}
|
|
93
|
+
if (rafRef.current)
|
|
94
|
+
cancelAnimationFrame(rafRef.current);
|
|
95
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
96
|
+
rafRef.current = 0;
|
|
97
|
+
const wrapper = wrapperRef.current;
|
|
98
|
+
if (!wrapper)
|
|
99
|
+
return;
|
|
100
|
+
const currentOrder = columnOrderRef.current ?? columnsRef.current.map((c) => c.columnId);
|
|
101
|
+
const result = calculateDropTarget(moveEvent.clientX, currentOrder, columnId, draggedPinState, wrapper, pinnedShape);
|
|
102
|
+
if (result) {
|
|
103
|
+
latestDropTargetIndex = result.targetIndex;
|
|
104
|
+
setDropIndicatorX(result.indicatorX);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
const cleanup = () => {
|
|
109
|
+
window.removeEventListener('mousemove', onMove, true);
|
|
110
|
+
window.removeEventListener('mouseup', onUp, true);
|
|
111
|
+
cleanupRef.current = null;
|
|
112
|
+
// Restore user-select and cursor
|
|
113
|
+
document.body.style.userSelect = prevUserSelect;
|
|
114
|
+
document.body.style.cursor = prevCursor;
|
|
115
|
+
// Cancel pending RAF
|
|
116
|
+
if (rafRef.current) {
|
|
117
|
+
cancelAnimationFrame(rafRef.current);
|
|
118
|
+
rafRef.current = 0;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const onUp = () => {
|
|
122
|
+
cleanup();
|
|
123
|
+
if (hasMoved && latestDropTargetIndex != null) {
|
|
124
|
+
const currentOrder = columnOrderRef.current ?? columnsRef.current.map((c) => c.columnId);
|
|
125
|
+
const newOrder = reorderColumnArray(currentOrder, columnId, latestDropTargetIndex);
|
|
126
|
+
onColumnOrderChangeRef.current?.(newOrder);
|
|
127
|
+
}
|
|
128
|
+
setIsDragging(false);
|
|
129
|
+
setDropIndicatorX(null);
|
|
130
|
+
};
|
|
131
|
+
window.addEventListener('mousemove', onMove, true);
|
|
132
|
+
window.addEventListener('mouseup', onUp, true);
|
|
133
|
+
cleanupRef.current = cleanup;
|
|
134
|
+
}, [enabled, wrapperRef]);
|
|
135
|
+
return { isDragging, dropIndicatorX, handleHeaderMouseDown };
|
|
136
|
+
}
|
|
@@ -11,7 +11,7 @@ const EMPTY_LOADING_OPTIONS = {};
|
|
|
11
11
|
* @returns Grouped props for DataGridTable, pagination controls, column chooser, layout, and filters.
|
|
12
12
|
*/
|
|
13
13
|
export function useOGrid(props, ref) {
|
|
14
|
-
const { columns: columnsProp, getRowId, data, dataSource, page: controlledPage, pageSize: controlledPageSize, sort: controlledSort, filters: controlledFilters, visibleColumns: controlledVisibleColumns, isLoading: controlledLoading, onPageChange, onPageSizeChange, onSortChange, onFiltersChange, onVisibleColumnsChange, columnOrder, onColumnOrderChange, onColumnResized, onColumnPinned, freezeRows, freezeCols, defaultPageSize = DEFAULT_PAGE_SIZE, defaultSortBy, defaultSortDirection = 'asc', toolbar, toolbarBelow, emptyState, entityLabelPlural = 'items', className, layoutMode = 'fill', suppressHorizontalScroll, editable, cellSelection, onCellValueChanged, onUndo, onRedo, canUndo, canRedo, rowSelection = 'none', selectedRows, onSelectionChange, statusBar, pageSizeOptions, sideBar, onFirstDataRendered, onError, columnChooser: columnChooserProp, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, } = props;
|
|
14
|
+
const { columns: columnsProp, getRowId, data, dataSource, page: controlledPage, pageSize: controlledPageSize, sort: controlledSort, filters: controlledFilters, visibleColumns: controlledVisibleColumns, isLoading: controlledLoading, onPageChange, onPageSizeChange, onSortChange, onFiltersChange, onVisibleColumnsChange, columnOrder, onColumnOrderChange, onColumnResized, onColumnPinned, freezeRows, freezeCols, defaultPageSize = DEFAULT_PAGE_SIZE, defaultSortBy, defaultSortDirection = 'asc', toolbar, toolbarBelow, emptyState, entityLabelPlural = 'items', className, layoutMode = 'fill', suppressHorizontalScroll, editable, cellSelection, onCellValueChanged, onUndo, onRedo, canUndo, canRedo, rowSelection = 'none', selectedRows, onSelectionChange, statusBar, pageSizeOptions, sideBar, onFirstDataRendered, onError, columnChooser: columnChooserProp, columnReorder, virtualScroll, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, } = props;
|
|
15
15
|
// Resolve column chooser placement
|
|
16
16
|
const columnChooserPlacement = columnChooserProp === false ? 'none'
|
|
17
17
|
: columnChooserProp === 'sidebar' ? 'sidebar'
|
|
@@ -265,6 +265,14 @@ export function useOGrid(props, ref) {
|
|
|
265
265
|
setRefreshCounter(refreshCounterRef.current);
|
|
266
266
|
}
|
|
267
267
|
},
|
|
268
|
+
getColumnOrder: () => columnOrder ?? columns.map((c) => c.columnId),
|
|
269
|
+
setColumnOrder: (order) => {
|
|
270
|
+
onColumnOrderChange?.(order);
|
|
271
|
+
},
|
|
272
|
+
scrollToRow: () => {
|
|
273
|
+
// No-op at orchestration level — DataGridTable components implement
|
|
274
|
+
// this via useVirtualScroll.scrollToIndex when virtual scrolling is active.
|
|
275
|
+
},
|
|
268
276
|
}), [
|
|
269
277
|
visibleColumns,
|
|
270
278
|
sort,
|
|
@@ -411,6 +419,8 @@ export function useOGrid(props, ref) {
|
|
|
411
419
|
getUserByEmail: dataSource?.getUserByEmail,
|
|
412
420
|
layoutMode,
|
|
413
421
|
suppressHorizontalScroll,
|
|
422
|
+
columnReorder,
|
|
423
|
+
virtualScroll,
|
|
414
424
|
'aria-label': ariaLabel,
|
|
415
425
|
'aria-labelledby': ariaLabelledBy,
|
|
416
426
|
emptyState: {
|
|
@@ -426,7 +436,8 @@ export function useOGrid(props, ref) {
|
|
|
426
436
|
editable, cellSelection, onCellValueChanged, onUndo, onRedo, canUndo, canRedo,
|
|
427
437
|
rowSelection, effectiveSelectedRows, handleSelectionChange, statusBarConfig,
|
|
428
438
|
isLoadingResolved, filters, handleFilterChange, clientFilterOptions, dataSource,
|
|
429
|
-
loadingFilterOptions, layoutMode, suppressHorizontalScroll,
|
|
439
|
+
loadingFilterOptions, layoutMode, suppressHorizontalScroll, columnReorder, virtualScroll,
|
|
440
|
+
ariaLabel, ariaLabelledBy,
|
|
430
441
|
hasActiveFilters, clearAllFilters, emptyState,
|
|
431
442
|
]);
|
|
432
443
|
const pagination = useMemo(() => ({
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useMemo, useCallback, useRef } from 'react';
|
|
2
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
3
|
+
/** Threshold below which virtual scrolling is a no-op (all rows rendered). */
|
|
4
|
+
const PASSTHROUGH_THRESHOLD = 100;
|
|
5
|
+
/**
|
|
6
|
+
* Wraps TanStack Virtual for row virtualization.
|
|
7
|
+
* When disabled or when totalRows < threshold, returns a pass-through (all rows visible).
|
|
8
|
+
* @param params - Total rows, row height, enabled flag, overscan, and container ref.
|
|
9
|
+
* @returns Virtualizer instance, total height, visible range, and scrollToIndex helper.
|
|
10
|
+
*/
|
|
11
|
+
export function useVirtualScroll(params) {
|
|
12
|
+
const { totalRows, rowHeight, enabled, overscan = 5, containerRef, } = params;
|
|
13
|
+
const isActive = enabled && totalRows >= PASSTHROUGH_THRESHOLD;
|
|
14
|
+
const getScrollElement = useCallback(() => containerRef.current, [containerRef]);
|
|
15
|
+
const virtualizer = useVirtualizer({
|
|
16
|
+
count: isActive ? totalRows : 0,
|
|
17
|
+
getScrollElement,
|
|
18
|
+
estimateSize: () => rowHeight,
|
|
19
|
+
overscan,
|
|
20
|
+
enabled: isActive,
|
|
21
|
+
});
|
|
22
|
+
const passthroughRange = useMemo(() => ({
|
|
23
|
+
startIndex: 0,
|
|
24
|
+
endIndex: Math.max(0, totalRows - 1),
|
|
25
|
+
offsetTop: 0,
|
|
26
|
+
offsetBottom: 0,
|
|
27
|
+
}), [totalRows]);
|
|
28
|
+
const activeRange = useMemo(() => {
|
|
29
|
+
if (!isActive)
|
|
30
|
+
return passthroughRange;
|
|
31
|
+
const virtualItems = virtualizer.getVirtualItems();
|
|
32
|
+
if (virtualItems.length === 0) {
|
|
33
|
+
return { startIndex: 0, endIndex: -1, offsetTop: 0, offsetBottom: 0 };
|
|
34
|
+
}
|
|
35
|
+
const first = virtualItems[0];
|
|
36
|
+
const last = virtualItems[virtualItems.length - 1];
|
|
37
|
+
const totalSize = virtualizer.getTotalSize();
|
|
38
|
+
return {
|
|
39
|
+
startIndex: first.index,
|
|
40
|
+
endIndex: last.index,
|
|
41
|
+
offsetTop: first.start,
|
|
42
|
+
offsetBottom: Math.max(0, totalSize - last.end),
|
|
43
|
+
};
|
|
44
|
+
}, [isActive, virtualizer, passthroughRange]);
|
|
45
|
+
const totalHeight = isActive
|
|
46
|
+
? virtualizer.getTotalSize()
|
|
47
|
+
: totalRows * rowHeight;
|
|
48
|
+
const scrollToIndexRef = useRef(virtualizer);
|
|
49
|
+
scrollToIndexRef.current = virtualizer;
|
|
50
|
+
const scrollToIndex = useCallback((index) => {
|
|
51
|
+
if (isActive) {
|
|
52
|
+
scrollToIndexRef.current?.scrollToIndex(index, { align: 'auto' });
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// When not virtualized, scroll the container directly
|
|
56
|
+
const container = containerRef.current;
|
|
57
|
+
if (container) {
|
|
58
|
+
const top = index * rowHeight;
|
|
59
|
+
container.scrollTo({ top, behavior: 'auto' });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}, [isActive, containerRef, rowHeight]);
|
|
63
|
+
return {
|
|
64
|
+
virtualizer: isActive ? virtualizer : null,
|
|
65
|
+
totalHeight,
|
|
66
|
+
visibleRange: activeRange,
|
|
67
|
+
scrollToIndex,
|
|
68
|
+
};
|
|
69
|
+
}
|
package/dist/esm/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export { CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, } from '@alaarab/ogrid-core';
|
|
3
3
|
export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
|
|
4
4
|
// Hooks
|
|
5
|
-
export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, useTableLayout, useLatestRef, } from './hooks';
|
|
5
|
+
export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, useTableLayout, useColumnReorder, useVirtualScroll, useLatestRef, } from './hooks';
|
|
6
6
|
// Components
|
|
7
7
|
export { OGridLayout } from './components/OGridLayout';
|
|
8
8
|
export { StatusBar } from './components/StatusBar';
|
|
@@ -45,4 +45,8 @@ export { useSideBarState } from './useSideBarState';
|
|
|
45
45
|
export type { UseSideBarStateParams, UseSideBarStateResult } from './useSideBarState';
|
|
46
46
|
export { useTableLayout } from './useTableLayout';
|
|
47
47
|
export type { UseTableLayoutParams, UseTableLayoutResult } from './useTableLayout';
|
|
48
|
+
export { useColumnReorder } from './useColumnReorder';
|
|
49
|
+
export type { UseColumnReorderParams, UseColumnReorderResult, } from './useColumnReorder';
|
|
50
|
+
export { useVirtualScroll } from './useVirtualScroll';
|
|
51
|
+
export type { IVirtualScrollConfig, UseVirtualScrollParams, UseVirtualScrollResult, } from './useVirtualScroll';
|
|
48
52
|
export { useLatestRef } from './useLatestRef';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
import type { IColumnDef } from '../types';
|
|
3
|
+
export interface UseColumnReorderParams<T> {
|
|
4
|
+
columns: IColumnDef<T>[];
|
|
5
|
+
columnOrder?: string[];
|
|
6
|
+
onColumnOrderChange?: (order: string[]) => void;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
/** Pinned column configuration for zone constraints. */
|
|
9
|
+
pinnedColumns?: Record<string, 'left' | 'right'>;
|
|
10
|
+
wrapperRef: RefObject<HTMLElement | null>;
|
|
11
|
+
}
|
|
12
|
+
export interface UseColumnReorderResult {
|
|
13
|
+
isDragging: boolean;
|
|
14
|
+
dropIndicatorX: number | null;
|
|
15
|
+
handleHeaderMouseDown: (columnId: string, event: React.MouseEvent) => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Manages column reorder drag interactions with RAF-throttled updates.
|
|
19
|
+
* @param params - Columns, order, change callback, enabled flag, and wrapper ref.
|
|
20
|
+
* @returns Drag state and mousedown handler for header cells.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useColumnReorder<T>(params: UseColumnReorderParams<T>): UseColumnReorderResult;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Virtualizer } from '@tanstack/react-virtual';
|
|
2
|
+
import type { RefObject } from 'react';
|
|
3
|
+
import type { IVisibleRange } from '@alaarab/ogrid-core';
|
|
4
|
+
export type { IVirtualScrollConfig } from '@alaarab/ogrid-core';
|
|
5
|
+
export interface UseVirtualScrollParams {
|
|
6
|
+
/** Total number of rows in the data set. */
|
|
7
|
+
totalRows: number;
|
|
8
|
+
/** Row height in pixels. */
|
|
9
|
+
rowHeight: number;
|
|
10
|
+
/** Whether virtual scrolling is enabled. */
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
/** Number of extra rows to render outside the visible area. Default: 5. */
|
|
13
|
+
overscan?: number;
|
|
14
|
+
/** Ref to the scrollable container element. */
|
|
15
|
+
containerRef: RefObject<HTMLElement | null>;
|
|
16
|
+
}
|
|
17
|
+
export interface UseVirtualScrollResult {
|
|
18
|
+
/** The TanStack virtualizer instance (null when disabled). */
|
|
19
|
+
virtualizer: Virtualizer<HTMLElement, Element> | null;
|
|
20
|
+
/** Total height of all rows in pixels. */
|
|
21
|
+
totalHeight: number;
|
|
22
|
+
/** The range of visible rows with spacer offsets. */
|
|
23
|
+
visibleRange: IVisibleRange;
|
|
24
|
+
/** Scroll to a specific row index. */
|
|
25
|
+
scrollToIndex: (index: number) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Wraps TanStack Virtual for row virtualization.
|
|
29
|
+
* When disabled or when totalRows < threshold, returns a pass-through (all rows visible).
|
|
30
|
+
* @param params - Total rows, row height, enabled flag, overscan, and container ref.
|
|
31
|
+
* @returns Virtualizer instance, total height, visible range, and scrollToIndex helper.
|
|
32
|
+
*/
|
|
33
|
+
export declare function useVirtualScroll(params: UseVirtualScrollParams): UseVirtualScrollResult;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, } from '@alaarab/ogrid-core';
|
|
2
|
-
export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnDef, IColumnGroupDef, IColumnDefinition, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IValueParserParams, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridDataGridProps, RowSelectionMode, RowId, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, HeaderCell, HeaderRow, SideBarPanelId, ISideBarDef, IDateFilterValue, } from './types';
|
|
2
|
+
export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnDef, IColumnGroupDef, IColumnDefinition, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IValueParserParams, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridDataGridProps, RowSelectionMode, RowId, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, HeaderCell, HeaderRow, SideBarPanelId, ISideBarDef, IDateFilterValue, IVirtualScrollConfig, IColumnReorderConfig, } from './types';
|
|
3
3
|
export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
|
|
4
|
-
export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, useTableLayout, useLatestRef, } from './hooks';
|
|
5
|
-
export type { UseFilterOptionsResult, UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, ColumnChooserPlacement, UseActiveCellResult, UseCellEditingResult, EditingCell, UseContextMenuResult, ContextMenuPosition, UseCellSelectionResult, UseCellSelectionParams, UseClipboardResult, UseClipboardParams, UseRowSelectionResult, UseRowSelectionParams, UseKeyboardNavigationResult, UseKeyboardNavigationParams, UseUndoRedoResult, UseUndoRedoParams, UseFillHandleResult, UseFillHandleParams, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseTextFilterStateParams, UseTextFilterStateResult, UseMultiSelectFilterStateParams, UseMultiSelectFilterStateResult, UsePeopleFilterStateParams, UsePeopleFilterStateResult, UseDateFilterStateParams, UseDateFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, InlineCellEditorType, UseColumnResizeParams, UseColumnResizeResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, UseTableLayoutParams, UseTableLayoutResult, } from './hooks';
|
|
4
|
+
export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, useTableLayout, useColumnReorder, useVirtualScroll, useLatestRef, } from './hooks';
|
|
5
|
+
export type { UseFilterOptionsResult, UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, ColumnChooserPlacement, UseActiveCellResult, UseCellEditingResult, EditingCell, UseContextMenuResult, ContextMenuPosition, UseCellSelectionResult, UseCellSelectionParams, UseClipboardResult, UseClipboardParams, UseRowSelectionResult, UseRowSelectionParams, UseKeyboardNavigationResult, UseKeyboardNavigationParams, UseUndoRedoResult, UseUndoRedoParams, UseFillHandleResult, UseFillHandleParams, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseTextFilterStateParams, UseTextFilterStateResult, UseMultiSelectFilterStateParams, UseMultiSelectFilterStateResult, UsePeopleFilterStateParams, UsePeopleFilterStateResult, UseDateFilterStateParams, UseDateFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, InlineCellEditorType, UseColumnResizeParams, UseColumnResizeResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, UseTableLayoutParams, UseTableLayoutResult, UseColumnReorderParams, UseColumnReorderResult, UseVirtualScrollParams, UseVirtualScrollResult, } from './hooks';
|
|
6
6
|
export { OGridLayout } from './components/OGridLayout';
|
|
7
7
|
export type { OGridLayoutProps } from './components/OGridLayout';
|
|
8
8
|
export { StatusBar } from './components/StatusBar';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
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, IColumnReorderConfig, 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>)[];
|
|
@@ -68,6 +68,10 @@ interface IOGridBaseProps<T> {
|
|
|
68
68
|
sideBar?: boolean | ISideBarDef;
|
|
69
69
|
/** Page size options shown in the pagination dropdown. Default: [10, 20, 50, 100]. */
|
|
70
70
|
pageSizeOptions?: number[];
|
|
71
|
+
/** Enable column reordering via drag-and-drop on header cells. Default: false. */
|
|
72
|
+
columnReorder?: boolean;
|
|
73
|
+
/** Virtual scrolling configuration. When provided, only visible rows are rendered for large datasets. */
|
|
74
|
+
virtualScroll?: IVirtualScrollConfig;
|
|
71
75
|
/** Fires once when the grid first renders with data (useful for restoring column state). */
|
|
72
76
|
onFirstDataRendered?: () => void;
|
|
73
77
|
/** Called when server-side fetchPage fails. */
|
|
@@ -145,6 +149,10 @@ export interface IOGridDataGridProps<T> {
|
|
|
145
149
|
message?: ReactNode;
|
|
146
150
|
render?: () => ReactNode;
|
|
147
151
|
};
|
|
152
|
+
/** Enable column reordering via drag-and-drop on header cells. Default: false. */
|
|
153
|
+
columnReorder?: boolean;
|
|
154
|
+
/** Virtual scrolling configuration. When provided, only visible rows are rendered for large datasets. */
|
|
155
|
+
virtualScroll?: IVirtualScrollConfig;
|
|
148
156
|
/** Called when a cell renderer or custom editor throws an error. */
|
|
149
157
|
onCellError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
150
158
|
'aria-label'?: string;
|
|
@@ -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, IColumnReorderConfig, } from './dataGridTypes';
|
|
3
3
|
export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './dataGridTypes';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-react",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
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",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"node": ">=18"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@alaarab/ogrid-core": "2.0.
|
|
38
|
+
"@alaarab/ogrid-core": "2.0.3",
|
|
39
|
+
"@tanstack/react-virtual": "^3.11.0"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
42
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|