From 69f37c485a3f9b3d29e73c7de2db1aa705b7309c Mon Sep 17 00:00:00 2001 From: LE SAULNIER Kevin Date: Fri, 5 Apr 2024 14:41:04 +0200 Subject: [PATCH 1/2] fix: POC Aggrid directory-content Signed-off-by: LE SAULNIER Kevin --- src/components/custom-aggrid.tsx | 128 +++++++++++ src/components/directory-content.js | 317 +++++++++++++++------------- 2 files changed, 304 insertions(+), 141 deletions(-) create mode 100644 src/components/custom-aggrid.tsx diff --git a/src/components/custom-aggrid.tsx b/src/components/custom-aggrid.tsx new file mode 100644 index 000000000..e55cc0e07 --- /dev/null +++ b/src/components/custom-aggrid.tsx @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React, { useCallback } from 'react'; +import { Theme, useTheme } from '@mui/material'; +import { AgGridReact, AgGridReactProps } from 'ag-grid-react'; +import { useIntl } from 'react-intl'; +import 'ag-grid-community/styles/ag-grid.css'; +import 'ag-grid-community/styles/ag-theme-alpine.css'; +import { ColumnResizedEvent, GetLocaleTextParams } from 'ag-grid-community'; +import { Box } from '@mui/system'; +import { mergeSx } from '../utils/functions'; + +interface CustomAGGGridStyleProps { + shouldHidePinnedHeaderRightBorder?: boolean; + showOverlay?: boolean; +} + +interface CustomAGGridProps extends AgGridReactProps, CustomAGGGridStyleProps {} + +const styles = { + grid: (theme: Theme) => ({ + width: 'auto', + height: '100%', + position: 'relative', + + // '--ag-value-change-value-highlight-background-color': + // theme.aggridValueChangeHighlightBackgroundColor, + + //overrides the default computed max heigt for ag grid default selector editor to make it more usable + //can be removed if a custom selector editor is implemented + '& .ag-select-list': { + maxHeight: '300px !important', + }, + + //allows to hide the scrollbar in the pinned rows section as it is unecessary to our implementation + '& .ag-body-horizontal-scroll:not(.ag-scrollbar-invisible) .ag-horizontal-left-spacer:not(.ag-scroller-corner)': + { + visibility: 'hidden', + }, + //removes border on focused cell - using "suppressCellFocus" Aggrid option causes side effects and breaks field edition + '& .ag-cell-focus, .ag-cell': { + border: 'none !important', + }, + }), + noBorderRight: { + // hides right border for header of "Edit" column due to column being pinned + '& .ag-pinned-left-header': { + borderRight: 'none', + }, + }, + overlayBackground: (theme: Theme) => ({ + // '& .ag-overlay-loading-wrapper': { + // background: theme.overlay.background, + // }, + '& .ag-overlay-no-rows-wrapper': { + background: 'none', + }, + }), +}; + +// We have to define a minWidth to column to activate this feature +const onColumnResized = (params: ColumnResizedEvent) => { + const { column, finished } = params; + const colDefinedMinWidth = column?.getColDef()?.minWidth; + if (column && colDefinedMinWidth && finished) { + const newWidth = column?.getActualWidth(); + if (newWidth < colDefinedMinWidth) { + column?.setActualWidth(colDefinedMinWidth); + } + } +}; + +export const CustomAGGrid = React.forwardRef( + (props, ref) => { + const { + shouldHidePinnedHeaderRightBorder = false, + overlayNoRowsTemplate, + loadingOverlayComponent, + loadingOverlayComponentParams, + showOverlay = false, + } = props; + const theme = useTheme(); + const intl = useIntl(); + + const GRID_PREFIX = 'grid.'; + + const getLocaleText = useCallback( + (params: GetLocaleTextParams) => { + const key = GRID_PREFIX + params.key; + return intl.formatMessage({ + id: key, + defaultMessage: params.defaultValue, + }); + }, + [intl] + ); + + return ( + + + + ); + } +); diff --git a/src/components/directory-content.js b/src/components/directory-content.js index 1658b5716..abdf4d0ef 100644 --- a/src/components/directory-content.js +++ b/src/components/directory-content.js @@ -27,7 +27,7 @@ import { OverflowableText, useSnackMessage, } from '@gridsuite/commons-ui'; -import { Box, Checkbox } from '@mui/material'; +import { Box, Checkbox, Grid } from '@mui/material'; import { fetchElementsInfos } from '../utils/rest-api'; import CriteriaBasedFilterEditionDialog from './dialogs/filter/criteria-based/criteria-based-filter-edition-dialog'; @@ -44,6 +44,8 @@ import ExpertFilterEditionDialog from './dialogs/filter/expert/expert-filter-edi import { noSelectionForCopy } from 'utils/constants'; import DescriptionModificationDialogue from './dialogs/description-modification/description-modification-dialogue'; import { useMultiselect } from 'utils/use-multiselect'; +import CustomAgGridTable from './utils/rhf-inputs/ag-grid-table-rhf/custom-ag-grid-table'; +import { CustomAGGrid } from './custom-aggrid'; const circularProgressSize = '70px'; @@ -52,16 +54,16 @@ const styles = { color: theme.link.color, textDecoration: 'none', }), - cell: { + grid: { height: 500, width: '100%' }, + tableCell: (theme) => ({ + fontSize: 'small', display: 'flex', alignItems: 'center', - textAlign: 'center', - boxSizing: 'border-box', - flex: 1, - height: '48px', - padding: `${DEFAULT_CELL_PADDING}px`, - overflow: 'hidden', + }), + overflow: { + whiteSpace: 'pre', textOverflow: 'ellipsis', + overflow: 'hidden', }, clickable: { cursor: 'pointer', @@ -167,12 +169,14 @@ const DirectoryContent = () => { [currentChildren] ); + const [selectedIds, setSelectedIds] = useState([]); + const { - selectedIds, + selectedIdss, toggleSelection, toggleSelectAll, handleShiftAndCtrlClick, - clearSelection, + clearSelections, } = useMultiselect(currentChildrenUuids); const appsAndUrls = useSelector((state) => state.appsAndUrls); @@ -301,12 +305,16 @@ const DirectoryContent = () => { ); const contextualMixPolicy = contextualMixPolicies.ALL; + const clearSelection = () => { + gridRef.current.api.forEachNode((node) => node.setSelected(false)); + }; + const onContextMenu = useCallback( (event) => { const element = currentChildren?.find( (e) => - event.rowData && // check if right click is made out of table in order to prevent bug when right clicking out of the table when an element is uploading - e.elementUuid === event.rowData?.elementUuid + event.data && // check if right click is made out of table in order to prevent bug when right clicking out of the table when an element is uploading + e.elementUuid === event.data?.elementUuid ); if (selectedDirectory) { @@ -317,10 +325,10 @@ const DirectoryContent = () => { if (element.type !== 'DIRECTORY') { setActiveElement({ hasMetadata: - childrenMetadata[event.rowData.elementUuid] !== + childrenMetadata[event.data.elementUuid] !== undefined, specificMetadata: - childrenMetadata[event.rowData.elementUuid] + childrenMetadata[event.data.elementUuid] ?.specificMetadata, ...element, }); @@ -341,7 +349,7 @@ const DirectoryContent = () => { element?.elementUuid && !selectedIds.includes(element.elementUuid) ) { - toggleSelection(element.elementUuid); + event.node.setSelected(true); } } } @@ -366,7 +374,6 @@ const DirectoryContent = () => { contextualMixPolicies, contextualMixPolicy, childrenMetadata, - toggleSelection, clearSelection, ] ); @@ -412,24 +419,10 @@ const DirectoryContent = () => { [appsAndUrls] ); - const handleClickElementCheckbox = useCallback( - (clickEvent, elementUuid) => { - clickEvent.stopPropagation(); - if (clickEvent.shiftKey) { - // if row is clicked while shift is pressed, range of rows selection is toggled - handleShiftAndCtrlClick(clickEvent, elementUuid); - // nothing else happens, hence the return - return; - } - - toggleSelection(elementUuid); - }, - [handleShiftAndCtrlClick, toggleSelection] - ); - const handleRowClick = useCallback( (clickEvent) => { - const clickedElementUuid = clickEvent.rowData.elementUuid; + console.log('ROW CLICKED', clickEvent); + const clickedElementUuid = clickEvent.data.elementUuid; const element = currentChildren.find( (e) => e.elementUuid === clickedElementUuid ); @@ -439,11 +432,11 @@ const DirectoryContent = () => { return; } - if (clickEvent.event?.shiftKey || clickEvent.event?.ctrlKey) { - handleShiftAndCtrlClick(clickEvent.event, clickedElementUuid); - // nothing else happens, hence the return - return; - } + // if (clickEvent.event?.shiftKey || clickEvent.event?.ctrlKey) { + // handleShiftAndCtrlClick(clickEvent.event, clickedElementUuid); + // // nothing else happens, hence the return + // return; + // } setElementName(childrenMetadata[element.elementUuid]?.name); const subtype = childrenMetadata[element.elementUuid].subtype; @@ -539,19 +532,17 @@ const DirectoryContent = () => { ); const typeCellRender = useCallback((cellData) => { - const { rowData = {} } = cellData || {}; + const { data = {} } = cellData || {}; return ( - + ); }, []); function userCellRender(cellData) { - const user = cellData.rowData[cellData.dataKey]; + const user = cellData.data[cellData.colDef.field]; + console.log('USER', user, cellData); return ( @@ -565,7 +556,8 @@ const DirectoryContent = () => { } function dateCellRender(cellData) { - const data = new Date(cellData.rowData[cellData.dataKey]); + console.log('ROWDATA', { ...cellData }); + const data = new Date(cellData.data[cellData.colDef.field]); if (data instanceof Date && !isNaN(data)) { const cellMidnight = new Date(data).setHours(0, 0, 0, 0); @@ -600,7 +592,7 @@ const DirectoryContent = () => { const descriptionCellRender = useCallback( (cellData) => { const element = currentChildren.find( - (e) => e.elementUuid === cellData.rowData.elementUuid + (e) => e.elementUuid === cellData.data.elementUuid ); const description = element.description; @@ -611,7 +603,10 @@ const DirectoryContent = () => { const tooltip = descriptionLines?.join('\n'); const handleDescriptionIconClick = (clickEvent) => { + console.log('ROW CLICKED 22', clickEvent); clickEvent.stopPropagation(); + clickEvent.preventDefault(); + clickEvent.nativeEvent.stopImmediatePropagation(); if (clickEvent.shiftKey || clickEvent.ctrlKey) { handleShiftAndCtrlClick(clickEvent, element?.elementUuid); // nothing else happens, hence the return @@ -643,18 +638,14 @@ const DirectoryContent = () => { onClick={handleDescriptionIconClick} /> ); - return ( - <> - {icon} - - ); + return {icon}; }, [currentChildren, handleShiftAndCtrlClick] ); const getDisplayedElementName = useCallback( (cellData) => { - const { elementName, uploading, elementUuid } = cellData.rowData; + const { elementName, uploading, elementUuid } = cellData.data; const formatMessage = intl.formatMessage; if (uploading) { return elementName + '\n' + formatMessage({ id: 'uploading' }); @@ -679,12 +670,13 @@ const DirectoryContent = () => { const nameCellRender = useCallback( (cellData) => { + console.log('CELLDATA', cellData); + const element = currentChildren.find( - (e) => e.elementUuid === cellData.rowData.elementUuid + (e) => e.elementUuid === cellData.data.elementUuid ); return ( - - {/* Icon */} + {!childrenMetadata[element.elementUuid] && isElementCaseOrStudy(element.type) && ( { )} {childrenMetadata[element.elementUuid] && getFileIcon(element.type, styles.icon)} - {/* Name */} - + + + ); }, [childrenMetadata, currentChildren, getDisplayedElementName] ); - function selectionHeaderRenderer() { - return ( - { - toggleSelectAll(); - e.stopPropagation(); - }} - sx={styles.checkboxes} - > - 0} - indeterminate={ - selectedIds.length !== 0 && - selectedIds.length !== currentChildren.length - } - /> - - ); - } - - function selectionRenderer(cellData) { - const elementUuid = cellData.rowData['elementUuid']; - const isUploading = cellData.rowData['uploading']; - return ( - - handleClickElementCheckbox(clickEvent, elementUuid) - } - sx={styles.checkboxes} - > - - - ); - } - useEffect(() => { if (!selectedDirectory) { return; @@ -832,6 +793,7 @@ const DirectoryContent = () => { () => currentChildren?.map((child) => ({ ...child, + selected: true, type: childrenMetadata[child.elementUuid] && getElementTypeTranslation( @@ -878,6 +840,78 @@ const DirectoryContent = () => { ); }; + const formatCell = (props) => { + let value = props?.valueFormatted || props.value; + let tooltipValue = undefined; + if (props.colDef.valueGetter) { + value = props?.context?.network + ? props.colDef.valueGetter(props, props.context.network) + : props.colDef.valueGetter(props); + } + if (props.colDef.normed) { + value = props.colDef.normed(props.fluxConvention, value); + } + if ( + value != null && + props.colDef.numeric && + props.colDef.fractionDigits + ) { + // only numeric rounded cells have a tooltip (their raw numeric value) + tooltipValue = value; + value = parseFloat(value).toFixed(props.colDef.fractionDigits); + } + if (props.colDef.numeric && isNaN(value)) { + value = null; + } + return { value: value, tooltip: tooltipValue }; + }; + + const DefaultCellRenderer = (props) => { + const cellValue = formatCell(props); + return ( + + + + + + ); + }; + + const gridRef = useRef(); + + const onSelectionChanged = useCallback(() => { + var selectedRows = gridRef.current.api.getSelectedRows(); + var selectedRowsString = ''; + var maxToShow = 5; + selectedRows.forEach(function (selectedRow, index) { + console.log('SELECTED', selectedRow); + }); + + setSelectedIds(selectedRows.map((row) => row.elementUuid)); + }, []); + + const defaultColDef = useMemo( + () => ({ + sortable: true, + resizable: false, + lockPinned: true, + wrapHeaderText: true, + autoHeaderHeight: true, + }), + [] + ); + + const onGridReady = useCallback(({ api }) => { + api?.sizeColumnsToFit(); + }, []); + + console.log('ROWS', rows); const renderTableContent = () => { return ( <> @@ -888,77 +922,85 @@ const DirectoryContent = () => { selectedIds?.length > 0 ? getSelectedChildren() : [] } /> - onContextMenu(e)} - onRowClick={handleRowClick} - rows={rows} - columns={[ + { + console.log( + 'CHACKING', + cellData, + cellData.data?.notClickable !== true + ); + if (cellData.data?.notClickable !== true) { + return { cursor: 'pointer' }; + } + }} + onSelectionChanged={onSelectionChanged} /> ); @@ -1084,16 +1126,9 @@ const DirectoryContent = () => { return ( <> -
onContextMenu(e)} - > + {renderContent()} -
+
{ From b8b57c0f2d52a361925170b0e1a7277026009cc9 Mon Sep 17 00:00:00 2001 From: LE SAULNIER Kevin Date: Mon, 8 Apr 2024 09:25:15 +0200 Subject: [PATCH 2/2] fix: improve style Signed-off-by: LE SAULNIER Kevin --- src/components/custom-aggrid.tsx | 2 +- src/components/directory-content.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/custom-aggrid.tsx b/src/components/custom-aggrid.tsx index e55cc0e07..8524ce0cb 100644 --- a/src/components/custom-aggrid.tsx +++ b/src/components/custom-aggrid.tsx @@ -70,7 +70,7 @@ const onColumnResized = (params: ColumnResizedEvent) => { if (column && colDefinedMinWidth && finished) { const newWidth = column?.getActualWidth(); if (newWidth < colDefinedMinWidth) { - column?.setActualWidth(colDefinedMinWidth); + column?.setActualWidth(colDefinedMinWidth, params.source); } } }; diff --git a/src/components/directory-content.js b/src/components/directory-content.js index abdf4d0ef..6a8fc5749 100644 --- a/src/components/directory-content.js +++ b/src/components/directory-content.js @@ -638,7 +638,11 @@ const DirectoryContent = () => { onClick={handleDescriptionIconClick} /> ); - return {icon}; + return ( + + {icon} + + ); }, [currentChildren, handleShiftAndCtrlClick] );