Forward Ref and selection/scrollto

This commit is contained in:
Hein
2025-10-24 16:51:55 +02:00
parent ad5bc14d7c
commit d6b7fa4076
8 changed files with 294 additions and 89 deletions

View File

@@ -56,6 +56,7 @@ export type FilterOptionOperator =
| 'startswith';
export interface GridlerProps extends PropsWithChildren {
allowMultiSelect?: boolean;
columns?: GridlerColumns;
defaultSort?: Array<SortOption>;
@@ -88,6 +89,7 @@ export interface GridlerProps extends PropsWithChildren {
) => GridCell;
rowHeight?: number;
scrollToRowKey?: number;
sections?: {
bottom?: React.ReactNode;
left?: React.ReactNode;
@@ -97,7 +99,7 @@ export interface GridlerProps extends PropsWithChildren {
rightElementStart?: React.ReactNode;
top?: React.ReactNode;
};
selectedRow?: number;
selectedRowKey?: number;
selectFirstRowOnMount?: boolean;
selectMode?: 'cell' | 'row';
showMenu?: (id: string, options?: Partial<MantineBetterMenuInstance>) => void;
@@ -110,6 +112,17 @@ export interface GridlerProps extends PropsWithChildren {
width?: number | string;
}
export interface GridlerRef {
getGlideRef: () => DataEditorRef | undefined;
getState: GridlerState['getState'];
refresh: (parms?: any) => Promise<void>;
reload: (parms?: any) => Promise<void>;
reloadRow: (key: number | string) => Promise<void>;
scrollToRow: (key: number | string) => Promise<void>;
selectRow: (key: number | string) => Promise<void>;
setStateFN: GridlerState['setStateFN'];
}
export interface GridlerState {
_active_requests?: Array<{ controller: AbortController; page: number }>;
_activeTooltip?: ReactNode;
@@ -140,6 +153,7 @@ export interface GridlerState {
abortSignal: AbortSignal
) => CellArray | GetCellsThunk;
getRowBuffer: (row: number) => Record<string, any>;
getRowIndexByKey: (key: number | string) => Promise<number | undefined>;
getState: <K extends keyof GridlerStoreState>(key: K) => GridlerStoreState[K];
hasLocalData: boolean;
@@ -174,7 +188,9 @@ export interface GridlerState {
pageSize: number;
ready: boolean;
refreshCells: (fromRow?: number, toRow?: number, col?: number) => void;
reload?: () => Promise<void>;
renderColumns?: GridlerColumns;
setState: <K extends keyof GridlerStoreState>(
key: K,
@@ -250,6 +266,7 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
return result as CellArray;
};
},
getRowBuffer: (row: number) => {
const state = get();
//Handle local data
@@ -272,6 +289,43 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
return rowData;
},
getRowIndexByKey: async (key: number | string) => {
const state = get();
let rowIndex = -1;
if (state.ready) {
const page_data = state._page_data;
const pageSize = state.pageSize;
const keyField = state.keyField ?? 'id';
for (const p in page_data) {
for (const r in page_data[p]) {
const idx = Number(p) * pageSize + Number(r);
//console.log('Found row', idx, page_data[p][r]?.[keyField], scrollToRowKey);
if (String(page_data[p][r]?.[keyField]) === String(key)) {
rowIndex =
page_data[p][r]?._rownumber > 0 ? page_data[p][r]?._rownumber : idx > 0 ? idx : -1;
break;
}
}
if (rowIndex > 0) {
console.log('Local row index', rowIndex, key);
return rowIndex;
}
}
if (rowIndex > 0) {
return rowIndex;
} else if (typeof state.askAPIRowNumber === 'function') {
const rn = await state.askAPIRowNumber(String(key));
if (rn && rn >= 0) {
console.log('Remote row index', rowIndex, key);
return rn;
}
}
}
return undefined;
},
getState: (key) => {
return get()[key];
},
@@ -702,6 +756,30 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
},
pageSize: 50,
ready: false,
refreshCells: (fromRow?: number, toRow?: number, col?: number) => {
const state = get();
const damageList: { cell: [number, number] }[] = [];
const colLen = Object.keys(state.renderColumns ?? [1, 2, 3]).length;
const from = fromRow && fromRow > 0 ? fromRow : 0;
const to = toRow && toRow >= from ? toRow : from + state.pageSize;
for (let row = from; row <= to; row++) {
if (col && col > 0) {
damageList.push({
cell: [col, row],
});
} else {
for (let c = 0; c <= colLen; c++) {
damageList.push({
cell: [c, row],
});
}
}
}
state._glideref?.updateCells(damageList);
},
setState: (key, value) => {
set(
produce((state) => {
@@ -816,60 +894,9 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
};
}, [setState, getState]);
/// logic to apply the selected row.
useEffect(() => {
const ready = getState('ready');
const ref = getState('_glideref');
const keyField = getState('keyField') ?? 'id';
const selectedRow = getState('selectedRow') ?? props.selectedRow;
const askAPIRowNumber = getState('askAPIRowNumber');
let rowIndex = -1;
if (selectedRow && ref && ready) {
const page_data = getState('_page_data');
const pageSize = getState('pageSize');
for (const p in page_data) {
for (const r in page_data[p]) {
const idx = Number(p) * pageSize + Number(r);
//console.log('Found row', idx, page_data[p][r]?.[keyField], selectedRow);
if (String(page_data[p][r]?.[keyField]) === String(selectedRow)) {
rowIndex =
page_data[p][r]?._rownumber > 0 ? page_data[p][r]?._rownumber : idx > 0 ? idx : -1;
break;
}
}
if (rowIndex > 0) {
break;
}
}
if (rowIndex > 0) {
ref.scrollTo(0, rowIndex);
getState('_events').dispatchEvent(
new CustomEvent('selectedRowFound', {
detail: { rowNumber: rowIndex, selectedRow: selectedRow },
})
);
} else if (typeof askAPIRowNumber === 'function') {
askAPIRowNumber(String(selectedRow))
.then((r) => {
if (r >= 0) {
ref.scrollTo(0, r);
getState('_events').dispatchEvent(
new CustomEvent('selectedRowFound', {
detail: { rowNumber: r, selectedRow: selectedRow },
})
);
}
})
.catch((e) => {
console.warn('Error in askAPIRowNumber', e);
});
}
}
}, [props.selectedRow]);
getState('_events').addEventListener('reload', (_e: Event) => {
getState('reload')?.();
getState('refreshCells')?.();
});
return {
@@ -877,6 +904,7 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
colSort: props.defaultSort ?? getState('colSort') ?? [],
hideMenu: props.hideMenu ?? menus.hide,
scrollToRowKey: props.scrollToRowKey ?? props.selectedRowKey ?? getState('scrollToRowKey'),
showMenu: props.showMenu ?? menus.show,
total_rows: props.total_rows ?? getState('total_rows') ?? 0,
};