import { identity } from 'lodash' import PropTypes from 'prop-types' import React, { ReactElement, useContext, useEffect, useState } from 'react' import styled from 'styled-components' import { margin, MarginProps } from 'styled-system' import { useControllableValue } from '../hooks' import { ScreenSizeContext, Size, SIZES } from '../ScreenSizeProvider/ScreenSizeProvider' import { TableVariant } from '../Table/Table' import BottomSection from './BottomSection' import DataGridColumn, { useColumns } from './DataGridColumn' import DataGridTable from './DataGridTable' import { DataGridContext, getMediaQuery, initialPageState } from './helpers' import PageSizeSelect from './PageSizeSelect' import { calcPageState, DataGridPagination, DataGridPrevNext, pageStateReducer } from './Pagination' import TopSection from './TopSection' import { PageStateAction, PaginationOptions, Pagisort, SortOption, TransientProps } from './types' interface DataGridProps extends MarginProps { paginationOptions?: PaginationOptions sortOptions?: SortOption[] initialSort?: SortOption[] onPagisort?: (newPagisort: Pagisort) => void onPageChange?: (newPageOptions: PaginationOptions) => void onSort?: (newSortOptions: SortOption[]) => void children: React.ReactNode fullWidth?: boolean cardBreakpoint?: Size variant?: TableVariant } export const DataGrid = (props: DataGridProps): ReactElement => { const { children, onPagisort, initialSort = [], onSort: onSortProp, onPageChange, fullWidth = false, cardBreakpoint = 'tiny', variant, ...rest } = props const [sortOptions, setSort] = useControllableValue(rest, 'sortOptions', initialSort) const [pageState, setPageState] = useControllableValue( rest, 'paginationOptions', pageStateReducer, initialPageState ) const onSort = (newSortOptions: SortOption[]) => { newSortOptions = setSort(newSortOptions) onSortProp?.(newSortOptions) if (onPagisort) { const pagisort = { ...setPageState(identity), sort: newSortOptions } onPagisort(calcPageState(pagisort)) } } const updatePageState = (action: PageStateAction, raiseEvent = false) => { const newPageOptions = setPageState(action) if (raiseEvent && newPageOptions !== pageState) { onPageChange?.(newPageOptions) if (onPagisort) { const pagisort: Pagisort = { ...newPageOptions, sort: setSort(identity) } onPagisort(pagisort) } } } const [columnState, columnDispatch] = useColumns() const [topSectionRendered, setTopSectionRendered] = useState(false) const size = useContext(ScreenSizeContext) const isCardView = variant === 'card' || (!variant && size <= SIZES[cardBreakpoint]) // Because the sort buttons are a part of the top section and those must always be present for card view, // we can check if TopSection has been rendered and if it has not, we will render it for them. useEffect(() => { setTopSectionRendered(false) React.Children.forEach(children, (child) => { // @ts-ignore if (child && child.type.displayName && child.type.displayName.includes('TopSection')) { setTopSectionRendered(true) } }) }, [children]) return ( {!topSectionRendered && } {children} ) } const StyledDataGrid = styled.div` display: inline-block; flex-direction: column; width: ${(p): string => (p.fullWidth ? '100%' : 'auto')}; overflow-x: auto; ${margin} ${getMediaQuery} { display: inline-flex; } ` DataGrid.propTypes = { paginationOptions: PropTypes.shape({ currentPage: PropTypes.number, pageSize: PropTypes.number, pageCount: PropTypes.number, itemCount: PropTypes.number, itemOffset: PropTypes.number, }), sortOptions: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.string.isRequired, sortAscending: PropTypes.bool.isRequired }) ), onPageChange: PropTypes.func, onSort: PropTypes.func, children: PropTypes.node.isRequired, fullWidth: PropTypes.bool, cardBreakpoint: PropTypes.oneOf(['tiny', 'small', 'medium', 'large', 'extraLarge']), } type DataGridType = React.ComponentType & { Table: typeof DataGridTable DataColumn: typeof DataGridColumn Column: typeof DataGridColumn TopSection: typeof TopSection PageSizeSelect: typeof PageSizeSelect BottomSection: typeof BottomSection Pagination: typeof DataGridPagination PrevNext: typeof DataGridPrevNext } DataGrid.Table = DataGridTable DataGrid.DataColumn = DataGridColumn DataGrid.Column = DataGridColumn DataGrid.TopSection = TopSection DataGrid.PageSizeSelect = PageSizeSelect DataGrid.BottomSection = BottomSection DataGrid.Pagination = DataGridPagination DataGrid.PrevNext = DataGridPrevNext DataGrid.displayName = 'DataGrid' export default DataGrid as DataGridType