Form interface work
This commit is contained in:
parent
8d26d56599
commit
6350b513ca
@ -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;
|
||||
|
||||
@ -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 <></>;
|
||||
}
|
||||
|
||||
@ -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 <></>;
|
||||
});
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user