Form interface work

This commit is contained in:
Hein 2025-10-17 17:04:12 +02:00
parent 8d26d56599
commit 6350b513ca
6 changed files with 197 additions and 62 deletions

View File

@ -2,6 +2,9 @@ import '@glideapps/glide-data-grid/dist/index.css';
import React from 'react';
import { MantineBetterMenusProvider } from '../MantineBetterMenu';
import { APIAdaptorGoLangv2 } from './components/APIAdaptorGoLangv2';
import { GlidlerFormInterface } from './components/GridlerFormInterface';
import { LocalDataAdaptor } from './components/LocalDataAdaptor';
import { type GridlerProps, Provider } from './components/Store';
import { GridlerDataGrid } from './GridlerDataGrid';
@ -23,3 +26,7 @@ export const Gridler = (props: GridlerProps) => {
</MantineBetterMenusProvider>
);
};
Gridler.GlidlerFormInterface = GlidlerFormInterface;
Gridler.APIAdaptorGoLangv2 = APIAdaptorGoLangv2;
Gridler.LocalDataAdaptor = LocalDataAdaptor;

View File

@ -1,26 +1,112 @@
import { useEffect } from 'react';
import { useCallback, useEffect } from 'react';
import type { MantineBetterMenuInstanceItem } from '../../MantineBetterMenu';
import type { FormRequestType } from '../utils/types';
import type { GridlerColumn } from './Column';
import { type GridlerProps, useGridlerStore } from './Store';
import { type GridlerProps, type GridlerState, useGridlerStore } from './Store';
export function GlidlerFormInterface(props: {
getMenuItems?: GridlerProps['getMenuItems'];
onRequestForm: (request: FormRequestType, data: Record<string, any>) => void;
onReload?: () => void;
onRequestForm: (request: FormRequestType, data: Record<string, unknown>) => void;
}) {
// const [getMenuItems, getState, , mounted, setState] = useGridlerStore((s) => [
// s.getMenuItems,
// s.getState,
// s.mounted,
// s.setState,
// ]);
const [getState, mounted, setState, reload] = useGridlerStore((s) => [
s.getState,
s.mounted,
s.setState,
s.reload,
]);
// useEffect(() => {
// if (mounted) {
// setState('getMenuItems', props.getMenuItems);
// setState('onRequestForm', props.onRequestForm);
// }
// }, [props.getMenuItems, props.onRequestForm, mounted, setState]);
const getMenuItems = useCallback(
(
id: string,
storeState: GridlerState,
row?: unknown,
col?: GridlerColumn,
defaultItems?: Array<unknown>
) => {
//console.log('GlidlerFormInterface getMenuItems', id);
if (id === 'header-menu') {
return defaultItems || [];
}
const items = [] as Array<MantineBetterMenuInstanceItem>;
if (!row) {
const firstRow = getState('_gridSelection')?.rows?.first();
if (firstRow !== undefined) {
row = storeState.getRowBuffer(firstRow);
}
}
if (id === 'other') {
items.push({
c: 'blue',
label: 'Add',
onClick: () => {
props.onRequestForm('insert', row as Record<string, unknown>);
},
});
}
if ((id === 'cell' && row) || (id === 'menu' && row)) {
items.push({
c: 'blue',
label: 'Add',
onClick: () => {
props.onRequestForm('insert', row as Record<string, unknown>);
},
});
items.push({
c: 'green',
label: 'Change',
onClick: () => {
props.onRequestForm('change', row as Record<string, unknown>);
},
});
items.push({
c: 'red',
label: 'Delete',
onClick: () => {
props.onRequestForm('delete', row as Record<string, unknown>);
},
});
}
items.push({
isDivider: true,
});
items.push({
c: 'orange',
label: 'Refresh',
onClick: () => {
reload?.();
},
});
const result = props.getMenuItems
? props.getMenuItems(id, storeState, row, col, items)
: items;
//console.log('GlidlerFormInterface getMenuItems', id, items);
if (!items || items.length === 0) {
return defaultItems || [];
}
return result;
},
[props.onRequestForm, getState]
);
useEffect(() => {
if (mounted && typeof setState === 'function') {
//console.log('GlidlerFormInterface setState getMenuItems1', mounted);
if (getState('getMenuItems') !== getMenuItems) {
setState('getMenuItems', getMenuItems);
}
}
return () => {};
}, [props.getMenuItems, mounted]);
return <></>;
}

View File

