Untitled

mail@pastecode.io avatarunknown
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