Skip to content

Commit

Permalink
Refactor/render top toolbar (#2694)
Browse files Browse the repository at this point in the history
* remove inline function from props to reduce rerenders.

* move top-toolbar into it's own component instead of defining inline for performance gains.

* fix types.

* memoize renderTopToolbar for better performance.

* fix types for build step.

* fix typescript issues and remove redundant proptype validation.

* fix typescript error regarding typing of function.

* fix function signature.

* fix return type of renderTopToolbar function.

* add comment about disabling prop-types.

* remove redundant eslint rule.

* add missing dependencies to useCallBack.

* fix typescript null error.

* capitalize gv.
  • Loading branch information
PVautour authored Jan 27, 2025
1 parent 820c88d commit 6f20959
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 123 deletions.
1 change: 1 addition & 0 deletions packages/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
}
},
"rules": {
"react/prop-types": "off",
"no-useless-constructor": "off",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-unused-vars": "warn",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,84 +7,83 @@ import { SxStyles } from '@/ui/style/types';
* @param {Theme} theme the theme object
* @returns {Object} the sx classes object
*/
export const getSxClasses = (theme: Theme): SxStyles =>
({
dataPanel: { background: theme.palette.geoViewColor.bgColor.main, paddingBottom: '1rem' },
gridContainer: { paddingLeft: '1rem', paddingRight: '1rem' },
selectedRows: {
transition: 'box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
fontWeight: 400,
fontSize: theme.palette.geoViewFontSize.sm,
linHeight: 1.43,
letterSpacing: '0.01071em',
display: 'flex',
padding: '6px',
alignItems: 'center',
export const getSxClasses = (theme: Theme): SxStyles => ({
dataPanel: { background: theme.palette.geoViewColor.bgColor.main, paddingBottom: '1rem' },
gridContainer: { paddingLeft: '1rem', paddingRight: '1rem' },
selectedRows: {
transition: 'box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
fontWeight: 400,
fontSize: theme.palette.geoViewFontSize.sm,
linHeight: 1.43,
letterSpacing: '0.01071em',
display: 'flex',
padding: '6px',
alignItems: 'center',
},
selectedRowsDirection: {
display: 'flex',
flexDirection: 'column',
},
tableCell: { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' },
dataTableWrapper: {
'& .MuiTableContainer-root': {
borderRadius: '6px',
},
selectedRowsDirection: {
display: 'flex',
flexDirection: 'column',
'& .MuiToolbar-root ': {
borderRadius: '6px',
},
tableCell: { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' },
dataTableWrapper: {
'& .MuiTableContainer-root': {
borderRadius: '6px',
},
'& .MuiToolbar-root ': {
borderRadius: '6px',
},
},
filterMap: {
'& .Mui-checked': {
'& .MuiTouchRipple-root': {
color: theme.palette.action.active,
},
},
},
filterMap: {
'& .Mui-checked': {
'& .MuiTouchRipple-root': {
color: theme.palette.geoViewColor.grey.dark[900],
color: theme.palette.action.active,
},
},
tableHead: {
'& th:nth-of-type(-n+3)': {
justifyContent: 'end',
'& .MuiTouchRipple-root': {
color: theme.palette.geoViewColor.grey.dark[900],
},
},
tableHead: {
'& th:nth-of-type(-n+3)': {
justifyContent: 'end',
},
},
tableHeadCell: {
'& .MuiCollapse-wrapperInner': {
'& .MuiBox-root': {
gridTemplateColumns: '1fr',
},
},
tableHeadCell: {
'& .MuiCollapse-wrapperInner': {
'& .MuiBox-root': {
gridTemplateColumns: '1fr',
},
'& .MuiInput-root': { fontSize: theme.palette.geoViewFontSize.sm, '& .MuiSvgIcon-root': { width: '0.75em', height: '0.75em' } },
'& .MuiBadge-root': {
marginLeft: '0.5rem',
'>span': {
width: '100%',
},
'& .MuiInput-root': { fontSize: theme.palette.geoViewFontSize.sm, '& .MuiSvgIcon-root': { width: '0.75em', height: '0.75em' } },
'& .MuiBadge-root': {
marginLeft: '0.5rem',
'>span': {
width: '100%',
},
svg: {
marginTop: '0.25rem',
marginBottom: '0.25rem',
},
'& .keyboard-focused': {
backgroundColor: 'rgba(81, 91, 165, 0.08)',
borderRadius: '50%',
border: `1px solid black !important`,
'> svg': {
opacity: 1,
},
svg: {
marginTop: '0.25rem',
marginBottom: '0.25rem',
},
'& .keyboard-focused': {
backgroundColor: 'rgba(81, 91, 165, 0.08)',
borderRadius: '50%',
border: `1px solid black !important`,
'> svg': {
opacity: 1,
},
},
},
dataTableInstructionsTitle: {
fontSize: theme.palette.geoViewFontSize.lg,
fontWeight: '600',
lineHeight: '1.5em',
},
dataTableInstructionsBody: {
fontSize: theme.palette.geoViewFontSize.sm,
},
rightPanelContainer: {
overflowY: 'auto',
color: theme.palette.geoViewColor.textColor.main,
},
}) as const;
},
dataTableInstructionsTitle: {
fontSize: theme.palette.geoViewFontSize.lg,
fontWeight: '600',
lineHeight: '1.5em',
},
dataTableInstructionsBody: {
fontSize: theme.palette.geoViewFontSize.sm,
},
rightPanelContainer: {
overflowY: 'auto',
color: theme.palette.geoViewColor.textColor.main,
},
});
85 changes: 32 additions & 53 deletions packages/geoview-core/src/core/components/data-table/data-table.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useRef, useState, memo, isValidElement } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState, memo, isValidElement, type ReactNode } from 'react';

import { useTranslation } from 'react-i18next';
import debounce from 'lodash/debounce';
Expand All @@ -18,24 +18,21 @@ import {
MaterialReactTable,
useMaterialReactTable,
type MRT_ColumnDef as MRTColumnDef,
MRT_ToggleDensePaddingButton as MRTToggleDensePaddingButton,
MRT_ShowHideColumnsButton as MRTShowHideColumnsButton,
MRT_ToggleFiltersButton as MRTToggleFiltersButton,
MRT_GlobalFilterTextField as MRTGlobalFilterTextField,
type MRT_SortingState as MRTSortingState,
type MRT_RowVirtualizer as MRTRowVirtualizer,
type MRT_ColumnFiltersState as MRTColumnFiltersState,
type MRT_DensityState as MRTDensityState,
type MRT_ColumnVirtualizer as MRTColumnVirtualizer,
type MRT_TableInstance as MRTTableInstance,
Box,
Button,
IconButton,
Tooltip,
ClearFiltersIcon,
ZoomInSearchIcon,
InfoOutlinedIcon,
} from '@/ui';

import TopToolbar from './top-toolbar';
import { useMapStoreActions } from '@/core/stores/store-interface-and-intial-values/map-state';
import { useLayerStoreActions } from '@/core/stores/store-interface-and-intial-values/layer-state';
import { useDataTableStoreActions, useDataTableLayerSettings } from '@/core/stores/store-interface-and-intial-values/data-table-state';
Expand All @@ -47,9 +44,6 @@ import { logger } from '@/core/utils/logger';
import { TypeFeatureInfoEntry } from '@/geo/map/map-schema-types';
import { useFilterRows, useToolbarActionMessage, useGlobalFilter } from './hooks';
import { getSxClasses } from './data-table-style';
import ExportButton from './export-button';
import JSONExportButton from './json-export-button';
import FilterMap from './filter-map';
import { useLightBox } from '@/core/components/common';
import { NUMBER_FILTER, DATE_FILTER, STRING_FILTER } from '@/core/utils/constant';
import { DataTableProps, ColumnsType } from './data-table-types';
Expand Down Expand Up @@ -103,6 +97,15 @@ function DataTable({ data, layerPath, tableHeight = '500px' }: DataTableProps):

const { enableFocusTrap } = useUIStoreActions();

const handleDensityChange = (updaterOrValue: MRTDensityState | ((prevState: MRTDensityState) => MRTDensityState)): void => {
setDensity(updaterOrValue);
};

const handleToggleColumnFilters = () => {
setShowColumnFilters((prev) => !prev);
setColumnsFiltersVisibility(false, layerPath);
};

/**
* Create table header cell
* @param {string} header value to be displayed in cell
Expand Down Expand Up @@ -400,16 +403,15 @@ function DataTable({ data, layerPath, tableHeight = '500px' }: DataTableProps):
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data.features, handleZoomIn]);

let useTable: MRTTableInstance<ColumnsType> | null = null;
// Create the Material React Table
const useTable = useMaterialReactTable({
useTable = useMaterialReactTable({
columns,
data: rows,
enableDensityToggle: true,
onDensityChange: setDensity,
onShowColumnFiltersChange: () => {
setShowColumnFilters(!showColumnFilters);
setColumnsFiltersVisibility(false, layerPath);
},
onDensityChange: handleDensityChange,
onShowColumnFiltersChange: handleToggleColumnFilters,

// NOTE: showGlobalFilter as true when layer change and we want to show global filter by default
initialState: {
showColumnFilters: datatableSettings[layerPath].columnsFiltersVisibility,
Expand All @@ -432,44 +434,21 @@ function DataTable({ data, layerPath, tableHeight = '500px' }: DataTableProps):
onGlobalFilterChange: setGlobalFilter,
enableBottomToolbar: false,
positionToolbarAlertBanner: 'none', // hide existing row count
renderTopToolbar: ({ table }) => (
<Box display="flex" sx={{ justifyContent: 'space-between', borderBottom: '1px solid #9e9e9e' }} p={4}>
<Box display="flex" sx={{ flexDirection: 'column', justifyContent: 'space-evenly' }}>
<Box sx={sxClasses.selectedRows}>{datatableSettings[layerPath].toolbarRowSelectedMessageRecord}</Box>
<Box display="flex">
<Box sx={sxClasses.selectedRows}>{t('dataTable.filterMap')}</Box>
<FilterMap layerPath={layerPath} isGlobalFilterOn={!!globalFilter?.length} />
</Box>
</Box>
<Box display="flex" sx={{ flexDirection: 'column' }}>
<Box sx={{ float: 'right', marginLeft: 'auto', maxWidth: '15rem' }}>
<MRTGlobalFilterTextField className="buttonOutline" table={table} />
</Box>
<Box display="flex" sx={{ justifyContent: 'space-around' }}>
<IconButton className="buttonOutline" color="primary" onClick={() => useTable.resetColumnFilters()}>
<Tooltip title={t('dataTable.clearFilters')} placement="bottom" arrow>
<ClearFiltersIcon />
</Tooltip>
</IconButton>

<MRTToggleFiltersButton className="buttonOutline" table={table} />
{/* enable column pinning options is override, so that pinning option in menu can be hide. */}
<MRTShowHideColumnsButton
className="buttonOutline"
table={{ ...table, options: { ...table.options, enableColumnPinning: false } }}
/>
<MRTToggleDensePaddingButton className="buttonOutline" table={table} />
{/* Only use filtered rows from material table */}
<ExportButton layerPath={layerPath} rows={useTable.getFilteredRowModel().rows.map((row) => row.original)} columns={columns}>
<JSONExportButton
rows={useTable.getFilteredRowModel().rows.map((row) => row.original)}
features={data.features as TypeFeatureInfoEntry[]}
layerPath={layerPath}
/>
</ExportButton>
</Box>
</Box>
</Box>
renderTopToolbar: useCallback(
(props: { table: MRTTableInstance<ColumnsType> }): ReactNode => (
<TopToolbar
sxClasses={sxClasses}
datatableSettings={datatableSettings}
layerPath={layerPath}
t={t}
globalFilter={globalFilter}
useTable={useTable}
columns={columns}
data={data}
table={props.table}
/>
),
[datatableSettings, layerPath, globalFilter, columns, data, sxClasses, t, useTable] // Include dependencies
),
enableFilterMatchHighlighting: true,
enableColumnResizing: true,
Expand Down
Loading

0 comments on commit 6f20959

Please sign in to comment.