Untitled
jsx
19 days ago
15 kB
0
Indexable
Never
import { FC, useEffect, useState } from 'react' import { typographyClasses } from '@mui/material' import { alpha } from '@mui/material/styles' import { DataGrid, GridRenderCellParams, gridClasses } from '@mui/x-data-grid' import { inject, observer } from 'mobx-react' import { useListContext, ListControllerProps, RaRecord, ListControllerResult } from 'react-admin' import { AllProps } from 'appTypes' import { Skeleton } from 'components/Skeletons' import { Action, ActionChildren } from 'components/actions' import { Pagination } from 'components/list' import { ResourceType, useResource } from 'components/resource' import { BoxContainer, Columns } from 'components/styled' import { Flags, useFlags } from 'hooks' import { AuthStore } from 'providers/authStore' import classes from 'theme/classes' import { DatagridActionView, DatagridActionsContainer, GridActionViewProps } from './DatagridAction' import type { DataGridProps } from '@mui/x-data-grid' import type { GridEnrichedColDef, GridColDef } from '@mui/x-data-grid/models/colDef/gridColDef' export type Column<RecordType extends RaRecord = any> = Omit<GridEnrichedColDef, 'renderCell'> & { hidden?: boolean field: keyof RecordType | 'actions' renderCell?: ( params: Parameters<NonNullable<GridEnrichedColDef<RecordType>['renderCell']>>[0], extraParams: { flags: Flags listContext: ListControllerResult<RecordType> }, ) => ReturnType<NonNullable<GridEnrichedColDef['renderCell']>> toExport?: { separate?: Pick<Column<RecordType>, 'field' | 'headerName'>[] } } export const checkboxColumnName = 'checkboxSelectColumn' export type DatagridActionType<RecordType extends RaRecord = any> = // TODO: Action - reduce amount of generics Action<GridRenderCellParams<never, RecordType>, {}, ActionChildren<GridActionViewProps>> type RecordKeys<RecordType extends AllProps> = keyof RecordType | typeof checkboxColumnName export interface DatagridColumnsProps<RecordType extends RaRecord = any> extends Pick<DataGridProps, 'checkboxSelection'> { columns: Column<RecordType>[] actions?: DatagridActionType<RecordType> massColProps?: MassColProps resetColumns?: { [key in RecordKeys<RecordType>]?: boolean } // if undefined sets all visible } export interface DatagridProps<RecordType extends RaRecord = any> extends ListControllerProps, DatagridColumnsProps<RecordType> { visibleColsRef: any preferencesResource: ResourceType initialVisibleColsRef: React.MutableRefObject<{ [key in RecordKeys<RecordType>]?: boolean }> } interface MassColProps extends Pick< GridColDef, 'sortable' | 'filterable' | 'hideable' | 'disableColumnMenu' | 'flex' > {} export type ExportColumnType = [string, string] export const getVisibleColumns = (columns: Column[]) => { return columns.reduce((cols, col) => { const appendColumn = (column: { field: string; headerName?: string }) => { cols.push([column.field, column.headerName || column.field]) } if (col.toExport) { if (col.toExport.separate) { col.toExport.separate.forEach(appendColumn) } } else { appendColumn(col) } return cols }, [] as ExportColumnType[]) } const defaultMassColProps: MassColProps = { sortable: false, filterable: false, disableColumnMenu: true, flex: 1, } const LoadingSkeleton = () => { console.log('here') return ( <Columns> {[...Array(10)].map((_) => ( <Skeleton variant="rectangular" width="100%" height="52px" /> ))} </Columns> ) } const Datagrid: FC<DatagridProps> = inject('auth')( observer( <RecordType extends RaRecord = any>({ columns, massColProps, actions, checkboxSelection, visibleColsRef, auth, preferencesResource, initialVisibleColsRef, ...props }: DatagridProps<RecordType> & { auth?: AuthStore }) => { const listContext = useListContext(props) const { data, isLoading, onSelect, selectedIds, page, setPage, perPage, setPerPage, total, } = listContext console.log(isLoading) const flags = useFlags() const [loading, setLoading] = useState(true) useEffect(() => { if (data && loading) { setLoading(false) } }, [data]) const resource = useResource() const configuredColumns: GridEnrichedColDef<RecordType>[] = columns.map((col) => ({ ...defaultMassColProps, ...massColProps, ...col, renderCell: (value) => col?.renderCell?.(value, { flags, listContext }), })) const handlePageChange = (event: React.ChangeEvent<unknown>, value: number) => { setPage(value) } const handleRowsChange = (event: React.ChangeEvent<unknown>, callback) => { auth.updatePreferences({ resources: { [preferencesResource.name]: { perPage: Number(callback.props.value), }, }, }) setPerPage(callback.props.value) } if (actions) { configuredColumns.push({ field: 'actions', headerName: 'Actions', flex: 1, maxWidth: 122, minWidth: 122, sortable: false, filterable: false, disableColumnMenu: true, hideable: false, renderCell: (cell) => ( <DatagridActionsContainer> {actions(cell as GridRenderCellParams<never, RecordType>, { children: (params) => <DatagridActionView {...params} />, resource, })} </DatagridActionsContainer> ), }) } const arr = new Array(perPage).fill({}).map((v, i) => { return { id: `${i}` } }) const extraClasses = loading ? { [`& .${gridClasses.virtualScroller}`]: { marginTop: '0px !important', }, [`& .${gridClasses.columnHeaders}`]: { display: 'none', }, [`& .${gridClasses.virtualScrollerContent}`]: { padding: '26px 0px', }, [`& .${gridClasses.virtualScrollerRenderZone}`]: { width: '100%', height: '100%', }, } : {} return ( <DataGrid disableSelectionOnClick autoHeight hideFooterSelectedRowCount rowHeight={56} rows={data ? data : arr} components={ loading ? { Row: () => ( <BoxContainer width="100%" height="56px" > {new Array(5).fill(0).map((v, i) => { return ( <Skeleton sx={{ flex: i === 0 ? '1' : '4', height: '8px', }} key={i} /> ) })} </BoxContainer> ), Footer: () => null, } : { Pagination, } } componentsProps={ loading ? undefined : { basePopper: { disablePortal: true, sx: { bottom: '55px !important', top: 'unset !important', transform: data.length < 5 ? 'translateY(50%) !important' : 'unset !important', }, }, pagination: { count: total, component: 'div', page: page - 1, onPageChange: handlePageChange, rowsPerPage: perPage, rowsPerPageOptions: [5, 10, 25, 50, 100], labelDisplayedRows: () => `${page * perPage - perPage + 1}-\ ${total < page * perPage ? total : page * perPage} of ${total}`, onRowsPerPageChange: handleRowsChange, }, } } sx={{ flexGrow: 0, width: '100%', bgcolor: 'white', color: 'text.main', [`& .${gridClasses.checkboxInput}`]: { padding: '0px !important', }, [`& .${gridClasses.cell}`]: { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', '&:focus-within': { outline: 'none', }, [`& .${typographyClasses.root}`]: { whiteSpace: 'inherit', overflow: 'inherit', textOverflow: 'inherit', }, }, [`& .${gridClasses.cellContent}, & .${gridClasses.cell}`]: { '&:empty::before': { content: '"-"', display: 'inline-block', }, }, [`& .${gridClasses.columnHeader}.${gridClasses['columnHeader--sortable']}`]: { padding: '16px', }, [`& .${gridClasses.columnHeader}:focus-within`]: { outline: 'none', }, [`& .${gridClasses.footerContainer}`]: { justifyContent: 'flex-end', }, [`& .${classes.selected}`]: (theme) => ({ backgroundColor: `${alpha( theme.palette.primary.main, 0.08, )} !important`, }), // Loading styles ...extraClasses, [`& .MuiSkeleton-root`]: { marginLeft: '32px', marginRight: '32px', }, }} rowCount={perPage} columns={configuredColumns} columnBuffer={Object.keys(visibleColsRef.current).length} onColumnVisibilityModelChange={(newModel) => { const { __check__, ...newModelRest } = newModel newModelRest[checkboxColumnName] = __check__ auth.updateLocalUserPreferencesByResource( preferencesResource.name, 'visibleColumns', newModelRest, ) auth.syncPreferences() visibleColsRef.current = getVisibleColumns( columns.filter( (column) => newModelRest[column.field] !== false && column.field !== 'actions', ), ) }} initialState={{ columns: { columnVisibilityModel: initialVisibleColsRef.current, }, }} paginationMode="server" rowsPerPageOptions={[1, 5, 10, 25, 100]} pageSize={perPage} checkboxSelection={checkboxSelection} selectionModel={selectedIds} onSelectionModelChange={(selectionModel) => { try { onSelect(selectionModel) } catch { // sometimes this throws an error when selectedIds change during render } }} localeText={{ toolbarColumns: 'MANAGE COLUMNS', checkboxSelectionHeaderName: 'Checkbox Selection', }} /> ) }, ), ) Datagrid.defaultProps = { checkboxSelection: true, actions: () => [], } export default Datagrid