import React from 'react' ; import { Column , RowsUpdateEvent , Position } from 'react-data-grid-temp' ; import { groupBy , isEqual } from 'lodash' ; import { generate } from 'shortid' ; import { Key } from 'rc-tree/lib/interface' ; import { Row , GroupRowData , ColumnProps , TableProps } from './type' ; export type ContextMenu = { row : Row ; rowIdx : number ; column : Column < any , unknown > ; openEditor : boolean ; position : Position ; } ; export function createStore ( props ) { const { onBeforeGroupData = ( datas ) => datas } = props ; const formatData = ( datas ) => datas . map ( ( ele , index ) => ( { ... ele , $index : index + 1 , } ) ) ; return { columns : [ ] as ColumnProps [ ] , visibleColumns : null as string [ ] | null , expandedRowNumber : - 1 , datas : [ ] as Row [ ] , rowEditData : { } as any , groupDatas : [ ] as GroupRowData [ ] | Row [ ] , cacheGroupDatas : undefined as GroupRowData [ ] | Row [ ] | undefined , total : 0 , pageNo : 1 , selectedRows : new Set < Row [ keyof Row ] > ( ) , loading : false , groupColumn : [ ] as string [ ] , expandedKeys : [ ] as string [ ] , contextMenu : { } as ContextMenu , loadRows ( rows , pageNo ) { this . datas = formatData ( rows ) ; this . pageNo = pageNo ; } , param node param targeNode switchColumns ( node , targeNode ) { let column ; let columnIndex ; let targeColumn ; let targeColumnIndex ; this . columns . forEach ( ( value , index ) => { if ( value . name === node ) { column = value ; columnIndex = index ; } if ( value . name === targeNode ) { targeColumn = value ; targeColumnIndex = index ; } } ) ; this . columns . splice ( columnIndex ! , 1 , targeColumn ! ) ; this . columns . splice ( targeColumnIndex ! , 1 , column ! ) ; props . onChangeColumn ?. ( this . columns ) ; } , setSelectedRows ( keys , onSelectedRowsChange ? ) { onSelectedRowsChange ?. ( keys ) ; this . selectedRows = keys ; } , setExpandedKeys ( keys , id ) { this . expandedKeys = keys ; this . groupDatas = this . getGroupDatas ( id ) ; } , reloadRows ( rows ) { this . datas = formatData ( rows ) ; this . pageNo = 1 ; } , getGroupDatas ( id ? ) { const { expandedKeys } = this ; const loops = ( parent , datas , currentLevel ) => { const groupColumns = this . groupColumn ; if ( currentLevel >= groupColumns . length ) { return datas . map ( ele => ( { ... ele , $parent : parent } ) ) ; } const result = [ ] ; const columnName = groupColumns [ currentLevel ] ; const groupData = groupBy ( datas , columnName ) ; Object . keys ( groupData ) . forEach ( key => { const dataNode = { $id : parent === null ? `root/${ generate ( ) }` : `${ parent . $id }/${ generate ( ) }` , $title : key , $type : 'GROUP' , $parent : parent , $column : columnName , $space : ( currentLevel + 1 ) * 20 , $level : currentLevel , } ; result . push ( { ... dataNode , $children : loops ( dataNode , groupData [ key ] , currentLevel + 1 ) } ) ; } ) ; return result ; } ; let groupDatas = this . cacheGroupDatas ; if ( ! groupDatas ) { groupDatas = loops ( null , this . datas , 0 ) ; this . cacheGroupDatas = groupDatas ; } const newData = [ ] ; const historyExpandedKeys = [ ] ; const loopsGroupData = ( loppsData ) => { loppsData . forEach ( ele => { if ( ele . $parent === null ) { newData . push ( ele ) ; } if ( ele . $parent && expandedKeys . includes ( ele . $parent . $id as string ) ) { historyExpandedKeys . push ( ele . $parent . $id as string ) ; newData . push ( ele ) ; } if ( isEqual ( historyExpandedKeys , expandedKeys ) ) { return ; } if ( ele . $children && ele . $children . length > 0 ) { let children = ele . $children ; if ( ele . $id === id ) { children = onBeforeGroupData ( children ) ; } loopsGroupData ( children ) ; } } ) ; } ; loopsGroupData ( id === undefined ? onBeforeGroupData ( groupDatas ! as any [ ] ) : groupDatas ) ; return newData ; } , setGroupColumn ( groupColumn ) { this . groupColumn = groupColumn ; if ( this . groupColumn . length === 0 ) { this . cacheGroupDatas = undefined ; this . expandedKeys = [ ] ; return ; } this . groupDatas = this . getGroupDatas ( ) ; } , setTotal ( total ) { this . total = total ; } , setLoading ( loading ) { this . loading = loading ; } , setContextMenu ( contextMenu ) { this . contextMenu = contextMenu ; } , del ( filter ) { return new Promise < void > ( resolve => { const delIndex = [ ] ; const datas = this . dataSource . filter ( ( ele , index ) => { const isDelete = filter ( ele ) ; if ( isDelete ) { delIndex . push ( index ) ; } return ! isDelete ; } ) ; if ( delIndex . includes ( this . expandedRowNumber ) ) { this . expandedRowNumber = - 1 ; this . setDataSource ( datas . filter ( data => data . $type !== 'FILL' ) ) ; } else { this . setDataSource ( datas ) ; } resolve ( ) ; } ) ; } , add ( rows , start ? ) { const addRows = rows . map ( ele => ( { ... ele , $state : 'CREATE' } ) ) ; this . dataSource . splice ( start || 0 , 0 , ... addRows ) ; this . setDataSource ( [ ... this . dataSource ] ) ; } , update ( change ) { return new Promise < void > ( resolve => { const datas = this . dataSource . map ( ele => { const row = change ( ele ) ; if ( isEqual ( row , ele ) ) { return ele ; } let newState = 'UPDATE' ; if ( ele . $state !== undefined && ele . $state !== 'UPDATE' ) { newState = ele . $state ; } return { ... row , $state : newState } ; } ) ; this . setDataSource ( datas ) ; resolve ( ) ; } ) ; } , commit ( e ) { return new Promise < Row [ ] > ( resolve => { const updates = [ ] ; const rows = this . dataSource ; const { datas } = this ; if ( e . action === 'CELL_UPDATE' || e . action === 'COPY_PASTE' ) { rows [ e . toRow ] = { ... rows [ e . toRow ] , ... ( e . updated as any ) } ; const index = datas . findIndex ( ele => ele . $index === rows [ e . toRow ] . $index ) ; datas [ index ] = { ... rows [ e . toRow ] , ... ( e . updated as any ) } ; updates . push ( { ... rows [ e . toRow ] , ... ( e . updated as any ) } ) ; } if ( e . action === 'CELL_DRAG' ) { const cells = [ ] ; for ( let i = e . fromRow ; i <= e . toRow ; i += 1 ) { cells . push ( i ) ; } rows . forEach ( ( value , index ) => { if ( cells . includes ( index ) ) { const dataIndex = datas . findIndex ( ele => ele . $index === rows [ index ] . $index ) ; datas [ dataIndex ] = { ... rows [ index ] , ... ( e . updated as any ) } ; updates . push ( { ... rows [ index ] , ... ( e . updated as any ) } ) ; } } ) ; } if ( this . isGroup ) { this . cacheGroupDatas = undefined ; } this . setDataSource ( rows . slice ( ) ) ; resolve ( updates ) ; } ) ; } , get isGroup ( ) { return this . groupColumn && this . groupColumn . length > 0 ; } , get dataSource ( ) { const isGroup = this . groupColumn && this . groupColumn . length > 0 ; const data = isGroup ? this . groupDatas : this . datas ; return data ; } , setDataSource ( datas ) { if ( this . isGroup ) { this . groupDatas = datas ; return ; } this . datas = datas ; } , setExpandedRowNumber ( index , count ) { const newDataSource = this . dataSource . filter ( data => data . $type !== 'FILL' ) ; if ( index !== - 1 ) { const fillData = [ ] ; for ( let i = 0 ; i < count ; i += 1 ) { fillData . push ( { $type : 'FILL' } ) ; } newDataSource . splice ( index + 1 , 0 , ... fillData ) ; } this . setDataSource ( newDataSource ) ; this . expandedRowNumber = index ; } , setVisibleColumns ( data ) { this . visibleColumns = data ; props . onChangeColumn ?. ( this . columns . filter ( ele => data ?. includes ( ele . name ) ) ) ; } } ; } export type TableStore = ReturnType < typeof createStore > ; export const StoreContext = React . createContext < TableStore | null > ( null ) ; export const useStore = ( ) => { const store = React . useContext ( StoreContext ) ; if ( ! store ) { throw new Error ( 'useStore must be used within a StoreProvider.' ) ; } return store ; } ;