Fixed the selection and rendering issues of the syncstore

This commit is contained in:
Hein 2025-10-15 09:57:40 +02:00
parent 24227f2110
commit 9c11b609dc
9 changed files with 225 additions and 185 deletions

View File

@ -84,7 +84,7 @@
"@mantine/core": "^8.3.1", "@mantine/core": "^8.3.1",
"@mantine/hooks": "^8.3.1", "@mantine/hooks": "^8.3.1",
"@warkypublic/artemis-kit": "^1.0.10", "@warkypublic/artemis-kit": "^1.0.10",
"@warkypublic/zustandsyncstore": "^0.0.2", "@warkypublic/zustandsyncstore": "^0.0.4",
"react": ">= 19.0.0", "react": ">= 19.0.0",
"use-sync-external-store": ">= 1.4.0", "use-sync-external-store": ">= 1.4.0",
"zustand": ">= 5.0.0" "zustand": ">= 5.0.0"

View File

@ -30,8 +30,8 @@ importers:
specifier: ^1.0.10 specifier: ^1.0.10
version: 1.0.10 version: 1.0.10
'@warkypublic/zustandsyncstore': '@warkypublic/zustandsyncstore':
specifier: ^0.0.2 specifier: ^0.0.4
version: 0.0.2(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1))(zustand@5.0.8(@types/react@19.1.13)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1))) version: 0.0.4(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1))(zustand@5.0.8(@types/react@19.1.13)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)))
immer: immer:
specifier: ^10.1.3 specifier: ^10.1.3
version: 10.1.3 version: 10.1.3
@ -1180,8 +1180,8 @@ packages:
resolution: {integrity: sha512-qIgjcWqLyYfoKDUYt3Gm7PVe2S4AdjA46J1jPIff1p6wUP5WsHA8UfZq7pEdP6YNxqavv+h84oe1+HsJOoU6jQ==} resolution: {integrity: sha512-qIgjcWqLyYfoKDUYt3Gm7PVe2S4AdjA46J1jPIff1p6wUP5WsHA8UfZq7pEdP6YNxqavv+h84oe1+HsJOoU6jQ==}
engines: {node: '>=14.16'} engines: {node: '>=14.16'}
'@warkypublic/zustandsyncstore@0.0.2': '@warkypublic/zustandsyncstore@0.0.4':
resolution: {integrity: sha512-QHeOjBcO5K5T9O50BCxQZSTkagiUBUi8WnxG2JBXtwTJL8SIPKqM4E21OxjH7sC9e8FyQdCyGWiAl/5GYxaWow==} resolution: {integrity: sha512-LJ+/rxnPeAybcRSVWHzl3dHC35IsqZH1n++g6Xv3fMXX41XPF/bkCMd3lKatqLmQWPwtMPriBSmG4ukm47vaAQ==}
peerDependencies: peerDependencies:
react: '>= 19.0.0' react: '>= 19.0.0'
use-sync-external-store: '>= 1.4.0' use-sync-external-store: '>= 1.4.0'
@ -4493,7 +4493,7 @@ snapshots:
semver: 7.7.2 semver: 7.7.2
uuid: 11.1.0 uuid: 11.1.0
'@warkypublic/zustandsyncstore@0.0.2(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1))(zustand@5.0.8(@types/react@19.1.13)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)))': '@warkypublic/zustandsyncstore@0.0.4(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1))(zustand@5.0.8(@types/react@19.1.13)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)))':
dependencies: dependencies:
'@warkypublic/artemis-kit': 1.0.10 '@warkypublic/artemis-kit': 1.0.10
react: 19.1.1 react: 19.1.1

View File

@ -4,8 +4,8 @@
0 1px 0 #00000030, 0 1px 0 #00000030,
-1px 0 0 #00000030; -1px 0 0 #00000030;
display: flex; display: flex;
min-height: 50px; min-height: 40px;
min-width: 50px; min-width: 40px;
height: 100%; height: 100%;
width: 100%; width: 100%;

View File

