Interesting delemma

This commit is contained in:
Hein 2025-10-13 18:05:59 +02:00
parent 04c516f702
commit 24227f2110
5 changed files with 326 additions and 52 deletions

View File

@ -1,5 +1,10 @@
import '@glideapps/glide-data-grid/dist/index.css'; import '@glideapps/glide-data-grid/dist/index.css';
import { DataEditor, type DataEditorRef, type GridColumn } from '@glideapps/glide-data-grid'; import {
CompactSelection,
DataEditor,
type DataEditorRef,
type GridColumn,
} from '@glideapps/glide-data-grid';
import { ActionIcon } from '@mantine/core'; import { ActionIcon } from '@mantine/core';
import { useElementSize, useMergedRef } from '@mantine/hooks'; import { useElementSize, useMergedRef } from '@mantine/hooks';
import { IconMenu2 } from '@tabler/icons-react'; import { IconMenu2 } from '@tabler/icons-react';
@ -20,9 +25,12 @@ export const GridlerDataGrid = () => {
const { ref: refWrapper, width } = useElementSize(); const { ref: refWrapper, width } = useElementSize();
const { const {
_gridSelection,
focused, focused,
getCellContent, getCellContent,
getCellsForSelection, getCellsForSelection,
getState,
glideProps,
hasLocalData, hasLocalData,
headerHeight, headerHeight,
mounted, mounted,
@ -36,13 +44,16 @@ export const GridlerDataGrid = () => {
onVisibleRegionChanged, onVisibleRegionChanged,
renderColumns, renderColumns,
rowHeight, rowHeight,
setState,
setStateFN, setStateFN,
total_rows, total_rows,
glideProps,
} = useGridlerStore((s) => ({ } = useGridlerStore((s) => ({
_gridSelection: s._gridSelection,
focused: s.focused, focused: s.focused,
getCellContent: s.getCellContent, getCellContent: s.getCellContent,
getCellsForSelection: s.getCellsForSelection, getCellsForSelection: s.getCellsForSelection,
getState: s.getState,
glideProps: s.glideProps,
hasLocalData: s.hasLocalData, hasLocalData: s.hasLocalData,
headerHeight: s.headerHeight, headerHeight: s.headerHeight,
mounted: s.mounted, mounted: s.mounted,
@ -56,13 +67,12 @@ export const GridlerDataGrid = () => {
onVisibleRegionChanged: s.onVisibleRegionChanged, onVisibleRegionChanged: s.onVisibleRegionChanged,
renderColumns: s.renderColumns, renderColumns: s.renderColumns,
rowHeight: s.rowHeight, rowHeight: s.rowHeight,
setState: s.setState,
setStateFN: s.setStateFN, setStateFN: s.setStateFN,
total_rows: s.total_rows, total_rows: s.total_rows,
glideProps: s.glideProps,
})); }));
const refMerged = useMergedRef(ref, (r) => { const refMerged = useMergedRef(ref, (r) => {
setStateFN('_glideref', () => { setStateFN('_glideref', () => {
return r ?? undefined; return r ?? undefined;
}); });
@ -80,7 +90,12 @@ export const GridlerDataGrid = () => {
const theme = useGridTheme(); const theme = useGridTheme();
if (!mounted) { if (!mounted) {
return <>Loadings...<Computer /></>; return (
<>
Loadings...
<Computer />
</>
);
} }
return ( return (
<div <div
@ -105,10 +120,10 @@ export const GridlerDataGrid = () => {
columns={(renderColumns as Array<GridColumn>) ?? []} columns={(renderColumns as Array<GridColumn>) ?? []}
columnSelect="none" columnSelect="none"
drawFocusRing drawFocusRing
getCellContent={getCellContent} getCellContent={getCellContent}
getCellsForSelection={getCellsForSelection} getCellsForSelection={getCellsForSelection}
getRowThemeOverride={theme.getRowThemeOverride} getRowThemeOverride={theme.getRowThemeOverride}
gridSelection={_gridSelection}
headerHeight={headerHeight ?? 32} headerHeight={headerHeight ?? 32}
headerIcons={{ sort: SortSprite, sortdown: SortDownSprite, sortup: SortUpSprite }} headerIcons={{ sort: SortSprite, sortdown: SortDownSprite, sortup: SortUpSprite }}
height={width} height={width}
@ -126,6 +141,30 @@ export const GridlerDataGrid = () => {
onColumnMoved={onColumnMoved} onColumnMoved={onColumnMoved}
onColumnProposeMove={onColumnProposeMove} onColumnProposeMove={onColumnProposeMove}
onColumnResize={onColumnResize} onColumnResize={onColumnResize}
onGridSelectionChange={(selection) => {
let rows = CompactSelection.empty();
const currentSelection = getState('_gridSelection');
// const currentRowSelection = getState('_gridSelectionRows') ?? CompactSelection.empty();
// for (const r of currentRowSelection) {
// rows = rows.hasIndex(r) ? rows : rows.add(r);
// }
for (const r of selection.rows) {
rows = rows.hasIndex(r) ? rows : rows.add(r);
}
if (
JSON.stringify(currentSelection?.columns) !== JSON.stringify(selection.columns) ||
JSON.stringify(currentSelection?.rows) !== JSON.stringify(selection.rows) ||
JSON.stringify(currentSelection?.current) !== JSON.stringify(selection.current)
) {
setState('_gridSelection', { ...selection, rows });
if (JSON.stringify(currentSelection?.rows) !== JSON.stringify(selection.rows)) {
setState('_gridSelectionRows', rows);
}
}
//console.log('Selection', selection);
}}
onHeaderClicked={onHeaderClicked} onHeaderClicked={onHeaderClicked}
onHeaderContextMenu={(col, event) => { onHeaderContextMenu={(col, event) => {
event.preventDefault(); event.preventDefault();
@ -139,7 +178,7 @@ export const GridlerDataGrid = () => {
}} }}
onHeaderMenuClick={onHeaderMenuClick} onHeaderMenuClick={onHeaderMenuClick}
onVisibleRegionChanged={onVisibleRegionChanged} onVisibleRegionChanged={onVisibleRegionChanged}
rangeSelect="none" rangeSelect="multi-rect"
ref={refMerged as any} ref={refMerged as any}
rightElement={ rightElement={
<ActionIcon mr="xs" mt="2px" onClick={(e) => onContextClick('menu', e)} variant="subtle"> <ActionIcon mr="xs" mt="2px" onClick={(e) => onContextClick('menu', e)} variant="subtle">
@ -151,11 +190,9 @@ export const GridlerDataGrid = () => {
//rowMarkersKind='both' //rowMarkersKind='both'
rowMarkers={{ rowMarkers={{
checkboxStyle: 'square', checkboxStyle: 'square',
kind: 'both' kind: 'both',
}} }}
rows={total_rows} rows={total_rows}
rowSelect="multi" rowSelect="multi"
// onGridSelectionChange={(sel) => { // onGridSelectionChange={(sel) => {
// console.log("Selection",sel); // console.log("Selection",sel);
@ -169,7 +206,7 @@ export const GridlerDataGrid = () => {
// width: 30 // width: 30
// }} // }}
rowSelectionMode='auto' rowSelectionMode="auto"
spanRangeBehavior="default" spanRangeBehavior="default"
theme={theme.gridTheme} theme={theme.gridTheme}
width="100%" width="100%"

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import type { APIOptions } from '../utils/types'; import type { APIOptions } from '../utils/types';
@ -11,7 +12,7 @@ export const APIAdaptorGoLangv2 = React.memo((props: APIOptions) => {
s.setState, s.setState,
s.getState, s.getState,
s.addError, s.addError,
s.mounted s.mounted,
]); ]);
const useAPIQuery: (index: number) => Promise<any> = async (index: number) => { const useAPIQuery: (index: number) => Promise<any> = async (index: number) => {
@ -19,7 +20,7 @@ export const APIAdaptorGoLangv2 = React.memo((props: APIOptions) => {
const pageSize = getState('pageSize'); const pageSize = getState('pageSize');
const colFilters = getState('colFilters'); const colFilters = getState('colFilters');
const _active_requests = getState('_active_requests'); const _active_requests = getState('_active_requests');
//console.log('APIAdaptorGoLangv2', { _active_requests, index, pageSize, props });
if (props && props.url) { if (props && props.url) {
const head = new Headers(); const head = new Headers();
head.set('x-limit', String(pageSize ?? 50)); head.set('x-limit', String(pageSize ?? 50));
@ -37,11 +38,13 @@ export const APIAdaptorGoLangv2 = React.memo((props: APIOptions) => {
} }
if (colFilters?.length && colFilters.length > 0) { if (colFilters?.length && colFilters.length > 0) {
colFilters?.filter((f)=>f.value?.length > 0)?.forEach((filter: any) => { colFilters
if (filter.value && filter.value !== '') { ?.filter((f) => f.value?.length > 0)
head.set(`x-searchop-${filter.operator}-${filter.id}`, `${filter.value}`); ?.forEach((filter: any) => {
} if (filter.value && filter.value !== '') {
}); head.set(`x-searchop-${filter.operator}-${filter.id}`, `${filter.value}`);
}
});
} }
const currentRequestIndex = _active_requests?.findIndex((f) => f.page === index) ?? -1; const currentRequestIndex = _active_requests?.findIndex((f) => f.page === index) ?? -1;
@ -56,17 +59,14 @@ export const APIAdaptorGoLangv2 = React.memo((props: APIOptions) => {
} }
const controller = new AbortController(); const controller = new AbortController();
await setStateFN('_active_requests', (cv) => [ await setStateFN('_active_requests', (cv) => [...(cv ?? []), { controller, page: index }]);
...(cv ?? []),
{ controller, page: index }
]);
const res = await fetch( const res = await fetch(
`${props.url}?x-limit=${String(pageSize ?? 50)}&x-offset=${String((pageSize ?? 50) * index)}`, `${props.url}?x-limit=${String(pageSize ?? 50)}&x-offset=${String((pageSize ?? 50) * index)}`,
{ {
headers: head, headers: head,
method: 'GET', method: 'GET',
signal: controller?.signal signal: controller?.signal,
} }
); );
@ -79,17 +79,56 @@ export const APIAdaptorGoLangv2 = React.memo((props: APIOptions) => {
const data = await res.json(); const data = await res.json();
return data ?? []; return data ?? [];
} }
addError(`${res.status} ${res.statusText}`, 'api', props.url); addError(`${res.status} ${res.statusText}`, 'api', props.url);
await setStateFN('_active_requests', (cv) => [...(cv ?? []).filter((f) => f.page !== index)]); await setStateFN('_active_requests', (cv) => [...(cv ?? []).filter((f) => f.page !== index)]);
} }
return []; return [];
}; };
const askAPIRowNumber: (key: string) => Promise<number> = async (key: string) => {
const colFilters = getState('colFilters');
//console.log('APIAdaptorGoLangv2', { _active_requests, index, pageSize, props });
if (props && props.url) {
const head = new Headers();
head.set('x-limit', '10');
head.set('x-fetch-rownumber', String(key));
head.set('Authorization', `Token ${props.authtoken}`);
if (colFilters?.length && colFilters.length > 0) {
colFilters
?.filter((f) => f.value?.length > 0)
?.forEach((filter: any) => {
if (filter.value && filter.value !== '') {
head.set(`x-searchop-${filter.operator}-${filter.id}`, `${filter.value}`);
}
});
}
const controller = new AbortController();
const res = await fetch(`${props.url}?x-fetch-rownumber=${key}}`, {
headers: head,
method: 'GET',
signal: controller?.signal,
});
if (res.ok) {
const data = await res.json();
return data?.[0]?._rownumber ?? data?._rownumber ?? 0;
}
addError(`${res.status} ${res.statusText}`, 'api', props.url);
}
return [];
};
useEffect(() => { useEffect(() => {
setState('useAPIQuery', useAPIQuery); setState('useAPIQuery', useAPIQuery);
setState('askAPIRowNumber', askAPIRowNumber);
}, [props.url, props.authtoken, mounted, setState]); }, [props.url, props.authtoken, mounted, setState]);
return <></>; return <></>;

View File

@ -1,3 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { CompactSelection } from '@glideapps/glide-data-grid';
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { useGridlerStore } from './Store'; import { useGridlerStore } from './Store';
@ -8,28 +10,157 @@ export const Computer = React.memo(() => {
const refLastFilters = useRef<any>(null); const refLastFilters = useRef<any>(null);
const { const {
_glideref, _glideref,
_gridSelectionRows,
askAPIRowNumber,
colFilters, colFilters,
colOrder, colOrder,
colSize, colSize,
colSort, colSort,
columns, columns,
getState,
loadPage, loadPage,
setState, setState,
setStateFN, setStateFN,
values,
} = useGridlerStore((s) => ({ } = useGridlerStore((s) => ({
_glideref: s._glideref, _glideref: s._glideref,
_gridSelectionRows: s._gridSelectionRows,
askAPIRowNumber: s.askAPIRowNumber,
colFilters: s.colFilters, colFilters: s.colFilters,
colOrder: s.colOrder, colOrder: s.colOrder,
colSize: s.colSize, colSize: s.colSize,
colSort: s.colSort, colSort: s.colSort,
columns: s.columns, columns: s.columns,
getState: s.getState,
loadPage: s.loadPage, loadPage: s.loadPage,
setState: s.setState, setState: s.setState,
setStateFN: s.setStateFN, setStateFN: s.setStateFN,
uniqueid: s.uniqueid, uniqueid: s.uniqueid,
values: s.values,
})); }));
useEffect(() => {
const searchSelection = async () => {
const page_data = getState('_page_data');
const pageSize = getState('pageSize');
const keyField = getState('keyField') ?? 'id';
const rowIndexes = [];
for (const vi in values as Array<any>) {
let rowIndex = -1;
const key = String(
typeof values?.[vi] === 'object'
? values?.[vi]?.[keyField]
: typeof values?.[vi] === 'string'
? values?.[vi]
: undefined
);
for (const p in page_data) {
for (const r in page_data[p]) {
const idx = Number(p) * pageSize + Number(r);
if (String(page_data[p][r]?.[keyField]) === key) {
//console.log('Found row S', idx, page_data[p][r], page_data[p][r]?.[keyField], key);
rowIndex = idx;
break;
}
}
if (rowIndex >= 0) {
rowIndexes.push(rowIndex);
break;
}
}
if (!(rowIndex >= 0) && typeof askAPIRowNumber === 'function') {
const idx = await askAPIRowNumber(key);
if (idx) {
rowIndexes.push(idx);
}
}
//console.log('Setting SSS', { key, rowIndex });
}
//console.log('Setting selection', { rowIndexes, values });
return rowIndexes;
};
if (values) {
searchSelection().then((rowIndexes) => {
// const newObj : GridSelection = {
// ...cur,
// rows: {
// items: rowIndexes.map((r) => [r - 1, r]) ?? [],
// },
// };
// console.log('Setting selection', {
// rowIndexes,
// values,
// newObj,
// });
setStateFN('_gridSelectionRows', () => {
let rows = CompactSelection.empty();
rowIndexes.forEach((r) => {
rows = rows.add(r);
});
// for (const r of cur ?? CompactSelection.empty()) {
// rows = rows.add(r);
// }
setStateFN('_gridSelection', (c) => ({
columns: c?.columns ?? CompactSelection.empty(),
...c,
rows,
}));
return rows;
});
});
}
}, [values]);
useEffect(() => {
//console.log('Gridler:Computer: Selection changed', _gridSelectionRows?.toArray());
const onChange = getState('onChange');
if (onChange && typeof onChange === 'function') {
const page_data = getState('_page_data');
const pageSize = getState('pageSize');
const buffers = [];
if (_gridSelectionRows) {
for (const range of _gridSelectionRows) {
let buffer = undefined;
for (const p in page_data) {
for (const r in page_data[p]) {
const idx = Number(p) * pageSize + Number(r);
if (isNaN(idx)) {
continue;
}
if (Number(page_data[p][r]?._rownumber) === range + 1) {
buffer = page_data[p][r];
//console.log('Found row', range, idx, page_data[p][r]?._rownumber);
break;
} else if (idx === range + 1) {
buffer = page_data[p][r];
//console.log('Found row 2', range, idx, page_data[p][r]?._rownumber);
break;
}
}
}
if (buffer !== undefined) {
buffers.push(buffer);
}
}
}
//console.log('Calling onChange with buffers', buffers, _gridSelectionRows?.toArray());
const _values = getState('values');
if (JSON.stringify(_values) !== JSON.stringify(buffers)) {
onChange(buffers);
}
}
}, [JSON.stringify(_gridSelectionRows), getState]);
useEffect(() => { useEffect(() => {
setState( setState(
'renderColumns', 'renderColumns',
@ -45,6 +176,14 @@ export const Computer = React.memo(() => {
if (!colSort) { if (!colSort) {
return; return;
} }
setState('_gridSelection', {
columns: CompactSelection.empty(),
current: undefined,
rows: CompactSelection.empty(),
});
setState('_gridSelectionRows', CompactSelection.empty());
setStateFN('renderColumns', (cols) => { setStateFN('renderColumns', (cols) => {
return cols?.map((c) => ({ return cols?.map((c) => ({
...c, ...c,
@ -64,7 +203,7 @@ export const Computer = React.memo(() => {
if (!colFilters) { if (!colFilters) {
return; return;
} }
if (JSON.stringify(refLastFilters.current) !== JSON.stringify(colFilters)) { if (JSON.stringify(refLastFilters.current) !== JSON.stringify(colFilters)) {
loadPage(0, 'all'); loadPage(0, 'all');
refLastFilters.current = colFilters; refLastFilters.current = colFilters;
@ -87,6 +226,7 @@ export const Computer = React.memo(() => {
if (!colOrder) { if (!colOrder) {
return; return;
} }
setStateFN('renderColumns', (cols) => { setStateFN('renderColumns', (cols) => {
const result = cols?.sort((a, b) => { const result = cols?.sort((a, b) => {
if (colOrder[a.id] > colOrder[b.id]) { if (colOrder[a.id] > colOrder[b.id]) {

View File

@ -10,9 +10,10 @@ import {
type GridCell, type GridCell,
GridCellKind, GridCellKind,
type GridColumn, type GridColumn,
type GridSelection,
type HeaderClickedEventArgs, type HeaderClickedEventArgs,
type Item, type Item,
type Rectangle type Rectangle,
} from '@glideapps/glide-data-grid'; } from '@glideapps/glide-data-grid';
import { getUUID } from '@warkypublic/artemis-kit'; import { getUUID } from '@warkypublic/artemis-kit';
import { getNestedValue } from '@warkypublic/artemis-kit/object'; import { getNestedValue } from '@warkypublic/artemis-kit/object';
@ -47,6 +48,7 @@ export type FilterOptionOperator =
| 'startswith'; | 'startswith';
export interface GridlerProps extends PropsWithChildren { export interface GridlerProps extends PropsWithChildren {
askAPIRowNumber?: (key: string) => Promise<number>;
columns?: GridlerColumns; columns?: GridlerColumns;
data?: Array<any>; data?: Array<any>;
defaultSort?: Array<SortOption>; defaultSort?: Array<SortOption>;
@ -58,11 +60,13 @@ export interface GridlerProps extends PropsWithChildren {
col?: GridlerColumn, col?: GridlerColumn,
defaultItems?: Array<any> defaultItems?: Array<any>
) => Array<any>; ) => Array<any>;
glideProps?: Partial<DataEditorProps>
glideProps?: Partial<DataEditorProps>;
headerHeight?: number; headerHeight?: number;
hideMenu?: (id: string) => void; hideMenu?: (id: string) => void;
keyField?: string;
maxConcurrency?: number; maxConcurrency?: number;
onChange?: (values: Array<Record<string, any>>) => void;
pageSize?: number; pageSize?: number;
progressiveScroll?: boolean; progressiveScroll?: boolean;
RenderCell?: <TRowType extends Record<string, string>>( RenderCell?: <TRowType extends Record<string, string>>(
@ -77,11 +81,15 @@ export interface GridlerProps extends PropsWithChildren {
selectedRow?: number; selectedRow?: number;
showMenu?: (id: string, options?: Partial<MantineBetterMenuInstance>) => void; showMenu?: (id: string, options?: Partial<MantineBetterMenuInstance>) => void;
uniqueid: string; uniqueid: string;
useAPIQuery?: (index: number) => Promise<Array<Record<string, any>>>;
values?: Array<Record<string, any>>;
} }
export interface GridlerState { export interface GridlerState {
_active_requests?: Array<{ controller: AbortController; page: number }>; _active_requests?: Array<{ controller: AbortController; page: number }>;
_glideref?: DataEditorRef; _glideref?: DataEditorRef;
_gridSelection?: GridSelection;
_gridSelectionRows?: GridSelection['rows'];
_loadingList: CompactSelection; _loadingList: CompactSelection;
_page_data: Record<number, Array<any>>; _page_data: Record<number, Array<any>>;
_scrollTimeout?: any | number; _scrollTimeout?: any | number;
@ -93,7 +101,6 @@ export interface GridlerState {
colSize?: Record<string, number>; colSize?: Record<string, number>;
colSort?: Array<SortOption>; colSort?: Array<SortOption>;
data?: Array<any>; data?: Array<any>;
errors: Array<string>; errors: Array<string>;
focused?: boolean; focused?: boolean;
@ -145,7 +152,6 @@ export interface GridlerState {
) => Promise<void>; ) => Promise<void>;
toCell: <TRowType extends Record<string, string>>(row: any, col: number) => GridCell; toCell: <TRowType extends Record<string, string>>(row: any, col: number) => GridCell;
total_rows: number; total_rows: number;
useAPIQuery?: (index: number) => Promise<Array<Record<string, any>>>;
} }
export type GridlerStoreState = GridlerProps & GridlerState; export type GridlerStoreState = GridlerProps & GridlerState;
@ -206,7 +212,6 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
const state = get(); const state = get();
//const firstPage = Math.max(0, Math.floor(selection.y / state.pageSize)); //const firstPage = Math.max(0, Math.floor(selection.y / state.pageSize));
//const lastPage = Math.floor((selection.y + selection.height) / state.pageSize); //const lastPage = Math.floor((selection.y + selection.height) / state.pageSize);
//console.log('Gridler:Debug:getCellsForSelection', selection, firstPage, lastPage);
await state.setStateFN('_visibleArea', (cv) => { await state.setStateFN('_visibleArea', (cv) => {
//if (r.x === cv.x && r.y === cv.y && r.width === cv.width && r.height === cv.height) //if (r.x === cv.x && r.y === cv.y && r.width === cv.width && r.height === cv.height)
@ -214,17 +219,6 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
return selection; return selection;
}); });
// for (const pageChunk of chunk(
// range(firstPage, lastPage + 1).filter((i) => !state._loadingList.hasIndex(i)),
// state.maxConcurrency
// )) {
// await Promise.all(pageChunk.map(state.loadPage));
// }
// for (const page of range(firstPage - 1, lastPage + 1, 1)) {
// state.loadPage(page);
// }
const result: GridCell[][] = []; const result: GridCell[][] = [];
for (let y = selection.y; y < selection.y + selection.height; y++) { for (let y = selection.y; y < selection.y + selection.height; y++) {
@ -235,6 +229,7 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
result.push(row); result.push(row);
} }
//console.log('Gridler:Debug:getCellsForSelection', selection, result);
return result as CellArray; return result as CellArray;
}; };
}, },
@ -242,6 +237,7 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
return get()[key]; return get()[key];
}, },
hasLocalData: false, hasLocalData: false,
keyField: 'id',
loadPage: async (pPage: number, clearMode?: 'all' | 'page') => { loadPage: async (pPage: number, clearMode?: 'all' | 'page') => {
const state = get(); const state = get();
const page = pPage < 0 ? 0 : pPage; const page = pPage < 0 ? 0 : pPage;
@ -477,7 +473,7 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
}, },
]; ];
const items = [ const items = [
...(coldef.disableSort ? []: sortItems), ...(coldef.disableSort ? [] : sortItems),
{ {
label: `Filter ${coldef?.title ?? coldef?.id}`, label: `Filter ${coldef?.title ?? coldef?.id}`,
}, },
@ -649,11 +645,7 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
uniqueid: getUUID(), uniqueid: getUUID(),
}), }),
(props) => { (props) => {
const [setState, glideref, getState] = props.useStore((s) => [ const [setState, getState] = props.useStore((s) => [s.setState, s.getState]);
s.setState,
s._glideref,
s.getState,
]);
const menus = useMantineBetterMenus(); const menus = useMantineBetterMenus();
@ -661,6 +653,47 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
setState('mounted', true); setState('mounted', true);
}, [setState]); }, [setState]);
/// logic to apply the selected row.
useEffect(() => {
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) {
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);
} else if (typeof askAPIRowNumber === 'function') {
askAPIRowNumber(String(selectedRow))
.then((r) => {
if (r >= 0) {
ref.scrollTo(0, r);
}
})
.catch((e) => {
console.warn('Error in askAPIRowNumber', e);
});
}
}
}, [props.selectedRow]);
return { return {
...props, ...props,
hasLocalData: props.data && props.data.length > 0, hasLocalData: props.data && props.data.length > 0,

View File

@ -1,5 +1,6 @@
import { Divider, Stack, TextInput } from '@mantine/core'; import { Divider, Group, Stack, TagsInput, TextInput } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
import { useState } from 'react';
import type { GridlerColumns } from '../components/Column'; import type { GridlerColumns } from '../components/Column';
@ -12,7 +13,8 @@ export const GridlerGoAPIExampleEventlog = () => {
key: 'apiurl', key: 'apiurl',
}); });
const [apiKey, setApiKey] = useLocalStorage({ defaultValue: '', key: 'apikey' }); const [apiKey, setApiKey] = useLocalStorage({ defaultValue: '', key: 'apikey' });
const [selectRow, setSelectRow] = useState<string | undefined>('');
const [values, setValues] = useState<Array<Record<string, any>>>([]);
const columns: GridlerColumns = [ const columns: GridlerColumns = [
{ {
id: 'id_process', id: 'id_process',
@ -58,10 +60,33 @@ export const GridlerGoAPIExampleEventlog = () => {
// }, // },
]; ];
}} }}
keyField="id_process"
onChange={(v) => {
console.log('GridlerGoAPIExampleEventlog onChange', v);
setValues(v);
}}
selectedRow={selectRow ? parseInt(selectRow, 10) : undefined}
uniqueid="gridtest" uniqueid="gridtest"
values={values}
> >
<APIAdaptorGoLangv2 authtoken={apiKey} url={`${apiUrl}/public/process`} /> <APIAdaptorGoLangv2 authtoken={apiKey} url={`${apiUrl}/public/process`} />
</Gridler> </Gridler>
<Divider />
<Group>
<TextInput
onChange={(e) => setSelectRow(e.target.value)}
placeholder="row"
value={selectRow}
w="90px"
/>
<TagsInput
data={[]}
onChange={(str) => setValues(str.map((v) => ({ id_process: String(v) })))}
placeholder="Values"
value={values.map((v) => String(v?.id_process))}
/>
;
</Group>
</Stack> </Stack>
); );
}; };