@ -5,7 +5,7 @@ import type { APIOptions } from '../utils/types';
import { useGridlerStore } from './Store';
interface LocalDataAdaptorProps extends APIOptions {
interface LocalDataAdaptorProps {
data: Array<unknown>;
}
@ -16,27 +16,23 @@ export const LocalDataAdaptor = React.memo((props: LocalDataAdaptorProps) => {
s.setState,
s.getState,
s.addError,
s.mounted
s.mounted,
]);
const useAPIQuery: (index: number) => Promise<any> = async (index: number) => {
const colSort = getState('colSort');
const pageSize = getState('pageSize');
const colFilters = getState('colFilters');
const _active_requests = getState('_active_requests');
if (!(props.data && Array.isArray(props.data))) {
return [];
}
setState('total_rows', props.data.length);
return props.data.slice(index * (pageSize ?? 50), (index + 1) * (pageSize ?? 50));
setState('total_rows', props.data.length);
return props.data.slice(index * (pageSize ?? 50), (index + 1) * (pageSize ?? 50));
};
useEffect(() => {
setState('useAPIQuery', useAPIQuery);
}, [props.url, props.authtoken, mounted, setState]);
}, [mounted, setState]);
return <></>;
});

View File

@ -22,7 +22,11 @@ import { createSyncStore } from '@warkypublic/zustandsyncstore';
import { produce } from 'immer';
import { type PropsWithChildren, type ReactNode, useEffect } from 'react';
import { type MantineBetterMenuInstance, useMantineBetterMenus } from '../../MantineBetterMenu';
import {
type MantineBetterMenuInstance,
type MantineBetterMenuInstanceItem,
useMantineBetterMenus,
} from '../../MantineBetterMenu';
import { type FormRequestType } from '../utils/types';
import { ColumnFilterSet, type GridlerColumn, type GridlerColumns } from './Column';
import { SortDownSprite } from './sprites/SortDown';
@ -51,7 +55,7 @@ export type FilterOptionOperator =
export interface GridlerProps extends PropsWithChildren {
askAPIRowNumber?: (key: string) => Promise<number>;
columns?: GridlerColumns;
data?: Array<any>;
defaultSort?: Array<SortOption>;
enableOddEvenRowColor?: boolean;
getMenuItems?: (
@ -59,8 +63,8 @@ export interface GridlerProps extends PropsWithChildren {
storeState: GridlerState,
row?: any,
col?: GridlerColumn,
defaultItems?: Array<any>
) => Array<any>;
defaultItems?: Array<MantineBetterMenuInstanceItem>
) => Array<MantineBetterMenuInstanceItem>;
glideProps?: Partial<DataEditorProps>;
headerHeight?: number;
@ -103,6 +107,7 @@ export interface GridlerProps extends PropsWithChildren {
export interface GridlerState {
_active_requests?: Array<{ controller: AbortController; page: number }>;
_activeTooltip?: ReactNode;
_events: EventTarget;
_glideref?: DataEditorRef;
_gridSelection?: GridSelection;
_gridSelectionRows?: GridSelection['rows'];
@ -111,14 +116,15 @@ export interface GridlerState {
_scrollTimeout?: any | number;
_visibleArea: Rectangle;
_visiblePages: Rectangle;
addError: (err: string, ...args: Array<any>) => void;
colFilters?: Array<FilterOption>;
colOrder?: Record<string, number>;
colSize?: Record<string, number>;
colSort?: Array<SortOption>;
data?: Array<any>;
errors: Array<string>;
errors: Array<string>;
focused?: boolean;
get: () => GridlerState;
getCellContent: (cell: Item) => GridCell;
@ -128,8 +134,8 @@ export interface GridlerState {
) => CellArray | GetCellsThunk;
getRowBuffer: (row: number) => any;
getState: <K extends keyof GridlerStoreState>(key: K) => GridlerStoreState[K];
hasLocalData: boolean;
hasLocalData: boolean;
loadingData?: boolean;
loadPage: (page: number, clearMode?: 'all' | 'page') => Promise<void>;
mounted: boolean;
@ -146,6 +152,7 @@ export interface GridlerState {
onHeaderClicked: (colIndex: number, event: HeaderClickedEventArgs) => void;
onHeaderMenuClick: (col: number, screenPosition: Rectangle) => void;
onItemHovered: (args: GridMouseEventArgs) => void;
onVisibleRegionChanged: (
r: Rectangle,
tx: number,
@ -158,7 +165,6 @@ export interface GridlerState {
) => void;
pageSize: number;
reload?: () => Promise<void>;
renderColumns?: GridlerColumns;
setState: <K extends keyof GridlerStoreState>(
@ -179,6 +185,7 @@ export type SortOption = { direction: 'asc' | 'desc'; id: string; order?: number
const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreState, GridlerProps>(
(set, get) => ({
_events: new EventTarget(),
_loadingList: CompactSelection.empty(),
_page_data: {},
_visibleArea: { height: 10000, width: 1000, x: 0, y: 0 },
@ -265,6 +272,15 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
const state = get();
const page = pPage < 0 ? 0 : pPage;
const result = state._events.dispatchEvent(
new CustomEvent('before_loadPage', {
detail: { clearMode, page: pPage, state },
})
);
if (!result) {
return;
}
const damageList: { cell: [number, number] }[] = [];
const colLen = Object.keys(state.renderColumns ?? [1, 2, 3]).length;
const upperPage = state.pageSize * page;
@ -302,9 +318,19 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
}
state._glideref?.updateCells(damageList);
state._events.dispatchEvent(
new CustomEvent('loadPage', {
detail: { clearMode, data, page: pPage, state },
})
);
})
.catch((e) => {
console.warn('loadPage Error: ', page, e);
state._events.dispatchEvent(
new CustomEvent('loadPage_error', {
detail: { clearMode, error: e, page: pPage, state },
})
);
});
}
},
@ -316,6 +342,11 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
//const current = state._editData?.[row];
//if (current === undefined) return;
//todo: complete
state._events.dispatchEvent(
new CustomEvent('onCellEdited', {
detail: { cell, newVal, row, state },
})
);
},
onColumnMoved: (from: number, to: number) => {
const s = get();
@ -396,22 +427,17 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
},
];
s.hideMenu?.(area);
s.showMenu?.(area, {
items:
coldef?.getMenuItems?.(
area,
s,
col && row ? s.getCellContent([col, row]) : undefined,
coldef,
items
) ??
s.getMenuItems?.(
area,
s,
col && row ? s.getCellContent([col, row]) : undefined,
col && row ? s.getRowBuffer(row) : undefined,
coldef,
items
) ??
s.getMenuItems?.(area, s, col && row ? s.getRowBuffer(row) : undefined, coldef, items) ??
items,
x: event.clientX ?? event.bounds?.x,
y: event.clientY ?? event.bounds?.y,
@ -701,9 +727,19 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
window.document.body.appendChild(div);
}
}
getState('_events').dispatchEvent(
new CustomEvent('mounted', {
detail: {},
})
);
return () => {
const onUnMounted = getState('onUnMounted');
setState('mounted', false);
getState('_events').dispatchEvent(
new CustomEvent('unmounted', {
detail: {},
})
);
if (typeof onUnMounted === 'function') {
onUnMounted();
}
@ -742,6 +778,11 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
.then((r) => {
if (r >= 0) {
ref.scrollTo(0, r);
getState('_events').dispatchEvent(
new CustomEvent('selectedRowFound', {
detail: { rowNumber: r, selectedRow: selectedRow },
})
);
}
})
.catch((e) => {
@ -751,6 +792,10 @@ const { Provider, useStore: useGridlerStore } = createSyncStore<GridlerStoreStat
}
}, [props.selectedRow]);
getState('_events').addEventListener('reload', (e: Event) => {
getState('reload')?.();
});
return {
...props,
hasLocalData: props.data && props.data.length > 0,

View File

@ -68,18 +68,19 @@ export const GridlerGoAPIExampleEventlog = () => {
<Gridler
height="100%"
columns={columns}
getMenuItems={(id, _state, row, col, defaultItems) => {
return [
...(defaultItems ?? []),
// {
// id: 'test',
// label: `Test -${id}`,
// onClick: () => {
// console.log('Test clicked', row, col);
// },
// },
];
}}
// getMenuItems={(id, _state, row, col, defaultItems) => {
// console.log('GridlerGoAPIExampleEventlog getMenuItems root', id, row, col, defaultItems);
// return [
// ...(defaultItems ?? []),
// // {
// // id: 'test',
// // label: `Test -${id}`,
// // onClick: () => {
// // console.log('Test clicked', row, col);
// // },
// // },
// ];
// }}
keyField="id_process"
onChange={(v) => {
//console.log('GridlerGoAPIExampleEventlog onChange', v);
@ -91,7 +92,12 @@ export const GridlerGoAPIExampleEventlog = () => {
uniqueid="gridtest"
values={values}
>
<APIAdaptorGoLangv2 authtoken={apiKey} url={`${apiUrl}/public/process`} />
<Gridler.APIAdaptorGoLangv2 authtoken={apiKey} url={`${apiUrl}/public/process`} />
<Gridler.GlidlerFormInterface
onRequestForm={(request, data) => {
console.log('Form requested', request, data);
}}
/>
</Gridler>
<Divider />
<Group>

File diff suppressed because one or more lines are too long