@ -5,11 +5,12 @@ import {
type DataEditorRef, type DataEditorRef,
type GridColumn, type GridColumn,
} from '@glideapps/glide-data-grid'; } from '@glideapps/glide-data-grid';
import { ActionIcon } from '@mantine/core'; import { ActionIcon, Stack } 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';
import React from 'react'; import React from 'react';
import { BottomBar } from './components/BottomBar';
import { Computer } from './components/Computer'; import { Computer } from './components/Computer';
import { Pager } from './components/Pager'; import { Pager } from './components/Pager';
import { SortSprite } from './components/sprites/Sort'; import { SortSprite } from './components/sprites/Sort';
@ -41,9 +42,12 @@ export const GridlerDataGrid = () => {
onContextClick, onContextClick,
onHeaderClicked, onHeaderClicked,
onHeaderMenuClick, onHeaderMenuClick,
onItemHovered,
onVisibleRegionChanged, onVisibleRegionChanged,
renderColumns, renderColumns,
rowHeight, rowHeight,
sections,
selectMode,
setState, setState,
setStateFN, setStateFN,
total_rows, total_rows,
@ -64,9 +68,12 @@ export const GridlerDataGrid = () => {
onContextClick: s.onContextClick, onContextClick: s.onContextClick,
onHeaderClicked: s.onHeaderClicked, onHeaderClicked: s.onHeaderClicked,
onHeaderMenuClick: s.onHeaderMenuClick, onHeaderMenuClick: s.onHeaderMenuClick,
onItemHovered: s.onItemHovered,
onVisibleRegionChanged: s.onVisibleRegionChanged, onVisibleRegionChanged: s.onVisibleRegionChanged,
renderColumns: s.renderColumns, renderColumns: s.renderColumns,
rowHeight: s.rowHeight, rowHeight: s.rowHeight,
sections: s.sections,
selectMode: s.selectMode,
setState: s.setState, setState: s.setState,
setStateFN: s.setStateFN, setStateFN: s.setStateFN,
total_rows: s.total_rows, total_rows: s.total_rows,
@ -78,145 +85,142 @@ export const GridlerDataGrid = () => {
}); });
}); });
// const args = useAsyncDataSource<string[]>(
// pageSize ?? 50,
// maxConcurrency ?? 1,
// getRowData,
// toCell,
// onEdited,
// ref
// );
const theme = useGridTheme(); const theme = useGridTheme();
if (!mounted) { if (!mounted) {
return ( return (
<> <>
Loadings... Loading...
<Computer /> <Computer />
</> </>
); );
} }
return ( return (
<div <Stack align="stretch" gap={0} h="100%" justify="stretch" w="100%">
className={classes.container} {sections?.top}
data-focused={focused} <div
onContextMenu={(e) => { className={classes.container}
e.preventDefault(); data-focused={focused}
//Yes this is a litle hacky, but it works to prevent double context menu onContextMenu={(e) => {
if (!refContextActivated.current) { e.preventDefault();
refContextActivated.current = true; //Yes this is a litle hacky, but it works to prevent double context menu
onContextClick('other', e);
setTimeout(() => {
refContextActivated.current = false;
}, 100);
}
}}
ref={refWrapper}
>
<DataEditor
cellActivationBehavior="double-click"
//getCelrefMergedlContent={getCellContent}
columns={(renderColumns as Array<GridColumn>) ?? []}
columnSelect="none"
drawFocusRing
getCellContent={getCellContent}
getCellsForSelection={getCellsForSelection}
getRowThemeOverride={theme.getRowThemeOverride}
gridSelection={_gridSelection}
headerHeight={headerHeight ?? 32}
headerIcons={{ sort: SortSprite, sortdown: SortDownSprite, sortup: SortUpSprite }}
height={width}
onCellContextMenu={(cell, event) => {
event.preventDefault();
if (!refContextActivated.current) { if (!refContextActivated.current) {
refContextActivated.current = true; refContextActivated.current = true;
onContextClick('cell', event, cell[0], cell[1]); onContextClick('other', e);
setTimeout(() => { setTimeout(() => {
refContextActivated.current = false; refContextActivated.current = false;
}, 100); }, 100);
} }
}} }}
onCellEdited={onCellEdited} ref={refWrapper}
onColumnMoved={onColumnMoved} >
onColumnProposeMove={onColumnProposeMove} {sections?.left}
onColumnResize={onColumnResize} <DataEditor
onGridSelectionChange={(selection) => { cellActivationBehavior="double-click"
let rows = CompactSelection.empty(); //getCelrefMergedlContent={getCellContent}
const currentSelection = getState('_gridSelection'); columns={(renderColumns as Array<GridColumn>) ?? []}
// const currentRowSelection = getState('_gridSelectionRows') ?? CompactSelection.empty(); columnSelect="none"
// for (const r of currentRowSelection) { drawFocusRing
// rows = rows.hasIndex(r) ? rows : rows.add(r); getCellContent={getCellContent}
// } getCellsForSelection={getCellsForSelection}
for (const r of selection.rows) { getRowThemeOverride={theme.getRowThemeOverride}
rows = rows.hasIndex(r) ? rows : rows.add(r); gridSelection={_gridSelection}
} headerHeight={headerHeight ?? 32}
headerIcons={{ sort: SortSprite, sortdown: SortDownSprite, sortup: SortUpSprite }}
if ( height={width}
JSON.stringify(currentSelection?.columns) !== JSON.stringify(selection.columns) || onCellContextMenu={(cell, event) => {
JSON.stringify(currentSelection?.rows) !== JSON.stringify(selection.rows) || event.preventDefault();
JSON.stringify(currentSelection?.current) !== JSON.stringify(selection.current) if (!refContextActivated.current) {
) { refContextActivated.current = true;
setState('_gridSelection', { ...selection, rows }); onContextClick('cell', event, cell[0], cell[1]);
if (JSON.stringify(currentSelection?.rows) !== JSON.stringify(selection.rows)) { setTimeout(() => {
setState('_gridSelectionRows', rows); refContextActivated.current = false;
}, 100);
}
}}
onCellEdited={onCellEdited}
onColumnMoved={onColumnMoved}
onColumnProposeMove={onColumnProposeMove}
onColumnResize={onColumnResize}
onGridSelectionChange={(selection) => {
let rows = CompactSelection.empty();
const currentSelection = getState('_gridSelection');
for (const r of selection.rows) {
rows = rows.hasIndex(r) ? rows : rows.add(r);
}
if (selectMode === 'row' && selection.current?.range) {
for (
let y = selection.current.range.y;
y < selection.current.range.y + selection.current.range.height;
y++
) {
rows = rows.hasIndex(y) ? rows : rows.add(y);
}
} }
}
//console.log('Selection', selection); if (
}} JSON.stringify(currentSelection?.columns) !== JSON.stringify(selection.columns) ||
onHeaderClicked={onHeaderClicked} JSON.stringify(currentSelection?.rows) !== JSON.stringify(rows) ||
onHeaderContextMenu={(col, event) => { JSON.stringify(currentSelection?.current) !== JSON.stringify(selection.current)
event.preventDefault(); ) {
if (!refContextActivated.current) { setState('_gridSelection', { ...selection, rows });
refContextActivated.current = true; if (JSON.stringify(currentSelection?.rows) !== JSON.stringify(rows)) {
onContextClick('header', event, col); setState('_gridSelectionRows', rows);
setTimeout(() => { }
refContextActivated.current = false; }
}, 100);
}
}}
onHeaderMenuClick={onHeaderMenuClick}
onVisibleRegionChanged={onVisibleRegionChanged}
rangeSelect="multi-rect"
ref={refMerged as any}
rightElement={
<ActionIcon mr="xs" mt="2px" onClick={(e) => onContextClick('menu', e)} variant="subtle">
<IconMenu2 />
</ActionIcon>
}
rowHeight={rowHeight ?? 22}
//rowMarkersCheckboxStyle='square'
//rowMarkersKind='both'
rowMarkers={{
checkboxStyle: 'square',
kind: 'both',
}}
rows={total_rows}
rowSelect="multi"
// onGridSelectionChange={(sel) => {
// console.log("Selection",sel);
// }}
// onItemHovered={(item) => {
// console.log('Hovered', item);
// }}
//showSearch={true}
// rowMarkers={{
// kind: 'clickable-number',
// width: 30
// }}
rowSelectionMode="auto" //console.log('Selection', selection);
spanRangeBehavior="default" }}
theme={theme.gridTheme} onHeaderClicked={onHeaderClicked}
width="100%" onHeaderContextMenu={(col, event) => {
{...glideProps} event.preventDefault();
/> if (!refContextActivated.current) {
{/* <Portal> */} refContextActivated.current = true;
<div id="portal" /> onContextClick('header', event, col);
{/* </Portal> */} setTimeout(() => {
<Computer /> refContextActivated.current = false;
{!hasLocalData && <Pager />} }, 100);
</div> }
}}
onHeaderMenuClick={onHeaderMenuClick}
onItemHovered={onItemHovered}
onVisibleRegionChanged={onVisibleRegionChanged}
rangeSelect="multi-rect"
ref={refMerged as any}
rightElement={
<ActionIcon
mr="xs"
mt="2px"
onClick={(e) => onContextClick('menu', e)}
variant="subtle"
>
<IconMenu2 />
</ActionIcon>
}
rowHeight={rowHeight ?? 22}
//rowMarkersCheckboxStyle='square'
//rowMarkersKind='both'
rowMarkers={{
checkboxStyle: 'square',
kind: 'both',
}}
rows={total_rows}
rowSelect="multi"
rowSelectionMode="auto"
spanRangeBehavior="default"
theme={theme.gridTheme}
width="100%"
{...glideProps}
/>
{/* <Portal> */}
<div id="portal" />
{/* </Portal> */}
<Computer />
{!hasLocalData && <Pager />}
{sections?.right}
</div>
<BottomBar />
{sections?.bottom}
</Stack>
); );
}; };

