Fixed the selection and rendering issues of the syncstore
This commit is contained in:
parent
24227f2110
commit
9c11b609dc
@ -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"
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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%;
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
14
src/Gridler/components/BottomBar.tsx
Normal file
14
src/Gridler/components/BottomBar.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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]);
|
||||||
|
|||||||
@ -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)) {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user