add scrollToIndex alignment to lists

This commit is contained in:
jeffvli
2025-11-14 01:26:56 -08:00
parent 3ecf59c32a
commit c5cd71c8c3
3 changed files with 101 additions and 13 deletions
@@ -360,10 +360,24 @@ export const ItemGridList = ({
const controls = useDefaultItemListControls();
const scrollToIndex = useCallback(
(index: number) => {
(
index: number,
options?: { align?: 'bottom' | 'center' | 'top'; behavior?: 'auto' | 'smooth' },
) => {
if (!listRef.current || !tableMeta) return;
const row = Math.floor(index / tableMeta.columnCount);
listRef.current.scrollToItem(row, 'smart');
// Map alignment options to react-window's alignment
let alignment: 'auto' | 'center' | 'end' | 'smart' | 'start' = 'smart';
if (options?.align === 'top') {
alignment = 'start';
} else if (options?.align === 'center') {
alignment = 'center';
} else if (options?.align === 'bottom') {
alignment = 'end';
}
listRef.current.scrollToItem(row, alignment);
},
[tableMeta],
);
@@ -580,8 +594,8 @@ export const ItemGridList = ({
const imperativeHandle: ItemListHandle = useMemo(() => {
return {
internalState,
scrollToIndex: (index: number) => {
scrollToIndex(index);
scrollToIndex: (index: number, options?: { align?: 'bottom' | 'center' | 'top' }) => {
scrollToIndex(index, options);
},
scrollToOffset: (offset: number) => {
scrollToOffset(offset);
@@ -703,14 +703,16 @@ export const ItemTableList = ({
| HTMLDivElement
| undefined;
const behavior = 'instant';
if (mainContainer) {
mainContainer.scrollTo({ behavior: 'instant', top: offset });
mainContainer.scrollTo({ behavior, top: offset });
}
if (pinnedLeftContainer) {
pinnedLeftContainer.scrollTo({ behavior: 'instant', top: offset });
pinnedLeftContainer.scrollTo({ behavior, top: offset });
}
if (pinnedRightContainer) {
pinnedRightContainer.scrollTo({ behavior: 'instant', top: offset });
pinnedRightContainer.scrollTo({ behavior, top: offset });
}
}, []);
@@ -781,11 +783,80 @@ export const ItemTableList = ({
);
const scrollToTableIndex = useCallback(
(index: number) => {
const offset = calculateScrollTopForIndex(index);
(index: number, options?: { align?: 'bottom' | 'center' | 'top' }) => {
const mainContainer = rowRef.current?.childNodes[0] as HTMLDivElement | undefined;
if (!mainContainer) return;
const viewportHeight = mainContainer.clientHeight;
const align = options?.align || 'top';
// Calculate the base scroll offset (top of the row)
let offset = calculateScrollTopForIndex(index);
// Calculate row height for the target index
const adjustedIndex = enableHeader ? Math.max(0, index - 1) : index;
const mockCellProps: TableItemProps = {
cellPadding,
columns: parsedColumns,
controls: {} as ItemControls,
data: enableHeader ? [null, ...data] : data,
enableAlternateRowColors,
enableExpansion,
enableHeader,
enableHorizontalBorders,
enableRowHoverHighlight,
enableSelection,
enableVerticalBorders,
getRowHeight: () => DEFAULT_ROW_HEIGHT,
internalState: {} as ItemListStateActions,
itemType,
playerContext,
size,
tableId,
};
let targetRowHeight: number;
if (typeof rowHeight === 'number') {
targetRowHeight = rowHeight;
} else if (typeof rowHeight === 'function') {
targetRowHeight = rowHeight(adjustedIndex, mockCellProps);
} else {
targetRowHeight = DEFAULT_ROW_HEIGHT;
}
// Adjust offset based on alignment
if (align === 'center') {
offset = offset - viewportHeight / 2 + targetRowHeight / 2;
} else if (align === 'bottom') {
offset = offset - viewportHeight + targetRowHeight;
}
// 'top' uses the base offset
// Ensure offset is not negative
offset = Math.max(0, offset);
scrollToTableOffset(offset);
},
[calculateScrollTopForIndex, scrollToTableOffset],
[
calculateScrollTopForIndex,
scrollToTableOffset,
enableHeader,
cellPadding,
parsedColumns,
data,
enableAlternateRowColors,
enableExpansion,
enableHorizontalBorders,
enableRowHoverHighlight,
enableSelection,
enableVerticalBorders,
itemType,
playerContext,
size,
tableId,
DEFAULT_ROW_HEIGHT,
rowHeight,
],
);
const [initialize] = useOverlayScrollbars({
@@ -1280,8 +1351,8 @@ export const ItemTableList = ({
const imperativeHandle: ItemListHandle = useMemo(() => {
return {
internalState,
scrollToIndex: (index: number) => {
scrollToTableIndex(enableHeader ? index + 1 : index);
scrollToIndex: (index: number, options?: { align?: 'bottom' | 'center' | 'top' }) => {
scrollToTableIndex(enableHeader ? index + 1 : index, options);
},
scrollToOffset: (offset: number) => {
scrollToTableOffset(offset);
+4 -1
View File
@@ -54,7 +54,10 @@ export interface ItemListGridComponentProps<TQuery> extends ItemListComponentPro
export interface ItemListHandle {
internalState: ItemListStateActions;
scrollToIndex: (index: number, options?: { behavior?: 'auto' | 'smooth' }) => void;
scrollToIndex: (
index: number,
options?: { align?: 'top' | 'bottom' | 'center'; behavior?: 'auto' | 'smooth' },
) => void;
scrollToOffset: (offset: number, options?: { behavior?: 'auto' | 'smooth' }) => void;
}