Untitled
unknown
jsx
2 years ago
15 kB
22
Indexable
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
Editor is loading...