Interesting delemma
This commit is contained in:
parent
04c516f702
commit
24227f2110
@ -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%"
|
||||||
|
|||||||
@ -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,7 +38,9 @@ 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
|
||||||
|
?.filter((f) => f.value?.length > 0)
|
||||||
|
?.forEach((filter: any) => {
|
||||||
if (filter.value && filter.value !== '') {
|
if (filter.value && filter.value !== '') {
|
||||||
head.set(`x-searchop-${filter.operator}-${filter.id}`, `${filter.value}`);
|
head.set(`x-searchop-${filter.operator}-${filter.id}`, `${filter.value}`);
|
||||||
}
|
}
|
||||||
@ -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,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -82,14 +82,53 @@ export const APIAdaptorGoLangv2 = React.memo((props: APIOptions) => {
|
|||||||
}
|
}
|
||||||
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 <></>;
|
||||||
|
|||||||
@ -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,
|
||||||
@ -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]) {
|
||||||
|
|||||||
@ -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;
|
||||||
@ -94,7 +102,6 @@ export interface GridlerState {
|
|||||||
colSort?: Array<SortOption>;
|
colSort?: Array<SortOption>;
|
||||||
data?: Array<any>;
|
data?: Array<any>;
|
||||||
|
|
||||||
|
|
||||||
errors: Array<string>;
|
errors: Array<string>;
|
||||||
focused?: boolean;
|
focused?: boolean;
|
||||||
get: () => GridlerState;
|
get: () => GridlerState;
|
||||||
@ -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;
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user