View File

@ -0,0 +1,14 @@
import { useGridlerStore } from './Store';
export function BottomBar() {
const { _activeTooltip, tooltipBarProps } = useGridlerStore((s) => ({
_activeTooltip: s._activeTooltip,
tooltipBarProps: s.tooltipBarProps,
}));
return (
<div data-tooltip-bar style={{ minHeight: '24px' }} {...tooltipBarProps}>
{_activeTooltip}
</div>
);
}

View File

@ -3,7 +3,7 @@ import { type BaseGridColumn, type GridCell, GridCellKind } from '@glideapps/gli
import { ActionIcon, Select, Stack, TextInput } from '@mantine/core'; import { ActionIcon, Select, Stack, TextInput } from '@mantine/core';
import { useDebouncedValue } from '@mantine/hooks'; import { useDebouncedValue } from '@mantine/hooks';
import { IconX } from '@tabler/icons-react'; import { IconX } from '@tabler/icons-react';
import { useEffect, useState } from 'react'; import { type ReactNode, useEffect, useState } from 'react';
import type { FilterOption, FilterOptionOperator, GridlerStoreState } from './Store'; import type { FilterOption, FilterOptionOperator, GridlerStoreState } from './Store';
@ -34,6 +34,7 @@ export interface GridlerColumn extends Partial<BaseGridColumn> {
id: string; id: string;
maxWidth?: number; maxWidth?: number;
minWidth?: number; minWidth?: number;
tooltip?: ((buffer: any, row: number, col: number) => ReactNode) | string;
width?: number; width?: number;
} }
@ -84,7 +85,6 @@ export const ColumnFilterInput = (props: ColumnFilterSetProps) => {
}); });
} }
return filters; return filters;
}); });
}, [defferedFilterValue, props.column.id, props.options, props.storeState]); }, [defferedFilterValue, props.column.id, props.options, props.storeState]);
@ -136,8 +136,6 @@ export const ColumnFilterInputOperator = (props: ColumnFilterSetProps) => {
}); });
} }
return filters; return filters;
}); });
}, [defferedFilterValue, props.column.id, props.options, props.storeState]); }, [defferedFilterValue, props.column.id, props.options, props.storeState]);

