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