View File

@ -77,48 +77,32 @@ export const Computer = React.memo(() => {
rowIndexes.push(idx); rowIndexes.push(idx);
} }
} }
//console.log('Setting SSS', { key, rowIndex });
} }
//console.log('Setting selection', { rowIndexes, values });
return rowIndexes; return rowIndexes;
}; };
if (values) { if (values) {
searchSelection().then((rowIndexes) => { searchSelection().then((rowIndexes) => {
// const newObj : GridSelection = { let rows = CompactSelection.empty();
// ...cur, rowIndexes.forEach((r) => {
rows = rows.add(r);
});
// rows: {
// items: rowIndexes.map((r) => [r - 1, r]) ?? [],
// },
// };
// console.log('Setting selection', {
// rowIndexes,
// values,
// newObj,
// });
setStateFN('_gridSelectionRows', () => { 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; return rows;
}); });
setStateFN('_gridSelection', (c) => ({
columns: c?.columns ?? CompactSelection.empty(),
...c,
rows,
}));
}); });
} }
}, [values]); }, [values]);
useEffect(() => { useEffect(() => {
//console.log('Gridler:Computer: Selection changed', _gridSelectionRows?.toArray());
const onChange = getState('onChange'); const onChange = getState('onChange');
if (onChange && typeof onChange === 'function') { if (onChange && typeof onChange === 'function') {
const page_data = getState('_page_data'); const page_data = getState('_page_data');
@ -152,7 +136,7 @@ export const Computer = React.memo(() => {
} }
} }
} }
//console.log('Calling onChange with buffers', buffers, _gridSelectionRows?.toArray());
const _values = getState('values'); const _values = getState('values');
if (JSON.stringify(_values) !== JSON.stringify(buffers)) { if (JSON.stringify(_values) !== JSON.stringify(buffers)) {

View File

@ -10,6 +10,7 @@ import {
type GridCell, type GridCell,
GridCellKind, GridCellKind,
type GridColumn, type GridColumn,
type GridMouseEventArgs,
type GridSelection, type GridSelection,
type HeaderClickedEventArgs, type HeaderClickedEventArgs,
type Item, type Item,
@ -19,7 +20,7 @@ import { getUUID } from '@warkypublic/artemis-kit';
import { getNestedValue } from '@warkypublic/artemis-kit/object'; import { getNestedValue } from '@warkypublic/artemis-kit/object';
import { createSyncStore } from '@warkypublic/zustandsyncstore'; import { createSyncStore } from '@warkypublic/zustandsyncstore';
import { produce } from 'immer'; import { produce } from 'immer';
import { type PropsWithChildren, useEffect } from 'react'; import { type PropsWithChildren, type ReactNode, useEffect } from 'react';
import { type MantineBetterMenuInstance, useMantineBetterMenus } from '../../MantineBetterMenu'; import { type MantineBetterMenuInstance, useMantineBetterMenus } from '../../MantineBetterMenu';
import { type TRequest } from '../utils/types'; import { type TRequest } from '../utils/types';
@ -78,8 +79,16 @@ export interface GridlerProps extends PropsWithChildren {
) => GridCell; ) => GridCell;
request?: TRequest; request?: TRequest;
rowHeight?: number; rowHeight?: number;
sections?: {
bottom?: React.ReactNode;
left?: React.ReactNode;
right?: React.ReactNode;
top?: React.ReactNode;
};
selectedRow?: number; selectedRow?: number;
selectMode?: 'cell' | 'row';
showMenu?: (id: string, options?: Partial<MantineBetterMenuInstance>) => void; showMenu?: (id: string, options?: Partial<MantineBetterMenuInstance>) => void;
tooltipBarProps?: React.HTMLAttributes<HTMLDivElement>;
uniqueid: string; uniqueid: string;
useAPIQuery?: (index: number) => Promise<Array<Record<string, any>>>; useAPIQuery?: (index: number) => Promise<Array<Record<string, any>>>;
values?: Array<Record<string, any>>; values?: Array<Record<string, any>>;
@ -87,6 +96,7 @@ export interface GridlerProps extends PropsWithChildren {
export interface GridlerState { export interface GridlerState {
_active_requests?: Array<{ controller: AbortController; page: number }>; _active_requests?: Array<{ controller: AbortController; page: number }>;
_activeTooltip?: ReactNode;
_glideref?: DataEditorRef; _glideref?: DataEditorRef;
_gridSelection?: GridSelection; _gridSelection?: GridSelection;
_gridSelectionRows?: GridSelection['rows']; _gridSelectionRows?: GridSelection['rows'];
@ -110,6 +120,7 @@ export interface GridlerState {
selection: Rectangle, selection: Rectangle,
abortSignal: AbortSignal abortSignal: AbortSignal
) => CellArray | GetCellsThunk; ) => CellArray | GetCellsThunk;
getRowBuffer: (row: number) => any;
getState: <K extends keyof GridlerStoreState>(key: K) => GridlerStoreState[K]; getState: <K extends keyof GridlerStoreState>(key: K) => GridlerStoreState[K];
hasLocalData: boolean; hasLocalData: boolean;
@ -127,6 +138,7 @@ export interface GridlerState {
onContextClick: (area: string, event: any, col?: number, row?: number) => void; onContextClick: (area: string, event: any, col?: number, row?: number) => void;
onHeaderClicked: (colIndex: number, event: HeaderClickedEventArgs) => void; onHeaderClicked: (colIndex: number, event: HeaderClickedEventArgs) => void;
onHeaderMenuClick: (col: number, screenPosition: Rectangle) => void; onHeaderMenuClick: (col: number, screenPosition: Rectangle) => void;
onItemHovered: (args: GridMouseEventArgs) => void;
onVisibleRegionChanged: ( onVisibleRegionChanged: (
r: Rectangle, r: Rectangle,
tx: number, tx: number,
@ -179,28 +191,10 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
const state = get(); const state = get();
const [col, row] = cell; const [col, row] = cell;
//Handle local data const buffer = state.getRowBuffer(row);
if (state.data && state.data.length > 0) {
if (state.data[row] === undefined) {
return {
allowOverlay: false,
kind: GridCellKind.Loading,
};
}
return state.toCell(state.data[row], col);
}
//Handle remote paged data if (buffer !== undefined) {
const firstPage = Math.max(0, Math.floor(row / state.pageSize)); return state.toCell(buffer, col);
const upperPage = state.pageSize * firstPage;
const index = row - upperPage;
const rowData = state._page_data?.[firstPage]?.[index];
//console.log('getCellContent', row, firstPage, upperPage, index, rowData);
//This is empty, that is why the grid renders nothing
if (rowData !== undefined) {
return state.toCell(rowData, col);
} }
return { return {
allowOverlay: false, allowOverlay: false,
@ -233,6 +227,28 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
return result as CellArray; return result as CellArray;
}; };
}, },
getRowBuffer: (row: number) => {
const state = get();
//Handle local data
if (state.data && state.data.length > 0) {
if (state.data[row] === undefined) {
return {
allowOverlay: false,
kind: GridCellKind.Loading,
};
}
return state.data[row];
}
//Handle remote paged data
const firstPage = Math.max(0, Math.floor(row / state.pageSize));
const upperPage = state.pageSize * firstPage;
const index = row - upperPage;
const rowData = state._page_data?.[firstPage]?.[index];
return rowData;
},
getState: (key) => { getState: (key) => {
return get()[key]; return get()[key];
}, },
@ -499,6 +515,20 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
y: screenPosition.y, y: screenPosition.y,
}); });
}, },
onItemHovered: (args: GridMouseEventArgs) => {
const s = get();
s.setState('_activeTooltip', undefined);
if (args.kind === 'cell') {
//_activeTooltip
const coldef = s.renderColumns?.[args.location[0]];
if (coldef?.tooltip && typeof coldef?.tooltip === 'string') {
s.setState('_activeTooltip', coldef?.tooltip);
} else if (coldef?.tooltip && typeof coldef?.tooltip === 'function') {
const buffer = s.getRowBuffer(args.location[1]);
s.setState('_activeTooltip', coldef?.tooltip(buffer, args.location[1], args.location[0]));
}
}
},
onVisibleRegionChanged: ( onVisibleRegionChanged: (
region: Rectangle, region: Rectangle,
tx: number, tx: number,

View File

@ -25,6 +25,9 @@ export const GridlerGoAPIExampleEventlog = () => {
{ {
id: 'process', id: 'process',
title: 'Process', title: 'Process',
tooltip: (buffer) => {
return `Process: ${buffer?.process}\nType: ${buffer?.processtype}\nStatus: ${buffer?.status}`;
},
width: 200, width: 200,
}, },
{ {
@ -62,10 +65,17 @@ export const GridlerGoAPIExampleEventlog = () => {
}} }}
keyField="id_process" keyField="id_process"
onChange={(v) => { onChange={(v) => {
console.log('GridlerGoAPIExampleEventlog onChange', v); //console.log('GridlerGoAPIExampleEventlog onChange', v);
setValues(v); setValues(v);
}} }}
sections={{
bottom: <div style={{ backgroundColor: 'teal', height: '25px' }}>bottom</div>,
left: <div style={{ backgroundColor: 'orange', width: '20px' }}>L</div>,
right: <div style={{ backgroundColor: 'green', width: '20px' }}>R</div>,
top: <div style={{ backgroundColor: 'purple', height: '20px' }}>top</div>,
}}
selectedRow={selectRow ? parseInt(selectRow, 10) : undefined} selectedRow={selectRow ? parseInt(selectRow, 10) : undefined}
selectMode="row"
uniqueid="gridtest" uniqueid="gridtest"
values={values} values={values}
> >