From c7cac761e2f6ea8df5afbf5007ea1bf2328e7941 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 7 May 2024 09:58:41 +0200 Subject: [PATCH 01/46] Migrate directory content to ag grid --- package-lock.json | 7 +- package.json | 2 +- src/components/directory-content.jsx | 821 +++++++--------------- src/hooks/useDirectoryContent.ts | 85 +++ src/redux/reducer.type.ts | 20 +- src/utils/directory-content-renderers.tsx | 233 ++++++ 6 files changed, 583 insertions(+), 585 deletions(-) create mode 100644 src/hooks/useDirectoryContent.ts create mode 100644 src/utils/directory-content-renderers.tsx diff --git a/package-lock.json b/package-lock.json index cd5f4a6a7..690e86699 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@gridsuite/commons-ui": "0.57.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.57.0.tgz", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "5.0.0-alpha.169", @@ -2955,8 +2955,9 @@ }, "node_modules/@gridsuite/commons-ui": { "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.57.0.tgz", - "integrity": "sha512-OkaUmjoNAK/g79Hc+0PajVF+nJro+uxGXr032Ar9zcDurT3emOcn+VvenQszb7Brl+9ZAUfRKMsmvUL8aiu/aw==", + "resolved": "file:../commons-ui/gridsuite-commons-ui-0.57.0.tgz", + "integrity": "sha512-WdFjlIygxsfy1m4+N4GzTuTXs3lbTv3l/j18cm8ARUnhd2xHvgvhyW2zxn7fLnIt/UOCyJt5AQSGG/se1B9B1w==", + "license": "MPL-2.0", "dependencies": { "@react-querybuilder/dnd": "^7.2.0", "@react-querybuilder/material": "^7.2.0", diff --git a/package.json b/package.json index 342359949..ee9513a0b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@gridsuite/commons-ui": "0.57.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.57.0.tgz", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "5.0.0-alpha.169", diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 9968bd446..8a30711ac 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -11,27 +11,21 @@ import { setActiveDirectory, setSelectionForCopy } from '../redux/actions'; import { FormattedMessage, useIntl } from 'react-intl'; import * as constants from '../utils/UIconstants'; - -import Chip from '@mui/material/Chip'; -import Tooltip from '@mui/material/Tooltip'; import CircularProgress from '@mui/material/CircularProgress'; import FolderOpenRoundedIcon from '@mui/icons-material/FolderOpenRounded'; -import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined'; -import VirtualizedTable from './virtualized-table'; import { ContingencyListType, FilterType } from '../utils/elementType'; import { ElementType, - DEFAULT_CELL_PADDING, - getFileIcon, - OverflowableText, useSnackMessage, ExplicitNamingFilterEditionDialog, ExpertFilterEditionDialog, CriteriaBasedFilterEditionDialog, DescriptionModificationDialog, + CustomAGGrid, + noSelectionForCopy, } from '@gridsuite/commons-ui'; -import { Box, Checkbox } from '@mui/material'; +import { Box } from '@mui/material'; import { createFilter, @@ -42,18 +36,26 @@ import { saveFilter, fetchDirectoryContent, fetchRootFolders, + updateElement, } from '../utils/rest-api'; import ContentContextualMenu from './menus/content-contextual-menu'; import ContentToolbar from './toolbars/content-toolbar'; import DirectoryTreeContextualMenu from './menus/directory-tree-contextual-menu'; -import CreateIcon from '@mui/icons-material/Create'; import CriteriaBasedEditionDialog from './dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog'; import ExplicitNamingEditionDialog from './dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog'; import ScriptEditionDialog from './dialogs/contingency-list/edition/script/script-edition-dialog'; -import { noSelectionForCopy } from 'utils/constants'; import { useParameterState } from './dialogs/parameters-dialog'; import { PARAM_LANGUAGE } from '../utils/config-params'; +import Grid from '@mui/material/Grid'; +import { + TypeCellRenderer, + DescriptionCellRenderer, + UserCellRenderer, + DateCellRenderer, + NameCellRenderer, +} from '../utils/directory-content-renderers.tsx'; +import { useDirectoryContent } from '../hooks/useDirectoryContent'; const circularProgressSize = '70px'; @@ -62,28 +64,12 @@ const styles = { color: theme.link.color, textDecoration: 'none', }), - cell: { - display: 'flex', - alignItems: 'center', - textAlign: 'center', - boxSizing: 'border-box', - flex: 1, - height: '48px', - padding: `${DEFAULT_CELL_PADDING}px`, - overflow: 'hidden', + grid: { height: 500, width: '100%' }, + overflow: { + whiteSpace: 'pre', textOverflow: 'ellipsis', + overflow: 'hidden', }, - chip: { - cursor: 'pointer', - }, - icon: (theme) => ({ - marginRight: theme.spacing(1), - width: '18px', - height: '18px', - }), - circularRoot: (theme) => ({ - marginRight: theme.spacing(1), - }), checkboxes: { width: '100%', justifyContent: 'center', @@ -98,17 +84,6 @@ const styles = { centeredCircularProgress: { alignSelf: 'center', }, - tooltip: { - maxWidth: '1000px', - }, - descriptionTooltip: { - display: 'inline-block', - whiteSpace: 'pre', - textOverflow: 'ellipsis', - overflow: 'hidden', - maxWidth: '250px', - maxHeight: '50px', - }, }; const initialMousePosition = { @@ -122,6 +97,7 @@ const DirectoryContent = () => { const selectionForCopy = useSelector((state) => state.selectionForCopy); const activeDirectory = useSelector((state) => state.activeDirectory); + const currentChildren = useSelector((state) => state.currentChildren); const [languageLocal] = useParameterState(PARAM_LANGUAGE); @@ -169,23 +145,20 @@ const DirectoryContent = () => { }; return broadcast; }); - const [childrenMetadata, setChildrenMetadata] = useState({}); - - const [selectedUuids, setSelectedUuids] = useState(new Set()); - - const currentChildren = useSelector((state) => state.currentChildren); - const currentChildrenRef = useRef(); - currentChildrenRef.current = currentChildren; const appsAndUrls = useSelector((state) => state.appsAndUrls); const selectedDirectory = useSelector((state) => state.selectedDirectory); const [activeElement, setActiveElement] = useState(null); const [isMissingDataAfterDirChange, setIsMissingDataAfterDirChange] = - useState(true); + useState(false); const intl = useIntl(); - const todayStart = new Date().setHours(0, 0, 0, 0); + const gridRef = useRef(); + const [data, childrenMetadata] = useDirectoryContent( + gridRef, + setIsMissingDataAfterDirChange + ); /* Menu states */ const [mousePosition, setMousePosition] = useState(initialMousePosition); @@ -293,66 +266,48 @@ const DirectoryContent = () => { event.stopPropagation(); }; - /* User interactions */ - const contextualMixPolicies = useMemo( - () => ({ - BIG: 'GoogleMicrosoft', // if !selectedUuids.has(selected.Uuid) deselects selectedUuids - ALL: 'All', // union of activeElement.Uuid and selectedUuids (currently implemented) - }), - [] - ); - const contextualMixPolicy = contextualMixPolicies.ALL; + const isRowHovered = () => { + return !!gridRef.current?.api + ?.getRenderedNodes() + .find((node) => node.hovered); + }; - const onContextMenu = useCallback( + const onCellContextMenu = useCallback( (event) => { - const element = currentChildren?.find( - (e) => e.elementUuid === event.rowData?.elementUuid + const element = data?.find( + (e) => + 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) { - dispatch(setActiveDirectory(selectedDirectory.elementUuid)); - } - if (element && element.uploading !== null) { 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, }); - - if (contextualMixPolicy === contextualMixPolicies.BIG) { - // If some elements were already selected and the active element is not in them, we deselect the already selected elements. - if ( - selectedUuids?.size && - element?.elementUuid && - !selectedUuids.has(element.elementUuid) - ) { - setSelectedUuids(new Set()); - } - } else { - // If some elements were already selected, we add the active element to the selected list if not already in it. - if ( - selectedUuids?.size && - element?.elementUuid && - !selectedUuids.has(element.elementUuid) - ) { - let updatedSelectedUuids = new Set(selectedUuids); - updatedSelectedUuids.add(element.elementUuid); - setSelectedUuids(updatedSelectedUuids); - } - } } setMousePosition({ mouseX: event.event.clientX + constants.HORIZONTAL_SHIFT, mouseY: event.event.clientY + constants.VERTICAL_SHIFT, }); handleOpenContentMenu(event.event); - } else { + } + }, + [childrenMetadata, data] + ); + + const onContextMenu = useCallback( + (event) => { + if (!isRowHovered()) { + if (selectedDirectory) { + dispatch(setActiveDirectory(selectedDirectory.elementUuid)); + } setMousePosition({ mouseX: event.clientX + constants.HORIZONTAL_SHIFT, mouseY: event.clientY + constants.VERTICAL_SHIFT, @@ -360,26 +315,9 @@ const DirectoryContent = () => { handleOpenDirectoryMenu(event); } }, - [ - currentChildren, - dispatch, - selectedDirectory, - selectedUuids, - contextualMixPolicies, - contextualMixPolicy, - childrenMetadata, - ] + [dispatch, selectedDirectory] ); - const abbreviationFromUserName = (name) => { - const tab = name.split(' ').map((x) => x.charAt(0)); - if (tab.length === 1) { - return tab[0]; - } else { - return tab[0] + tab[tab.length - 1]; - } - }; - const handleError = useCallback( (message) => { snackError({ @@ -412,333 +350,108 @@ const DirectoryContent = () => { [appsAndUrls] ); - const handleRowClick = useCallback( + const handleDescriptionIconClick = (e) => { + setActiveElement(e.data); + setOpenDescModificationDialog(true); + }; + + //TODO DEAL WITH THIS RERENDERING BASTARD + const handleCellClick = useCallback( (event) => { - const element = currentChildren.find( - (e) => e.elementUuid === event.rowData.elementUuid - ); - if (childrenMetadata[element.elementUuid] !== undefined) { - setElementName(childrenMetadata[element.elementUuid]?.name); - const subtype = childrenMetadata[element.elementUuid].subtype; - /** set active directory on the store because it will be used while editing the contingency name */ - dispatch(setActiveDirectory(selectedDirectory?.elementUuid)); - switch (element.type) { - case ElementType.STUDY: - let url = getLink(element.elementUuid, element.type); - url - ? window.open(url, '_blank') - : handleError( - intl.formatMessage( - { id: 'getAppLinkError' }, - { type: element.type } - ) - ); - break; - case ElementType.CONTINGENCY_LIST: - if (subtype === ContingencyListType.CRITERIA_BASED.id) { - setCurrentFiltersContingencyListId( - element.elementUuid - ); - setOpenDialog(subtype); - } else if (subtype === ContingencyListType.SCRIPT.id) { - setCurrentScriptContingencyListId( - element.elementUuid - ); - setOpenDialog(subtype); - } else if ( - subtype === ContingencyListType.EXPLICIT_NAMING.id - ) { - setCurrentExplicitNamingContingencyListId( - element.elementUuid - ); - setOpenDialog(subtype); - } - break; - case ElementType.FILTER: - if (subtype === FilterType.EXPLICIT_NAMING.id) { - setCurrentExplicitNamingFilterId( - element.elementUuid - ); - setOpenDialog(subtype); - } else if (subtype === FilterType.CRITERIA_BASED.id) { - setCurrentCriteriaBasedFilterId( - element.elementUuid + if (event.colDef.field === 'description') { + handleDescriptionIconClick(event); + } else { + if (childrenMetadata[event.data.elementUuid] !== undefined) { + setElementName( + childrenMetadata[event.data.elementUuid]?.elementName + ); + const subtype = + childrenMetadata[event.data.elementUuid] + .specificMetadata.type; + /** set active directory on the store because it will be used while editing the contingency name */ + dispatch( + setActiveDirectory(selectedDirectory?.elementUuid) + ); + switch (event.data.type) { + case ElementType.STUDY: + let url = getLink( + event.data.elementUuid, + event.data.type ); - setOpenDialog(subtype); - } else if (subtype === FilterType.EXPERT.id) { - setCurrentExpertFilterId(element.elementUuid); - setOpenDialog(subtype); - } - break; - default: - break; + url + ? window.open(url, '_blank') + : handleError( + intl.formatMessage( + { id: 'getAppLinkError' }, + { type: event.data.type } + ) + ); + break; + case ElementType.CONTINGENCY_LIST: + if ( + subtype === + ContingencyListType.CRITERIA_BASED.id + ) { + setCurrentFiltersContingencyListId( + event.data.elementUuid + ); + setOpenDialog(subtype); + } else if ( + subtype === ContingencyListType.SCRIPT.id + ) { + setCurrentScriptContingencyListId( + event.data.elementUuid + ); + setOpenDialog(subtype); + } else if ( + subtype === + ContingencyListType.EXPLICIT_NAMING.id + ) { + setCurrentExplicitNamingContingencyListId( + event.data.elementUuid + ); + setOpenDialog(subtype); + } + break; + case ElementType.FILTER: + if (subtype === FilterType.EXPLICIT_NAMING.id) { + setCurrentExplicitNamingFilterId( + event.data.elementUuid + ); + setOpenDialog(subtype); + } else if ( + subtype === FilterType.CRITERIA_BASED.id + ) { + setCurrentCriteriaBasedFilterId( + event.data.elementUuid + ); + setOpenDialog(subtype); + } else if (subtype === FilterType.EXPERT.id) { + setCurrentExpertFilterId( + event.data.elementUuid + ); + setOpenDialog(subtype); + } + break; + default: + break; + } } } }, [ childrenMetadata, - currentChildren, dispatch, getLink, handleError, intl, - selectedDirectory?.elementUuid, + selectedDirectory, ] ); - const getElementTypeTranslation = useCallback( - (type, subtype, formatCase) => { - let translatedType; - switch (type) { - case ElementType.FILTER: - case ElementType.CONTINGENCY_LIST: - translatedType = intl.formatMessage({ - id: subtype + '_' + type, - }); - break; - case ElementType.MODIFICATION: - translatedType = - intl.formatMessage({ id: type }) + - ' (' + - intl.formatMessage({ - id: 'network_modifications.' + subtype, - }) + - ')'; - break; - default: - translatedType = intl.formatMessage({ id: type }); - break; - } - - const translatedFormat = formatCase - ? ' (' + intl.formatMessage({ id: formatCase }) + ')' - : ''; - - return `${translatedType}${translatedFormat}`; - }, - [intl] - ); - - const typeCellRender = useCallback((cellData) => { - const { rowData = {} } = cellData || {}; - return ( - - - - ); - }, []); - - function userCellRender(cellData) { - const user = cellData.rowData[cellData.dataKey]; - return ( - - - - - - ); - } - - function dateCellRender(cellData) { - const data = new Date(cellData.rowData[cellData.dataKey]); - if (data instanceof Date && !isNaN(data)) { - const cellMidnight = new Date(data).setHours(0, 0, 0, 0); - - const time = new Intl.DateTimeFormat(intl.locale, { - timeStyle: 'medium', - hour12: false, - }).format(data); - const displayedDate = - intl.locale === 'en' - ? data.toISOString().substring(0, 10) - : data.toLocaleDateString(intl.locale); - const cellText = todayStart === cellMidnight ? time : displayedDate; - const fullDate = new Intl.DateTimeFormat(intl.locale, { - dateStyle: 'long', - timeStyle: 'long', - hour12: false, - }).format(data); - - return ( - - - {cellText} - - - ); - } - } - const [openDescModificationDialog, setOpenDescModificationDialog] = useState(false); - const descriptionCellRender = useCallback( - (cellData) => { - const element = currentChildren.find( - (e) => e.elementUuid === cellData.rowData.elementUuid - ); - - const description = element.description; - const descriptionLines = description?.split('\n'); - if (descriptionLines?.length > 3) { - descriptionLines[2] = '...'; - } - const tooltip = descriptionLines?.join('\n'); - - const handleDescriptionIconClick = (e) => { - setActiveElement(element); - setOpenDescModificationDialog(true); - e.stopPropagation(); - }; - - const icon = description ? ( - - } - placement="right" - > - - - ) : ( - - ); - return ( - <> - {icon} - - ); - }, - [currentChildren] - ); - - const getDisplayedElementName = useCallback( - (cellData) => { - const { elementName, uploading, elementUuid } = cellData.rowData; - const formatMessage = intl.formatMessage; - if (uploading) { - return elementName + '\n' + formatMessage({ id: 'uploading' }); - } - if (!childrenMetadata[elementUuid]) { - return ( - elementName + - '\n' + - formatMessage({ id: 'creationInProgress' }) - ); - } - return childrenMetadata[elementUuid].name; - }, - [childrenMetadata, intl.formatMessage] - ); - - const isElementCaseOrStudy = (objectType) => { - return ( - objectType === ElementType.STUDY || objectType === ElementType.CASE - ); - }; - - const nameCellRender = useCallback( - (cellData) => { - const element = currentChildren.find( - (e) => e.elementUuid === cellData.rowData.elementUuid - ); - return ( - - {/* Icon */} - {!childrenMetadata[element.elementUuid] && - isElementCaseOrStudy(element.type) && ( - - )} - {childrenMetadata[element.elementUuid] && - getFileIcon(element.type, styles.icon)} - {/* Name */} - - - ); - }, - [childrenMetadata, currentChildren, getDisplayedElementName] - ); - - function toggleSelection(elementUuid) { - let element = currentChildren?.find( - (e) => e.elementUuid === elementUuid - ); - if (element === undefined) { - return; - } - let newSelection = new Set(selectedUuids); - if (!newSelection.delete(elementUuid)) { - newSelection.add(elementUuid); - } - setSelectedUuids(newSelection); - } - - function toggleSelectAll() { - if (selectedUuids.size === 0) { - setSelectedUuids( - new Set( - currentChildren - .filter((e) => !e.uploading) - .map((c) => c.elementUuid) - ) - ); - } else { - setSelectedUuids(new Set()); - } - } - - function selectionHeaderRenderer() { - return ( - { - toggleSelectAll(); - e.stopPropagation(); - }} - sx={styles.checkboxes} - > - 0} - indeterminate={ - selectedUuids.size !== 0 && - selectedUuids.size !== currentChildren.length - } - /> - - ); - } - - function selectionRenderer(cellData) { - const elementUuid = cellData.rowData['elementUuid']; - return ( - { - toggleSelection(elementUuid); - e.stopPropagation(); - }} - sx={styles.checkboxes} - > - - - ); - } - useEffect(() => { if (!selectedDirectory) { return; @@ -746,101 +459,29 @@ const DirectoryContent = () => { setIsMissingDataAfterDirChange(true); }, [selectedDirectory, setIsMissingDataAfterDirChange]); - useEffect(() => { - if (!currentChildren?.length) { - setChildrenMetadata({}); - setIsMissingDataAfterDirChange(false); - return; - } - - let metadata = {}; - let childrenToFetchElementsInfos = Object.values(currentChildren) - .filter((e) => !e.uploading) - .map((e) => e.elementUuid); - if (childrenToFetchElementsInfos.length > 0) { - fetchElementsInfos(childrenToFetchElementsInfos) - .then((res) => { - res.forEach((e) => { - metadata[e.elementUuid] = { - name: e.elementName, - subtype: e.specificMetadata - ? e.specificMetadata.type - : null, - format: e.specificMetadata?.format ?? null, - specificMetadata: e.specificMetadata, - }; - }); - }) - .catch((error) => { - if (Object.keys(currentChildrenRef.current).length === 0) { - handleError(error.message); - } - }) - .finally(() => { - // discarding request for older directory - if (currentChildrenRef.current === currentChildren) { - setChildrenMetadata(metadata); - setIsMissingDataAfterDirChange(false); - } - }); - } - setSelectedUuids(new Set()); - }, [handleError, currentChildren, currentChildrenRef]); - const getSelectedChildren = () => { - let selectedChildren = []; - if (currentChildren?.length > 0) { - // Adds the previously selected elements - if (selectedUuids?.size) { - selectedChildren = currentChildren - .filter( - (child) => - selectedUuids.has(child.elementUuid) && - child.elementUuid !== activeElement?.elementUuid - ) - .map((child) => { - return { - subtype: - childrenMetadata[child.elementUuid]?.subtype, - hasMetadata: - childrenMetadata[child.elementUuid] !== - undefined, - ...child, - }; - }); - } - - // Adds the active element - if (activeElement) { - selectedChildren.push({ - ...activeElement, + let selectedChildren = + gridRef.current?.api?.getSelectedRows().map((row) => { + return { + ...row, subtype: - childrenMetadata[activeElement.elementUuid]?.subtype, + childrenMetadata[row.elementUuid]?.specificMetadata + .type, hasMetadata: - childrenMetadata[activeElement.elementUuid] !== - undefined, - }); - } + childrenMetadata[row.elementUuid] !== undefined, + }; + }) ?? []; + if (activeElement) { + selectedChildren.push({ + ...activeElement, + subtype: childrenMetadata[activeElement.elementUuid]?.subtype, + hasMetadata: + childrenMetadata[activeElement.elementUuid] !== undefined, + }); } - return [...new Set(selectedChildren)]; + return selectedChildren; }; - const rows = useMemo( - () => - currentChildren?.map((child) => ({ - ...child, - type: - childrenMetadata[child.elementUuid] && - getElementTypeTranslation( - child.type, - childrenMetadata[child.elementUuid].subtype, - childrenMetadata[child.elementUuid].format - ), - notClickable: child.type === ElementType.CASE, - })), - [childrenMetadata, currentChildren, getElementTypeTranslation] - ); - const renderLoadingContent = () => { return ( @@ -856,13 +497,7 @@ const DirectoryContent = () => { const renderEmptyDirContent = () => { return ( <> - 0 ? getSelectedChildren() : [] - } - /> +
{ ); }; + const defaultColDef = useMemo( + () => ({ + sortable: true, + resizable: false, + lockPinned: true, + wrapHeaderText: true, + autoHeaderHeight: true, + suppressMovable: true, + flex: 1, + }), + [] + ); + + const onGridReady = useCallback(({ api }) => { + api?.sizeColumnsToFit(); + }, []); + + const getRowId = useCallback( + (params) => + params.data.elementUuid ?? + params.data.elementName + params.data.owner, + [] + ); + + const getRowStyle = useCallback((cellData) => { + if ( + ![ElementType.CASE, ElementType.PARAMETERS].includes( + cellData.data?.type + ) + ) { + return { cursor: 'pointer' }; + } + }, []); + const renderTableContent = () => { return ( <> - 0 ? getSelectedChildren() : [] - } - /> - onContextMenu(e)} - onRowClick={handleRowClick} - rows={rows} - columns={[ - { - cellRenderer: selectionRenderer, - dataKey: 'selected', - label: '', - headerRenderer: selectionHeaderRenderer, - minWidth: '3%', - }, + + ); @@ -967,14 +639,14 @@ const DirectoryContent = () => { return renderLoadingContent(); } - // If no selection or currentChildren = null (first time) render nothing + // If no selection or data = null (first time) render nothing // TODO : Make a beautiful page here - if (!currentChildren || !selectedDirectory) { + if (!data || !selectedDirectory) { return; } // If empty dir then render an appropriate content - if (currentChildren.length === 0) { + if (data.length === 0) { return renderEmptyDirContent(); } @@ -993,6 +665,7 @@ const DirectoryContent = () => { setActiveElement(null); setOpenDescModificationDialog(false); }} + updateElement={updateElement} /> ); } @@ -1107,17 +780,9 @@ const DirectoryContent = () => { return ( <> -
onContextMenu(e)} - > + {renderContent()} -
- +
{ if ( diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts new file mode 100644 index 000000000..65665d2f1 --- /dev/null +++ b/src/hooks/useDirectoryContent.ts @@ -0,0 +1,85 @@ +import { useSelector } from 'react-redux'; +import React, { + useRef, + useEffect, + useCallback, + useState, + MutableRefObject, +} from 'react'; +import { useSnackMessage } from '@gridsuite/commons-ui'; +import { fetchElementsInfos } from '../utils/rest-api'; +import { UUID } from 'crypto'; +import { AgGridReact } from 'ag-grid-react'; +import { IElement, IElementMetadata, ReduxState } from '../redux/reducer.type'; + +type useDirectoryContentReturnType = [ + data: IElement[], + childrenMetadata: Record +]; +export const useDirectoryContent = ( + gridRef: MutableRefObject, + setIsMissingDataAfterDirChange: React.Dispatch< + React.SetStateAction + > +): useDirectoryContentReturnType => { + const currentChildren = useSelector( + (state: ReduxState) => state.currentChildren + ); + const [childrenMetadata, setChildrenMetadata] = useState({}); + const { snackError } = useSnackMessage(); + + const [data, setData] = useState(currentChildren); + const previousData = useRef(); + previousData.current = currentChildren; + + const handleError = useCallback( + (message: string) => { + snackError({ + messageTxt: message, + }); + }, + [snackError] + ); + + useEffect(() => { + if (!currentChildren?.length) { + setChildrenMetadata({}); + setIsMissingDataAfterDirChange(false); + return; + } + + let metadata: Record = {}; + let childrenToFetchElementsInfos = Object.values(currentChildren) + .filter((e) => !e.uploading) + .map((e) => e.elementUuid); + if (childrenToFetchElementsInfos.length > 0) { + fetchElementsInfos(childrenToFetchElementsInfos) + .then((res) => { + res.forEach((e: IElementMetadata) => { + metadata[e.elementUuid] = e; + }); + }) + .catch((error) => { + if ( + previousData.current && + Object.keys(previousData.current).length === 0 + ) { + handleError(error.message); + } + }) + .finally(() => { + // discarding request for older directory + if (previousData.current === currentChildren) { + setChildrenMetadata(metadata); + setIsMissingDataAfterDirChange(false); + } + }); + } + }, [handleError, currentChildren, setIsMissingDataAfterDirChange]); + + useEffect(() => { + setData(currentChildren); + }, [currentChildren, gridRef]); + + return [data, childrenMetadata]; +}; diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index a33a5f1bf..7cd8c6880 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { UUID } from 'crypto'; +import { ElementType } from '@gridsuite/commons-ui'; type UserProfile = { sub: string; @@ -22,10 +23,11 @@ interface IUser { expires_at: number; } -export interface IDirectory { +export interface IElement { elementUuid: UUID; elementName: string; - type: 'DIRECTORY'; + description: string; + type: ElementType; accessRights: { isPrivate: boolean; }; @@ -36,10 +38,22 @@ export interface IDirectory { lastModifiedBy: string; children: any[]; parentUuid: null | UUID; + uploading?: boolean; +} + +export interface IElementMetadata { + elementUuid: UUID; + elementName: string; + specificMetadata: { + type: string; + equipmentType: string; + format: string | null; + }; } export interface ReduxState { activeDirectory: UUID; - selectedDirectory: IDirectory; + currentChildren: IElement[]; + selectedDirectory: IElement; user: IUser; } diff --git a/src/utils/directory-content-renderers.tsx b/src/utils/directory-content-renderers.tsx new file mode 100644 index 000000000..caf33a242 --- /dev/null +++ b/src/utils/directory-content-renderers.tsx @@ -0,0 +1,233 @@ +import { Box, Theme } from '@mui/material'; +import CircularProgress from '@mui/material/CircularProgress'; +import { + getFileIcon, + OverflowableText, + ElementType, +} from '@gridsuite/commons-ui'; +import Tooltip from '@mui/material/Tooltip'; +import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined'; +import CreateIcon from '@mui/icons-material/Create'; +import Chip from '@mui/material/Chip'; +import { IntlShape, useIntl } from 'react-intl'; +import { UUID } from 'crypto'; +import { IElement, IElementMetadata } from '../redux/reducer.type'; + +const styles = { + tableCell: (theme: Theme) => ({ + fontSize: 'small', + display: 'flex', + alignItems: 'center', + }), + chip: { + cursor: 'pointer', + }, + icon: (theme: Theme) => ({ + marginRight: theme.spacing(1), + width: '18px', + height: '18px', + }), + circularRoot: (theme: Theme) => ({ + marginRight: theme.spacing(1), + }), + tooltip: { + maxWidth: '1000px', + }, + descriptionTooltip: { + display: 'inline-block', + whiteSpace: 'pre', + textOverflow: 'ellipsis', + overflow: 'hidden', + maxWidth: '250px', + maxHeight: '50px', + cursor: 'pointer', + }, +}; + +const getDisplayedElementName = ( + data: IElement, + childrenMetadata: Record, + intl: IntlShape +) => { + const { elementName, uploading, elementUuid } = data; + const formatMessage = intl.formatMessage; + if (uploading) { + return elementName + '\n' + formatMessage({ id: 'uploading' }); + } + if (!childrenMetadata[elementUuid]) { + return elementName + '\n' + formatMessage({ id: 'creationInProgress' }); + } + return childrenMetadata[elementUuid].elementName; +}; + +const isElementCaseOrStudy = (objectType: ElementType) => { + return objectType === ElementType.STUDY || objectType === ElementType.CASE; +}; + +const abbreviationFromUserName = (name: string | null) => { + if (name === null) { + return ''; + } + const tab = name.split(' ').map((x) => x.charAt(0)); + if (tab.length === 1) { + return tab[0]; + } else { + return tab[0] + tab[tab.length - 1]; + } +}; + +const getElementTypeTranslation = ( + type: ElementType, + subtype: string | null, + formatCase: string | null, + intl: IntlShape +) => { + let translatedType; + switch (type) { + case ElementType.FILTER: + case ElementType.CONTINGENCY_LIST: + translatedType = intl.formatMessage({ + id: subtype + '_' + type, + }); + break; + case ElementType.MODIFICATION: + translatedType = + intl.formatMessage({ id: type }) + + ' (' + + intl.formatMessage({ + id: 'network_modifications.' + subtype, + }) + + ')'; + break; + default: + translatedType = type ? intl.formatMessage({ id: type }) : ''; + break; + } + + const translatedFormat = formatCase + ? ' (' + intl.formatMessage({ id: formatCase }) + ')' + : ''; + + return `${translatedType}${translatedFormat}`; +}; +export const NameCellRenderer = ({ + data, + childrenMetadata, +}: { + data: IElement; + childrenMetadata: Record; +}) => { + const intl = useIntl(); + return ( + + {/* Icon */} + {!childrenMetadata[data.elementUuid] && + isElementCaseOrStudy(data.type) && ( + + )} + {childrenMetadata[data.elementUuid] && + getFileIcon(data.type, styles.icon)} + {/* Name */} + + + ); +}; + +export const DescriptionCellRenderer = ({ data }: { data: IElement }) => { + const description = data.description; + const descriptionLines = description?.split('\n'); + if (descriptionLines?.length > 3) { + descriptionLines[2] = '...'; + } + const tooltip = descriptionLines?.join('\n'); + + const icon = description ? ( + } + placement="right" + > + + + ) : ( + + ); + return ( + + {icon} + + ); +}; + +export const TypeCellRenderer = ({ + data, + childrenMetadata, +}: { + data: IElement; + childrenMetadata: Record; +}) => { + const intl = useIntl(); + + return ( + + + + ); +}; + +export const DateCellRenderer = ({ value }: { value: string }) => { + const intl = useIntl(); + + const todayStart = new Date().setHours(0, 0, 0, 0); + const dateValue = new Date(value); + if (!Number.isNaN(dateValue)) { + const cellMidnight = new Date(value).setHours(0, 0, 0, 0); + + const time = new Intl.DateTimeFormat(intl.locale, { + timeStyle: 'medium', + hour12: false, + }).format(dateValue); + const displayedDate = + intl.locale === 'en' + ? dateValue.toISOString().substring(0, 10) + : dateValue.toLocaleDateString(intl.locale); + const cellText = todayStart === cellMidnight ? time : displayedDate; + const fullDate = new Intl.DateTimeFormat(intl.locale, { + dateStyle: 'long', + timeStyle: 'long', + hour12: false, + }).format(dateValue); + + return ( + + + {cellText} + + + ); + } +}; + +export const UserCellRenderer = ({ value }: { value: string }) => { + return ( + + + + + + ); +}; From b2957155df9764ad05f290f4e73eeaf09c7a2b1b Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Mon, 13 May 2024 16:19:34 +0200 Subject: [PATCH 02/46] Fix content toolbar and date renderer --- src/components/directory-content.jsx | 59 ++++++++++++----------- src/utils/directory-content-renderers.tsx | 2 +- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 8a30711ac..e61132226 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -97,7 +97,6 @@ const DirectoryContent = () => { const selectionForCopy = useSelector((state) => state.selectionForCopy); const activeDirectory = useSelector((state) => state.activeDirectory); - const currentChildren = useSelector((state) => state.currentChildren); const [languageLocal] = useParameterState(PARAM_LANGUAGE); @@ -355,7 +354,6 @@ const DirectoryContent = () => { setOpenDescModificationDialog(true); }; - //TODO DEAL WITH THIS RERENDERING BASTARD const handleCellClick = useCallback( (event) => { if (event.colDef.field === 'description') { @@ -459,19 +457,20 @@ const DirectoryContent = () => { setIsMissingDataAfterDirChange(true); }, [selectedDirectory, setIsMissingDataAfterDirChange]); - const getSelectedChildren = () => { - let selectedChildren = - gridRef.current?.api?.getSelectedRows().map((row) => { - return { - ...row, - subtype: - childrenMetadata[row.elementUuid]?.specificMetadata - .type, - hasMetadata: - childrenMetadata[row.elementUuid] !== undefined, - }; - }) ?? []; - if (activeElement) { + const getSelectedChildren = useCallback(() => { + const selectedChildren = + gridRef.current?.api?.getSelectedRows().map((row) => ({ + ...row, + subtype: + childrenMetadata[row.elementUuid]?.specificMetadata.type, + hasMetadata: childrenMetadata[row.elementUuid] !== undefined, + })) ?? []; + if ( + activeElement && + !selectedChildren.find( + (children) => children.elementUuid === activeElement.elementUuid + ) + ) { selectedChildren.push({ ...activeElement, subtype: childrenMetadata[activeElement.elementUuid]?.subtype, @@ -480,7 +479,7 @@ const DirectoryContent = () => { }); } return selectedChildren; - }; + }, [activeElement, childrenMetadata]); const renderLoadingContent = () => { return ( @@ -497,7 +496,7 @@ const DirectoryContent = () => { const renderEmptyDirContent = () => { return ( <> - +
{ [] ); - const onGridReady = useCallback(({ api }) => { - api?.sizeColumnsToFit(); - }, []); - - const getRowId = useCallback( - (params) => - params.data.elementUuid ?? - params.data.elementName + params.data.owner, - [] + const [selectedChildren, setSelectedChildren] = useState( + getSelectedChildren() ); + const onGridReady = useCallback( + ({ api }) => { + api?.sizeColumnsToFit(); + api?.addEventListener('rowSelected', () => + setSelectedChildren(getSelectedChildren()) + ); + }, + [getSelectedChildren] + ); + + const getRowId = useCallback((params) => params.data.elementUuid, []); const getRowStyle = useCallback((cellData) => { if ( @@ -547,7 +550,7 @@ const DirectoryContent = () => { const renderTableContent = () => { return ( <> - + { > { const todayStart = new Date().setHours(0, 0, 0, 0); const dateValue = new Date(value); - if (!Number.isNaN(dateValue)) { + if (!isNaN(dateValue.getDate())) { const cellMidnight = new Date(value).setHours(0, 0, 0, 0); const time = new Intl.DateTimeFormat(intl.locale, { From 73b9a6548441ebe9bf886c1d5a415030d9872fd5 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 14 May 2024 08:58:26 +0200 Subject: [PATCH 03/46] Simplify selection logic --- src/components/directory-content.jsx | 67 ++++++++++++++++------------ 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index e61132226..c79a59982 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -273,14 +273,8 @@ const DirectoryContent = () => { const onCellContextMenu = useCallback( (event) => { - const element = data?.find( - (e) => - 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 (element && element.uploading !== null) { - if (element.type !== 'DIRECTORY') { + if (event.data && event.data.uploading !== null) { + if (event.data.type !== 'DIRECTORY') { setActiveElement({ hasMetadata: childrenMetadata[event.data.elementUuid] !== @@ -288,7 +282,7 @@ const DirectoryContent = () => { specificMetadata: childrenMetadata[event.data.elementUuid] ?.specificMetadata, - ...element, + ...event.data, }); } setMousePosition({ @@ -298,7 +292,7 @@ const DirectoryContent = () => { handleOpenContentMenu(event.event); } }, - [childrenMetadata, data] + [childrenMetadata] ); const onContextMenu = useCallback( @@ -457,29 +451,48 @@ const DirectoryContent = () => { setIsMissingDataAfterDirChange(true); }, [selectedDirectory, setIsMissingDataAfterDirChange]); - const getSelectedChildren = useCallback(() => { - const selectedChildren = + const getCheckedElement = useCallback( + () => gridRef.current?.api?.getSelectedRows().map((row) => ({ ...row, subtype: childrenMetadata[row.elementUuid]?.specificMetadata.type, hasMetadata: childrenMetadata[row.elementUuid] !== undefined, - })) ?? []; - if ( + })) ?? [], + [childrenMetadata] + ); + + const [checkedElement, setCheckedElement] = useState(getCheckedElement()); + + const isActiveElementUnchecked = useMemo( + () => activeElement && - !selectedChildren.find( + !checkedElement.find( (children) => children.elementUuid === activeElement.elementUuid - ) - ) { - selectedChildren.push({ + ), + [activeElement, checkedElement] + ); + + //It includes checked rows and the row with its context menu open + const fullSelection = useMemo(() => { + const selection = [...checkedElement]; + if (isActiveElementUnchecked) { + selection.push({ ...activeElement, subtype: childrenMetadata[activeElement.elementUuid]?.subtype, hasMetadata: childrenMetadata[activeElement.elementUuid] !== undefined, }); } - return selectedChildren; - }, [activeElement, childrenMetadata]); + return selection; + }, [ + activeElement, + checkedElement, + childrenMetadata, + isActiveElementUnchecked, + ]); + + console.log(fullSelection); const renderLoadingContent = () => { return ( @@ -496,7 +509,6 @@ const DirectoryContent = () => { const renderEmptyDirContent = () => { return ( <> -
{ [] ); - const [selectedChildren, setSelectedChildren] = useState( - getSelectedChildren() - ); const onGridReady = useCallback( ({ api }) => { api?.sizeColumnsToFit(); - api?.addEventListener('rowSelected', () => - setSelectedChildren(getSelectedChildren()) + api?.addEventListener('selectionChanged', () => + setCheckedElement(getCheckedElement()) ); }, - [getSelectedChildren] + [getCheckedElement] ); const getRowId = useCallback((params) => params.data.elementUuid, []); @@ -550,7 +559,7 @@ const DirectoryContent = () => { const renderTableContent = () => { return ( <> - + { > Date: Tue, 14 May 2024 15:20:13 +0200 Subject: [PATCH 04/46] Styling --- src/components/app-wrapper.jsx | 6 + src/components/directory-content.jsx | 115 +++--------------- .../utils/directory-content-utils.ts | 91 ++++++++++++++ src/utils/directory-content-renderers.tsx | 2 +- 4 files changed, 114 insertions(+), 100 deletions(-) create mode 100644 src/components/utils/directory-content-utils.ts diff --git a/src/components/app-wrapper.jsx b/src/components/app-wrapper.jsx index 147831cb9..884b6d3fc 100644 --- a/src/components/app-wrapper.jsx +++ b/src/components/app-wrapper.jsx @@ -86,6 +86,7 @@ let lightTheme = createTheme({ hover: '#8E9C9B', }, aggrid: 'ag-theme-alpine', + alternateTheme: 'ag-theme-material', agGridBackground: { color: 'white', }, @@ -94,6 +95,8 @@ let lightTheme = createTheme({ textTransform: 'none', }, }, + aggridMaterialColor: '#90caf9 !important', + aggridHiglightColor: '#8e9c9b !important', }); lightTheme = createTheme(lightTheme, { @@ -140,6 +143,7 @@ let darkTheme = createTheme({ hover: '#545C5B', }, aggrid: 'ag-theme-alpine-dark', + alternateTheme: 'ag-theme-material-dark', agGridBackground: { color: '#383838', }, @@ -148,6 +152,8 @@ let darkTheme = createTheme({ textTransform: 'none', }, }, + aggridMaterialColor: '#1976d2 !important', + aggridHiglightColor: '#545c5b !important', }); darkTheme = createTheme(darkTheme, { diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index c79a59982..85c36f1ce 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -48,14 +48,11 @@ import ScriptEditionDialog from './dialogs/contingency-list/edition/script/scrip import { useParameterState } from './dialogs/parameters-dialog'; import { PARAM_LANGUAGE } from '../utils/config-params'; import Grid from '@mui/material/Grid'; -import { - TypeCellRenderer, - DescriptionCellRenderer, - UserCellRenderer, - DateCellRenderer, - NameCellRenderer, -} from '../utils/directory-content-renderers.tsx'; import { useDirectoryContent } from '../hooks/useDirectoryContent'; +import { + getColumnsDefinition, + defaultColumnDefinition, +} from './utils/directory-content-utils.ts'; const circularProgressSize = '70px'; @@ -150,7 +147,7 @@ const DirectoryContent = () => { const [activeElement, setActiveElement] = useState(null); const [isMissingDataAfterDirChange, setIsMissingDataAfterDirChange] = - useState(false); + useState(true); const intl = useIntl(); const gridRef = useRef(); @@ -451,7 +448,7 @@ const DirectoryContent = () => { setIsMissingDataAfterDirChange(true); }, [selectedDirectory, setIsMissingDataAfterDirChange]); - const getCheckedElement = useCallback( + const getCheckedElements = useCallback( () => gridRef.current?.api?.getSelectedRows().map((row) => ({ ...row, @@ -462,7 +459,7 @@ const DirectoryContent = () => { [childrenMetadata] ); - const [checkedElement, setCheckedElement] = useState(getCheckedElement()); + const [checkedElement, setCheckedElement] = useState(getCheckedElements()); const isActiveElementUnchecked = useMemo( () => @@ -492,8 +489,6 @@ const DirectoryContent = () => { isActiveElementUnchecked, ]); - console.log(fullSelection); - const renderLoadingContent = () => { return ( @@ -521,39 +516,27 @@ const DirectoryContent = () => { ); }; - const defaultColDef = useMemo( - () => ({ - sortable: true, - resizable: false, - lockPinned: true, - wrapHeaderText: true, - autoHeaderHeight: true, - suppressMovable: true, - flex: 1, - }), - [] - ); - const onGridReady = useCallback( ({ api }) => { api?.sizeColumnsToFit(); api?.addEventListener('selectionChanged', () => - setCheckedElement(getCheckedElement()) + setCheckedElement(getCheckedElements()) ); }, - [getCheckedElement] + [getCheckedElements] ); const getRowId = useCallback((params) => params.data.elementUuid, []); - const getRowStyle = useCallback((cellData) => { + const style = { fontSize: '1rem' }; if ( ![ElementType.CASE, ElementType.PARAMETERS].includes( cellData.data?.type ) ) { - return { cursor: 'pointer' }; + style.cursor = 'pointer'; } + return style; }, []); const renderTableContent = () => { @@ -564,82 +547,16 @@ const DirectoryContent = () => { ref={gridRef} rowData={data} getRowId={getRowId} - defaultColDef={defaultColDef} + defaultColDef={defaultColumnDefinition} rowSelection="multiple" suppressRowClickSelection onGridReady={onGridReady} onCellClicked={handleCellClick} onCellContextMenu={onCellContextMenu} - animateRows={false} - columnDefs={[ - { - headerName: intl.formatMessage({ - id: 'elementName', - }), - field: 'elementName', - cellRenderer: NameCellRenderer, - cellRendererParams: { - childrenMetadata: childrenMetadata, - }, - headerCheckboxSelection: true, - checkboxSelection: true, - flex: 5, - }, - { - headerName: intl.formatMessage({ - id: 'description', - }), - field: 'description', - cellRenderer: DescriptionCellRenderer, - maxWidth: '150', - }, - { - headerName: intl.formatMessage({ - id: 'type', - }), - field: 'type', - cellRenderer: TypeCellRenderer, - cellRendererParams: { - childrenMetadata: childrenMetadata, - }, - flex: 2, - }, - { - headerName: intl.formatMessage({ - id: 'creator', - }), - field: 'owner', - cellRenderer: UserCellRenderer, - maxWidth: '150', - }, - { - headerName: intl.formatMessage({ - id: 'created', - }), - field: 'creationDate', - cellRenderer: DateCellRenderer, - maxWidth: '150', - flex: 2, - }, - { - headerName: intl.formatMessage({ - id: 'modifiedBy', - }), - field: 'lastModifiedBy', - cellRenderer: UserCellRenderer, - maxWidth: '150', - }, - { - headerName: intl.formatMessage({ - id: 'modified', - }), - field: 'lastModificationDate', - cellRenderer: DateCellRenderer, - maxWidth: '150', - flex: 2, - }, - ]} + animateRows={true} + columnDefs={getColumnsDefinition(childrenMetadata, intl)} getRowStyle={getRowStyle} + alternateTheme /> ); diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts new file mode 100644 index 000000000..02eda769c --- /dev/null +++ b/src/components/utils/directory-content-utils.ts @@ -0,0 +1,91 @@ +import { + DateCellRenderer, + DescriptionCellRenderer, + NameCellRenderer, + TypeCellRenderer, + UserCellRenderer, +} from '../../utils/directory-content-renderers'; +import { IntlShape } from 'react-intl'; +import { UUID } from 'crypto'; +import { IElementMetadata } from '../../redux/reducer.type'; + +export const defaultColumnDefinition = { + sortable: true, + resizable: false, + lockPinned: true, + wrapHeaderText: true, + autoHeaderHeight: true, + suppressMovable: true, + flex: 1, +}; +export const getColumnsDefinition = ( + childrenMetadata: Record, + intl: IntlShape +) => [ + { + headerName: intl.formatMessage({ + id: 'elementName', + }), + field: 'elementName', + cellRenderer: NameCellRenderer, + cellRendererParams: { + childrenMetadata: childrenMetadata, + }, + headerCheckboxSelection: true, + checkboxSelection: true, + flex: 5, + }, + { + headerName: intl.formatMessage({ + id: 'description', + }), + field: 'description', + cellRenderer: DescriptionCellRenderer, + maxWidth: '150', + }, + { + headerName: intl.formatMessage({ + id: 'type', + }), + field: 'type', + cellRenderer: TypeCellRenderer, + cellRendererParams: { + childrenMetadata: childrenMetadata, + }, + flex: 2, + }, + { + headerName: intl.formatMessage({ + id: 'creator', + }), + field: 'owner', + cellRenderer: UserCellRenderer, + maxWidth: '150', + }, + { + headerName: intl.formatMessage({ + id: 'created', + }), + field: 'creationDate', + cellRenderer: DateCellRenderer, + maxWidth: '150', + flex: 2, + }, + { + headerName: intl.formatMessage({ + id: 'modifiedBy', + }), + field: 'lastModifiedBy', + cellRenderer: UserCellRenderer, + maxWidth: '150', + }, + { + headerName: intl.formatMessage({ + id: 'modified', + }), + field: 'lastModificationDate', + cellRenderer: DateCellRenderer, + maxWidth: '150', + flex: 2, + }, +]; diff --git a/src/utils/directory-content-renderers.tsx b/src/utils/directory-content-renderers.tsx index a640c84d7..802d92093 100644 --- a/src/utils/directory-content-renderers.tsx +++ b/src/utils/directory-content-renderers.tsx @@ -15,7 +15,7 @@ import { IElement, IElementMetadata } from '../redux/reducer.type'; const styles = { tableCell: (theme: Theme) => ({ - fontSize: 'small', + fontSize: '1rem', display: 'flex', alignItems: 'center', }), From f5dd1f61f09ba01df4db573a1afc0d2367495647 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 14 May 2024 16:07:30 +0200 Subject: [PATCH 05/46] Copyrights --- src/components/utils/directory-content-utils.ts | 7 +++++++ src/hooks/useDirectoryContent.ts | 7 +++++++ src/utils/directory-content-renderers.tsx | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index 02eda769c..8da638676 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -1,3 +1,10 @@ +/** + * Copyright (c) 2024, 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 { DateCellRenderer, DescriptionCellRenderer, diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index 65665d2f1..7ca6ccba1 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -1,3 +1,10 @@ +/** + * Copyright (c) 2024, 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 { useSelector } from 'react-redux'; import React, { useRef, diff --git a/src/utils/directory-content-renderers.tsx b/src/utils/directory-content-renderers.tsx index 802d92093..b66dbecee 100644 --- a/src/utils/directory-content-renderers.tsx +++ b/src/utils/directory-content-renderers.tsx @@ -1,3 +1,10 @@ +/** + * Copyright (c) 2024, 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 { Box, Theme } from '@mui/material'; import CircularProgress from '@mui/material/CircularProgress'; import { From 9415b33b2bd533e5dbc2187fdb86bd31687b6ba8 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 14 May 2024 16:55:56 +0200 Subject: [PATCH 06/46] Remove unused prop --- src/components/directory-content.jsx | 1 - src/hooks/useDirectoryContent.ts | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 85c36f1ce..9ea9dd4c2 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -152,7 +152,6 @@ const DirectoryContent = () => { const intl = useIntl(); const gridRef = useRef(); const [data, childrenMetadata] = useDirectoryContent( - gridRef, setIsMissingDataAfterDirChange ); diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index 7ca6ccba1..fcd538b18 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -11,12 +11,10 @@ import React, { useEffect, useCallback, useState, - MutableRefObject, } from 'react'; import { useSnackMessage } from '@gridsuite/commons-ui'; import { fetchElementsInfos } from '../utils/rest-api'; import { UUID } from 'crypto'; -import { AgGridReact } from 'ag-grid-react'; import { IElement, IElementMetadata, ReduxState } from '../redux/reducer.type'; type useDirectoryContentReturnType = [ @@ -24,7 +22,6 @@ type useDirectoryContentReturnType = [ childrenMetadata: Record ]; export const useDirectoryContent = ( - gridRef: MutableRefObject, setIsMissingDataAfterDirChange: React.Dispatch< React.SetStateAction > @@ -86,7 +83,7 @@ export const useDirectoryContent = ( useEffect(() => { setData(currentChildren); - }, [currentChildren, gridRef]); + }, [currentChildren]); return [data, childrenMetadata]; }; From caaa98ee4e9fe6ae43a2fdb7fc61f6d20d43ba28 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 14 May 2024 20:09:29 +0200 Subject: [PATCH 07/46] Fix selection handler --- src/components/directory-content.jsx | 39 ++++++------------- .../utils/directory-content-utils.ts | 22 +++++++++++ src/hooks/useDirectoryContent.ts | 7 +--- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 9ea9dd4c2..eac057033 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -52,6 +52,8 @@ import { useDirectoryContent } from '../hooks/useDirectoryContent'; import { getColumnsDefinition, defaultColumnDefinition, + computeCheckedElements, + formatMetadata, } from './utils/directory-content-utils.ts'; const circularProgressSize = '70px'; @@ -447,18 +449,7 @@ const DirectoryContent = () => { setIsMissingDataAfterDirChange(true); }, [selectedDirectory, setIsMissingDataAfterDirChange]); - const getCheckedElements = useCallback( - () => - gridRef.current?.api?.getSelectedRows().map((row) => ({ - ...row, - subtype: - childrenMetadata[row.elementUuid]?.specificMetadata.type, - hasMetadata: childrenMetadata[row.elementUuid] !== undefined, - })) ?? [], - [childrenMetadata] - ); - - const [checkedElement, setCheckedElement] = useState(getCheckedElements()); + const [checkedElement, setCheckedElement] = useState([]); const isActiveElementUnchecked = useMemo( () => @@ -469,16 +460,15 @@ const DirectoryContent = () => { [activeElement, checkedElement] ); + const handleRowSelected = useCallback(() => { + setCheckedElement(computeCheckedElements(gridRef, childrenMetadata)); + }, [childrenMetadata]); + //It includes checked rows and the row with its context menu open const fullSelection = useMemo(() => { const selection = [...checkedElement]; if (isActiveElementUnchecked) { - selection.push({ - ...activeElement, - subtype: childrenMetadata[activeElement.elementUuid]?.subtype, - hasMetadata: - childrenMetadata[activeElement.elementUuid] !== undefined, - }); + selection.push(formatMetadata(activeElement, childrenMetadata)); } return selection; }, [ @@ -515,15 +505,9 @@ const DirectoryContent = () => { ); }; - const onGridReady = useCallback( - ({ api }) => { - api?.sizeColumnsToFit(); - api?.addEventListener('selectionChanged', () => - setCheckedElement(getCheckedElements()) - ); - }, - [getCheckedElements] - ); + const onGridReady = useCallback(({ api }) => { + api?.sizeColumnsToFit(); + }, []); const getRowId = useCallback((params) => params.data.elementUuid, []); const getRowStyle = useCallback((cellData) => { @@ -552,6 +536,7 @@ const DirectoryContent = () => { onGridReady={onGridReady} onCellClicked={handleCellClick} onCellContextMenu={onCellContextMenu} + onRowSelected={handleRowSelected} animateRows={true} columnDefs={getColumnsDefinition(childrenMetadata, intl)} getRowStyle={getRowStyle} diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index 8da638676..3a101ed47 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -15,6 +15,28 @@ import { import { IntlShape } from 'react-intl'; import { UUID } from 'crypto'; import { IElementMetadata } from '../../redux/reducer.type'; +import { AgGridReact } from 'ag-grid-react'; +import React from 'react'; + +export const formatMetadata = ( + data: any, + childrenMetadata: Record +) => ({ + ...data, + subtype: childrenMetadata[data.elementUuid]?.specificMetadata.type, + hasMetadata: !!childrenMetadata[data.elementUuid], +}); + +export const computeCheckedElements = ( + gridRef: React.MutableRefObject, + childrenMetadata: Record +) => { + return ( + gridRef.current?.api + ?.getSelectedRows() + .map((row) => formatMetadata(row, childrenMetadata)) ?? [] + ); +}; export const defaultColumnDefinition = { sortable: true, diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index fcd538b18..0c24fb4a1 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -6,12 +6,7 @@ */ import { useSelector } from 'react-redux'; -import React, { - useRef, - useEffect, - useCallback, - useState, -} from 'react'; +import React, { useRef, useEffect, useCallback, useState } from 'react'; import { useSnackMessage } from '@gridsuite/commons-ui'; import { fetchElementsInfos } from '../utils/rest-api'; import { UUID } from 'crypto'; From ec69422972aed976f4399ab71f59881543e585de Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 14 May 2024 20:10:16 +0200 Subject: [PATCH 08/46] Revert commons ui version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 690e86699..820637766 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.57.0.tgz", + "@gridsuite/commons-ui": "0.57.0", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "5.0.0-alpha.169", @@ -2956,7 +2956,7 @@ "node_modules/@gridsuite/commons-ui": { "version": "0.57.0", "resolved": "file:../commons-ui/gridsuite-commons-ui-0.57.0.tgz", - "integrity": "sha512-WdFjlIygxsfy1m4+N4GzTuTXs3lbTv3l/j18cm8ARUnhd2xHvgvhyW2zxn7fLnIt/UOCyJt5AQSGG/se1B9B1w==", + "integrity": "sha512-JNlVu32+LvJHdW/S0TM9SMLDJCqY0k7nraIV1Lwr7PVGYssMR/tDDhQ8KzuAGOfOfxTs7d6xMPhnz3ZeSan7GQ==", "license": "MPL-2.0", "dependencies": { "@react-querybuilder/dnd": "^7.2.0", diff --git a/package.json b/package.json index ee9513a0b..342359949 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.57.0.tgz", + "@gridsuite/commons-ui": "0.57.0", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "5.0.0-alpha.169", From 4b554e06609d4104c6f5eafa1387489418549a0f Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Tue, 14 May 2024 20:12:00 +0200 Subject: [PATCH 09/46] Revert lock changes --- package-lock.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 820637766..cd5f4a6a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2955,9 +2955,8 @@ }, "node_modules/@gridsuite/commons-ui": { "version": "0.57.0", - "resolved": "file:../commons-ui/gridsuite-commons-ui-0.57.0.tgz", - "integrity": "sha512-JNlVu32+LvJHdW/S0TM9SMLDJCqY0k7nraIV1Lwr7PVGYssMR/tDDhQ8KzuAGOfOfxTs7d6xMPhnz3ZeSan7GQ==", - "license": "MPL-2.0", + "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.57.0.tgz", + "integrity": "sha512-OkaUmjoNAK/g79Hc+0PajVF+nJro+uxGXr032Ar9zcDurT3emOcn+VvenQszb7Brl+9ZAUfRKMsmvUL8aiu/aw==", "dependencies": { "@react-querybuilder/dnd": "^7.2.0", "@react-querybuilder/material": "^7.2.0", From 2d49e39186dc6c4c6e7526b19c994f12379accbe Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 15 May 2024 10:14:17 +0200 Subject: [PATCH 10/46] Fix console error --- src/utils/directory-content-renderers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/directory-content-renderers.tsx b/src/utils/directory-content-renderers.tsx index b66dbecee..dfa6700b2 100644 --- a/src/utils/directory-content-renderers.tsx +++ b/src/utils/directory-content-renderers.tsx @@ -94,7 +94,7 @@ const getElementTypeTranslation = ( case ElementType.FILTER: case ElementType.CONTINGENCY_LIST: translatedType = intl.formatMessage({ - id: subtype + '_' + type, + id: subtype ? subtype + '_' + type : type, }); break; case ElementType.MODIFICATION: From 73590e9e505692a9fd16c8eed4a0a47520d7187b Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 24 May 2024 14:08:51 +0200 Subject: [PATCH 11/46] Lint --- src/components/directory-content.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 0b24f7c2b..73dd93582 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -53,7 +53,7 @@ import { computeCheckedElements, formatMetadata, } from './utils/directory-content-utils.ts'; -import NoContentDirectory from './no-content-directory.js'; +import NoContentDirectory from './no-content-directory'; const circularProgressSize = '70px'; From 16d182a64c0d3622127000ff88433b42712d4c19 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 24 May 2024 14:12:12 +0200 Subject: [PATCH 12/46] PR remarks --- src/hooks/useDirectoryContent.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index 0c24fb4a1..7f7090dc0 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -12,22 +12,18 @@ import { fetchElementsInfos } from '../utils/rest-api'; import { UUID } from 'crypto'; import { IElement, IElementMetadata, ReduxState } from '../redux/reducer.type'; -type useDirectoryContentReturnType = [ - data: IElement[], - childrenMetadata: Record -]; export const useDirectoryContent = ( setIsMissingDataAfterDirChange: React.Dispatch< React.SetStateAction > -): useDirectoryContentReturnType => { +) => { const currentChildren = useSelector( (state: ReduxState) => state.currentChildren ); - const [childrenMetadata, setChildrenMetadata] = useState({}); + const [childrenMetadata, setChildrenMetadata] = useState>({}); const { snackError } = useSnackMessage(); - const [data, setData] = useState(currentChildren); + const [data, setData] = useState(currentChildren); const previousData = useRef(); previousData.current = currentChildren; @@ -53,8 +49,8 @@ export const useDirectoryContent = ( .map((e) => e.elementUuid); if (childrenToFetchElementsInfos.length > 0) { fetchElementsInfos(childrenToFetchElementsInfos) - .then((res) => { - res.forEach((e: IElementMetadata) => { + .then((res: IElementMetadata[]) => { + res.forEach((e) => { metadata[e.elementUuid] = e; }); }) From 3014f340ebe4029d284889969f0ce0988adad3af Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 24 May 2024 14:15:40 +0200 Subject: [PATCH 13/46] PR remarks --- src/components/directory-content.jsx | 9 --------- src/components/utils/directory-content-utils.ts | 4 ++-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 73dd93582..a7b2e78ad 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -63,15 +63,6 @@ const styles = { textDecoration: 'none', }), grid: { height: 500, width: '100%' }, - overflow: { - whiteSpace: 'pre', - textOverflow: 'ellipsis', - overflow: 'hidden', - }, - checkboxes: { - width: '100%', - justifyContent: 'center', - }, circularProgressContainer: { overflow: 'hidden', display: 'flex', diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index 3a101ed47..7077b318b 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -19,7 +19,7 @@ import { AgGridReact } from 'ag-grid-react'; import React from 'react'; export const formatMetadata = ( - data: any, + data: IElementMetadata, childrenMetadata: Record ) => ({ ...data, @@ -34,7 +34,7 @@ export const computeCheckedElements = ( return ( gridRef.current?.api ?.getSelectedRows() - .map((row) => formatMetadata(row, childrenMetadata)) ?? [] + .map((row: IElementMetadata) => formatMetadata(row, childrenMetadata)) ?? [] ); }; From e1d338806db53c1c207cfe78061813a4ba4c7d82 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 24 May 2024 14:36:23 +0200 Subject: [PATCH 14/46] PR remarks --- src/components/directory-content.jsx | 25 ++++++++++++------------- src/hooks/useDirectoryContent.ts | 6 +++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index a7b2e78ad..106b7e30e 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -144,9 +144,10 @@ const DirectoryContent = () => { const intl = useIntl(); const gridRef = useRef(); - const [data, childrenMetadata] = useDirectoryContent( + const [rows, childrenMetadata] = useDirectoryContent( setIsMissingDataAfterDirChange ); + const [checkedRows, setCheckedRows] = useState([]); /* Menu states */ const [mousePosition, setMousePosition] = useState(initialMousePosition); @@ -426,7 +427,7 @@ const DirectoryContent = () => { getLink, handleError, intl, - selectedDirectory, + selectedDirectory?.elementUuid, ] ); @@ -440,31 +441,29 @@ const DirectoryContent = () => { setIsMissingDataAfterDirChange(true); }, [selectedDirectory, setIsMissingDataAfterDirChange]); - const [checkedElement, setCheckedElement] = useState([]); - const isActiveElementUnchecked = useMemo( () => activeElement && - !checkedElement.find( + !checkedRows.find( (children) => children.elementUuid === activeElement.elementUuid ), - [activeElement, checkedElement] + [activeElement, checkedRows] ); const handleRowSelected = useCallback(() => { - setCheckedElement(computeCheckedElements(gridRef, childrenMetadata)); + setCheckedRows(computeCheckedElements(gridRef, childrenMetadata)); }, [childrenMetadata]); //It includes checked rows and the row with its context menu open const fullSelection = useMemo(() => { - const selection = [...checkedElement]; + const selection = [...checkedRows]; if (isActiveElementUnchecked) { selection.push(formatMetadata(activeElement, childrenMetadata)); } return selection; }, [ activeElement, - checkedElement, + checkedRows, childrenMetadata, isActiveElementUnchecked, ]); @@ -520,10 +519,10 @@ const DirectoryContent = () => { const renderTableContent = () => { return ( <> - + { } // If no selection or currentChildren = null (first time) render nothing - if (!data || !selectedDirectory) { + if (!rows || !selectedDirectory) { if (treeData.rootDirectories.length === 0 && treeData.initialized) { return ( @@ -558,7 +557,7 @@ const DirectoryContent = () => { } // If empty dir then render an appropriate content - if (data.length === 0) { + if (rows.length === 0) { return renderEmptyDirContent(); } diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index 7f7090dc0..2d485d22a 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -23,7 +23,7 @@ export const useDirectoryContent = ( const [childrenMetadata, setChildrenMetadata] = useState>({}); const { snackError } = useSnackMessage(); - const [data, setData] = useState(currentChildren); + const [rows, setRows] = useState(currentChildren); const previousData = useRef(); previousData.current = currentChildren; @@ -73,8 +73,8 @@ export const useDirectoryContent = ( }, [handleError, currentChildren, setIsMissingDataAfterDirChange]); useEffect(() => { - setData(currentChildren); + setRows(currentChildren); }, [currentChildren]); - return [data, childrenMetadata]; + return [rows, childrenMetadata]; }; From ec56ccef109271199bde89d399f2178f01dbfe76 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 30 May 2024 15:39:12 +0200 Subject: [PATCH 15/46] PR remarks --- src/components/DirectoryContentTable.tsx | 70 +++++++++++++++++++ src/components/directory-content.jsx | 67 ++++++------------ .../utils/directory-content-utils.ts | 13 ++-- 3 files changed, 99 insertions(+), 51 deletions(-) create mode 100644 src/components/DirectoryContentTable.tsx diff --git a/src/components/DirectoryContentTable.tsx b/src/components/DirectoryContentTable.tsx new file mode 100644 index 000000000..9d8ea2f94 --- /dev/null +++ b/src/components/DirectoryContentTable.tsx @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2024, 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 { defaultColumnDefinition } from './utils/directory-content-utils'; +import { CustomAGGrid, ElementType } from '@gridsuite/commons-ui'; +import { AgGridReact } from 'ag-grid-react'; +import { IElement } from '../redux/reducer.type'; +import { GetRowIdParams } from 'ag-grid-community/dist/types/core/interfaces/iCallbackParams'; +import { ColDef, GridReadyEvent, RowClassParams } from 'ag-grid-community'; + +interface DirectoryContentTableProps { + gridRef: React.ForwardedRef>; + rows: IElement[]; + handleRowSelected: () => void; + handleCellClick: () => void; + colDef: ColDef[]; +} + +const onGridReady = ({ api }: GridReadyEvent) => { + api?.sizeColumnsToFit(); +}; + +const getRowId = (params: GetRowIdParams): string => + params.data?.elementUuid; + +const getRowStyle = (cellData: RowClassParams) => { + const style: Record = { fontSize: '1rem' }; + if ( + cellData.data && + ![ + ElementType.CASE, + ElementType.LOADFLOW_PARAMETERS, + ElementType.SENSITIVITY_PARAMETERS, + ElementType.SECURITY_ANALYSIS_PARAMETERS, + ElementType.VOLTAGE_INIT_PARAMETERS, + ].includes(cellData.data.type) + ) { + style.cursor = 'pointer'; + } + return style; +}; + +export const DirectoryContentTable = ({ + gridRef, + rows, + handleRowSelected, + handleCellClick, + colDef, +}: DirectoryContentTableProps) => { + return ( + + ); +}; diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 106b7e30e..effb09efa 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -49,11 +49,11 @@ import Grid from '@mui/material/Grid'; import { useDirectoryContent } from '../hooks/useDirectoryContent'; import { getColumnsDefinition, - defaultColumnDefinition, computeCheckedElements, formatMetadata, } from './utils/directory-content-utils.ts'; import NoContentDirectory from './no-content-directory'; +import { DirectoryContentTable } from './DirectoryContentTable'; const circularProgressSize = '70px'; @@ -255,29 +255,29 @@ const DirectoryContent = () => { event.stopPropagation(); }; - const isRowHovered = () => { - return !!gridRef.current?.api + const getRowHovered = () => { + return gridRef.current?.api ?.getRenderedNodes() .find((node) => node.hovered); }; const onCellContextMenu = useCallback( - (event) => { - if (event.data && event.data.uploading !== null) { - if (event.data.type !== 'DIRECTORY') { + (event, row) => { + if (row.data && row.data.uploading !== null) { + if (row.data.type !== 'DIRECTORY') { setActiveElement({ hasMetadata: - childrenMetadata[event.data.elementUuid] !== + childrenMetadata[row.data.elementUuid] !== undefined, specificMetadata: - childrenMetadata[event.data.elementUuid] + childrenMetadata[row.data.elementUuid] ?.specificMetadata, - ...event.data, + ...row.data, }); } setMousePosition({ - mouseX: event.event.clientX + constants.HORIZONTAL_SHIFT, - mouseY: event.event.clientY + constants.VERTICAL_SHIFT, + mouseX: event.clientX + constants.HORIZONTAL_SHIFT, + mouseY: event.clientY + constants.VERTICAL_SHIFT, }); handleOpenContentMenu(event.event); } @@ -287,7 +287,10 @@ const DirectoryContent = () => { const onContextMenu = useCallback( (event) => { - if (!isRowHovered()) { + const row = getRowHovered(); + if (row) { + onCellContextMenu(event, row); + } else { if (selectedDirectory) { dispatch(setActiveDirectory(selectedDirectory.elementUuid)); } @@ -298,7 +301,7 @@ const DirectoryContent = () => { handleOpenDirectoryMenu(event); } }, - [dispatch, selectedDirectory] + [dispatch, onCellContextMenu, selectedDirectory] ); const handleError = useCallback( @@ -499,42 +502,16 @@ const DirectoryContent = () => { ); }; - const onGridReady = useCallback(({ api }) => { - api?.sizeColumnsToFit(); - }, []); - - const getRowId = useCallback((params) => params.data.elementUuid, []); - const getRowStyle = useCallback((cellData) => { - const style = { fontSize: '1rem' }; - if ( - ![ElementType.CASE, ElementType.PARAMETERS].includes( - cellData.data?.type - ) - ) { - style.cursor = 'pointer'; - } - return style; - }, []); - const renderTableContent = () => { return ( <> - ); diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index 7077b318b..26264ec14 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -17,6 +17,7 @@ import { UUID } from 'crypto'; import { IElementMetadata } from '../../redux/reducer.type'; import { AgGridReact } from 'ag-grid-react'; import React from 'react'; +import { ColDef } from 'ag-grid-community'; export const formatMetadata = ( data: IElementMetadata, @@ -50,7 +51,7 @@ export const defaultColumnDefinition = { export const getColumnsDefinition = ( childrenMetadata: Record, intl: IntlShape -) => [ +): ColDef[] => [ { headerName: intl.formatMessage({ id: 'elementName', @@ -70,7 +71,7 @@ export const getColumnsDefinition = ( }), field: 'description', cellRenderer: DescriptionCellRenderer, - maxWidth: '150', + maxWidth: 150, }, { headerName: intl.formatMessage({ @@ -89,7 +90,7 @@ export const getColumnsDefinition = ( }), field: 'owner', cellRenderer: UserCellRenderer, - maxWidth: '150', + maxWidth: 150, }, { headerName: intl.formatMessage({ @@ -97,7 +98,7 @@ export const getColumnsDefinition = ( }), field: 'creationDate', cellRenderer: DateCellRenderer, - maxWidth: '150', + maxWidth: 150, flex: 2, }, { @@ -106,7 +107,7 @@ export const getColumnsDefinition = ( }), field: 'lastModifiedBy', cellRenderer: UserCellRenderer, - maxWidth: '150', + maxWidth: 150, }, { headerName: intl.formatMessage({ @@ -114,7 +115,7 @@ export const getColumnsDefinition = ( }), field: 'lastModificationDate', cellRenderer: DateCellRenderer, - maxWidth: '150', + maxWidth: 150, flex: 2, }, ]; From adf71b0d8a4274b6eac9ee31759a81f6a341f47d Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 30 May 2024 15:57:47 +0200 Subject: [PATCH 16/46] PR remarks --- .../utils/directory-content-utils.ts | 12 +- .../utils/renderers/DateCellRenderer.tsx | 42 +++ .../renderers/DescriptionCellRenderer.tsx | 48 ++++ .../utils/renderers/NameCellRenderer.tsx | 80 ++++++ .../utils/renderers/TypeCellRenderer.tsx | 77 ++++++ .../utils/renderers/UserCellRenderer.tsx | 40 +++ src/utils/directory-content-renderers.tsx | 240 ------------------ 7 files changed, 292 insertions(+), 247 deletions(-) create mode 100644 src/components/utils/renderers/DateCellRenderer.tsx create mode 100644 src/components/utils/renderers/DescriptionCellRenderer.tsx create mode 100644 src/components/utils/renderers/NameCellRenderer.tsx create mode 100644 src/components/utils/renderers/TypeCellRenderer.tsx create mode 100644 src/components/utils/renderers/UserCellRenderer.tsx delete mode 100644 src/utils/directory-content-renderers.tsx diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index 26264ec14..7e7d96ed3 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -5,19 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { - DateCellRenderer, - DescriptionCellRenderer, - NameCellRenderer, - TypeCellRenderer, - UserCellRenderer, -} from '../../utils/directory-content-renderers'; import { IntlShape } from 'react-intl'; import { UUID } from 'crypto'; import { IElementMetadata } from '../../redux/reducer.type'; import { AgGridReact } from 'ag-grid-react'; import React from 'react'; import { ColDef } from 'ag-grid-community'; +import { NameCellRenderer } from './renderers/NameCellRenderer'; +import { DescriptionCellRenderer } from './renderers/DescriptionCellRenderer'; +import { TypeCellRenderer } from './renderers/TypeCellRenderer'; +import { UserCellRenderer } from './renderers/UserCellRenderer'; +import { DateCellRenderer } from './renderers/DateCellRenderer'; export const formatMetadata = ( data: IElementMetadata, diff --git a/src/components/utils/renderers/DateCellRenderer.tsx b/src/components/utils/renderers/DateCellRenderer.tsx new file mode 100644 index 000000000..aa312734b --- /dev/null +++ b/src/components/utils/renderers/DateCellRenderer.tsx @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2024, 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 { useIntl } from 'react-intl'; +import { Box } from '@mui/material'; +import Tooltip from '@mui/material/Tooltip'; + +export const DateCellRenderer = ({ value }: { value: string }) => { + const intl = useIntl(); + + const todayStart = new Date().setHours(0, 0, 0, 0); + const dateValue = new Date(value); + if (!isNaN(dateValue.getDate())) { + const cellMidnight = new Date(value).setHours(0, 0, 0, 0); + + const time = new Intl.DateTimeFormat(intl.locale, { + timeStyle: 'medium', + hour12: false, + }).format(dateValue); + const displayedDate = + intl.locale === 'en' + ? dateValue.toISOString().substring(0, 10) + : dateValue.toLocaleDateString(intl.locale); + const cellText = todayStart === cellMidnight ? time : displayedDate; + const fullDate = new Intl.DateTimeFormat(intl.locale, { + dateStyle: 'long', + timeStyle: 'long', + hour12: false, + }).format(dateValue); + + return ( + + + {cellText} + + + ); + } +}; diff --git a/src/components/utils/renderers/DescriptionCellRenderer.tsx b/src/components/utils/renderers/DescriptionCellRenderer.tsx new file mode 100644 index 000000000..8b3b5e24d --- /dev/null +++ b/src/components/utils/renderers/DescriptionCellRenderer.tsx @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2024, 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 { IElement } from '../../../redux/reducer.type'; +import Tooltip from '@mui/material/Tooltip'; +import { Box } from '@mui/material'; +import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined'; +import CreateIcon from '@mui/icons-material/Create'; + +const styles = { + descriptionTooltip: { + display: 'inline-block', + whiteSpace: 'pre', + textOverflow: 'ellipsis', + overflow: 'hidden', + maxWidth: '250px', + maxHeight: '50px', + cursor: 'pointer', + }, +}; + +export const DescriptionCellRenderer = ({ data }: { data: IElement }) => { + const description = data.description; + const descriptionLines = description?.split('\n'); + if (descriptionLines?.length > 3) { + descriptionLines[2] = '...'; + } + const tooltip = descriptionLines?.join('\n'); + + const icon = description ? ( + } + placement="right" + > + + + ) : ( + + ); + return ( + + {icon} + + ); +}; diff --git a/src/components/utils/renderers/NameCellRenderer.tsx b/src/components/utils/renderers/NameCellRenderer.tsx new file mode 100644 index 000000000..26b4868d3 --- /dev/null +++ b/src/components/utils/renderers/NameCellRenderer.tsx @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2024, 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 { IElement, IElementMetadata } from '../../../redux/reducer.type'; +import { UUID } from 'crypto'; +import { IntlShape, useIntl } from 'react-intl'; +import { Box, Theme } from '@mui/material'; +import CircularProgress from '@mui/material/CircularProgress'; +import { + ElementType, + getFileIcon, + OverflowableText, +} from '@gridsuite/commons-ui'; + +const isElementCaseOrStudy = (objectType: ElementType) => { + return objectType === ElementType.STUDY || objectType === ElementType.CASE; +}; + +const getDisplayedElementName = ( + data: IElement, + childrenMetadata: Record, + intl: IntlShape +) => { + const { elementName, uploading, elementUuid } = data; + const formatMessage = intl.formatMessage; + if (uploading) { + return elementName + '\n' + formatMessage({ id: 'uploading' }); + } + if (!childrenMetadata[elementUuid]) { + return elementName + '\n' + formatMessage({ id: 'creationInProgress' }); + } + return childrenMetadata[elementUuid].elementName; +}; + +const styles = { + tableCell: (theme: Theme) => ({ + fontSize: '1rem', + display: 'flex', + alignItems: 'center', + }), + circularRoot: (theme: Theme) => ({ + marginRight: theme.spacing(1), + }), + icon: (theme: Theme) => ({ + marginRight: theme.spacing(1), + width: '18px', + height: '18px', + }), + tooltip: { + maxWidth: '1000px', + }, +}; +export const NameCellRenderer = ({ + data, + childrenMetadata, +}: { + data: IElement; + childrenMetadata: Record; +}) => { + const intl = useIntl(); + return ( + + {/* Icon */} + {!childrenMetadata[data.elementUuid] && + isElementCaseOrStudy(data.type) && ( + + )} + {childrenMetadata[data.elementUuid] && + getFileIcon(data.type, styles.icon)} + {/* Name */} + + + ); +}; diff --git a/src/components/utils/renderers/TypeCellRenderer.tsx b/src/components/utils/renderers/TypeCellRenderer.tsx new file mode 100644 index 000000000..4924d6a02 --- /dev/null +++ b/src/components/utils/renderers/TypeCellRenderer.tsx @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2024, 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 { ElementType, OverflowableText } from '@gridsuite/commons-ui'; +import { IntlShape, useIntl } from 'react-intl'; +import { IElement, IElementMetadata } from '../../../redux/reducer.type'; +import { UUID } from 'crypto'; +import { Box } from '@mui/material'; + +const getElementTypeTranslation = ( + type: ElementType, + subtype: string | null, + formatCase: string | null, + intl: IntlShape +) => { + let translatedType; + switch (type) { + case ElementType.FILTER: + case ElementType.CONTINGENCY_LIST: + translatedType = intl.formatMessage({ + id: subtype ? subtype + '_' + type : type, + }); + break; + case ElementType.MODIFICATION: + translatedType = + intl.formatMessage({ id: type }) + + ' (' + + intl.formatMessage({ + id: 'network_modifications.' + subtype, + }) + + ')'; + break; + default: + translatedType = type ? intl.formatMessage({ id: type }) : ''; + break; + } + + const translatedFormat = formatCase + ? ' (' + intl.formatMessage({ id: formatCase }) + ')' + : ''; + + return `${translatedType}${translatedFormat}`; +}; + +const styles = { + tooltip: { + maxWidth: '1000px', + }, +}; + +export const TypeCellRenderer = ({ + data, + childrenMetadata, +}: { + data: IElement; + childrenMetadata: Record; +}) => { + const intl = useIntl(); + + return ( + + + + ); +}; diff --git a/src/components/utils/renderers/UserCellRenderer.tsx b/src/components/utils/renderers/UserCellRenderer.tsx new file mode 100644 index 000000000..4193b74a8 --- /dev/null +++ b/src/components/utils/renderers/UserCellRenderer.tsx @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2024, 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 { Box } from '@mui/material'; +import Tooltip from '@mui/material/Tooltip'; +import Chip from '@mui/material/Chip'; + +const abbreviationFromUserName = (name: string | null) => { + if (name === null) { + return ''; + } + const tab = name.split(' ').map((x) => x.charAt(0)); + if (tab.length === 1) { + return tab[0]; + } else { + return tab[0] + tab[tab.length - 1]; + } +}; + +const styles = { + chip: { + cursor: 'pointer', + }, +}; + +export const UserCellRenderer = ({ value }: { value: string }) => { + return ( + + + + + + ); +}; diff --git a/src/utils/directory-content-renderers.tsx b/src/utils/directory-content-renderers.tsx deleted file mode 100644 index dfa6700b2..000000000 --- a/src/utils/directory-content-renderers.tsx +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright (c) 2024, 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 { Box, Theme } from '@mui/material'; -import CircularProgress from '@mui/material/CircularProgress'; -import { - getFileIcon, - OverflowableText, - ElementType, -} from '@gridsuite/commons-ui'; -import Tooltip from '@mui/material/Tooltip'; -import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined'; -import CreateIcon from '@mui/icons-material/Create'; -import Chip from '@mui/material/Chip'; -import { IntlShape, useIntl } from 'react-intl'; -import { UUID } from 'crypto'; -import { IElement, IElementMetadata } from '../redux/reducer.type'; - -const styles = { - tableCell: (theme: Theme) => ({ - fontSize: '1rem', - display: 'flex', - alignItems: 'center', - }), - chip: { - cursor: 'pointer', - }, - icon: (theme: Theme) => ({ - marginRight: theme.spacing(1), - width: '18px', - height: '18px', - }), - circularRoot: (theme: Theme) => ({ - marginRight: theme.spacing(1), - }), - tooltip: { - maxWidth: '1000px', - }, - descriptionTooltip: { - display: 'inline-block', - whiteSpace: 'pre', - textOverflow: 'ellipsis', - overflow: 'hidden', - maxWidth: '250px', - maxHeight: '50px', - cursor: 'pointer', - }, -}; - -const getDisplayedElementName = ( - data: IElement, - childrenMetadata: Record, - intl: IntlShape -) => { - const { elementName, uploading, elementUuid } = data; - const formatMessage = intl.formatMessage; - if (uploading) { - return elementName + '\n' + formatMessage({ id: 'uploading' }); - } - if (!childrenMetadata[elementUuid]) { - return elementName + '\n' + formatMessage({ id: 'creationInProgress' }); - } - return childrenMetadata[elementUuid].elementName; -}; - -const isElementCaseOrStudy = (objectType: ElementType) => { - return objectType === ElementType.STUDY || objectType === ElementType.CASE; -}; - -const abbreviationFromUserName = (name: string | null) => { - if (name === null) { - return ''; - } - const tab = name.split(' ').map((x) => x.charAt(0)); - if (tab.length === 1) { - return tab[0]; - } else { - return tab[0] + tab[tab.length - 1]; - } -}; - -const getElementTypeTranslation = ( - type: ElementType, - subtype: string | null, - formatCase: string | null, - intl: IntlShape -) => { - let translatedType; - switch (type) { - case ElementType.FILTER: - case ElementType.CONTINGENCY_LIST: - translatedType = intl.formatMessage({ - id: subtype ? subtype + '_' + type : type, - }); - break; - case ElementType.MODIFICATION: - translatedType = - intl.formatMessage({ id: type }) + - ' (' + - intl.formatMessage({ - id: 'network_modifications.' + subtype, - }) + - ')'; - break; - default: - translatedType = type ? intl.formatMessage({ id: type }) : ''; - break; - } - - const translatedFormat = formatCase - ? ' (' + intl.formatMessage({ id: formatCase }) + ')' - : ''; - - return `${translatedType}${translatedFormat}`; -}; -export const NameCellRenderer = ({ - data, - childrenMetadata, -}: { - data: IElement; - childrenMetadata: Record; -}) => { - const intl = useIntl(); - return ( - - {/* Icon */} - {!childrenMetadata[data.elementUuid] && - isElementCaseOrStudy(data.type) && ( - - )} - {childrenMetadata[data.elementUuid] && - getFileIcon(data.type, styles.icon)} - {/* Name */} - - - ); -}; - -export const DescriptionCellRenderer = ({ data }: { data: IElement }) => { - const description = data.description; - const descriptionLines = description?.split('\n'); - if (descriptionLines?.length > 3) { - descriptionLines[2] = '...'; - } - const tooltip = descriptionLines?.join('\n'); - - const icon = description ? ( - } - placement="right" - > - - - ) : ( - - ); - return ( - - {icon} - - ); -}; - -export const TypeCellRenderer = ({ - data, - childrenMetadata, -}: { - data: IElement; - childrenMetadata: Record; -}) => { - const intl = useIntl(); - - return ( - - - - ); -}; - -export const DateCellRenderer = ({ value }: { value: string }) => { - const intl = useIntl(); - - const todayStart = new Date().setHours(0, 0, 0, 0); - const dateValue = new Date(value); - if (!isNaN(dateValue.getDate())) { - const cellMidnight = new Date(value).setHours(0, 0, 0, 0); - - const time = new Intl.DateTimeFormat(intl.locale, { - timeStyle: 'medium', - hour12: false, - }).format(dateValue); - const displayedDate = - intl.locale === 'en' - ? dateValue.toISOString().substring(0, 10) - : dateValue.toLocaleDateString(intl.locale); - const cellText = todayStart === cellMidnight ? time : displayedDate; - const fullDate = new Intl.DateTimeFormat(intl.locale, { - dateStyle: 'long', - timeStyle: 'long', - hour12: false, - }).format(dateValue); - - return ( - - - {cellText} - - - ); - } -}; - -export const UserCellRenderer = ({ value }: { value: string }) => { - return ( - - - - - - ); -}; From 9d364c6228930b8c61f07b62696426b96f8a356d Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 30 May 2024 16:15:51 +0200 Subject: [PATCH 17/46] Remove altername theme --- src/components/app-wrapper.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/app-wrapper.jsx b/src/components/app-wrapper.jsx index 884b6d3fc..499ddb22a 100644 --- a/src/components/app-wrapper.jsx +++ b/src/components/app-wrapper.jsx @@ -143,7 +143,6 @@ let darkTheme = createTheme({ hover: '#545C5B', }, aggrid: 'ag-theme-alpine-dark', - alternateTheme: 'ag-theme-material-dark', agGridBackground: { color: '#383838', }, @@ -152,7 +151,6 @@ let darkTheme = createTheme({ textTransform: 'none', }, }, - aggridMaterialColor: '#1976d2 !important', aggridHiglightColor: '#545c5b !important', }); From 1f3474b273e4c087b49b8f1b9e008c21c4f8a19c Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 30 May 2024 16:16:57 +0200 Subject: [PATCH 18/46] Remove alternate theme --- src/components/app-wrapper.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/app-wrapper.jsx b/src/components/app-wrapper.jsx index 499ddb22a..9c22e8243 100644 --- a/src/components/app-wrapper.jsx +++ b/src/components/app-wrapper.jsx @@ -86,7 +86,6 @@ let lightTheme = createTheme({ hover: '#8E9C9B', }, aggrid: 'ag-theme-alpine', - alternateTheme: 'ag-theme-material', agGridBackground: { color: 'white', }, @@ -95,7 +94,6 @@ let lightTheme = createTheme({ textTransform: 'none', }, }, - aggridMaterialColor: '#90caf9 !important', aggridHiglightColor: '#8e9c9b !important', }); From be4f69da3b0fd687f52d45aca88254503a089be8 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 30 May 2024 17:43:20 +0200 Subject: [PATCH 19/46] PR remarks --- src/components/directory-content.jsx | 33 ++++++++----------- .../utils/directory-content-utils.ts | 4 ++- .../utils/renderers/NameCellRenderer.tsx | 9 +++++ 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index effb09efa..47d3230e7 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -22,7 +22,6 @@ import { ExpertFilterEditionDialog, CriteriaBasedFilterEditionDialog, DescriptionModificationDialog, - CustomAGGrid, noSelectionForCopy, } from '@gridsuite/commons-ui'; import { Box } from '@mui/material'; @@ -62,13 +61,14 @@ const styles = { color: theme.link.color, textDecoration: 'none', }), - grid: { height: 500, width: '100%' }, circularProgressContainer: { overflow: 'hidden', display: 'flex', flexDirection: 'row', flexGrow: '1', justifyContent: 'center', + textAlign: 'center', + marginTop: '100px', }, centeredCircularProgress: { alignSelf: 'center', @@ -442,6 +442,7 @@ const DirectoryContent = () => { return; } setIsMissingDataAfterDirChange(true); + setCheckedRows([]); }, [selectedDirectory, setIsMissingDataAfterDirChange]); const isActiveElementUnchecked = useMemo( @@ -502,21 +503,6 @@ const DirectoryContent = () => { ); }; - const renderTableContent = () => { - return ( - <> - - - - ); - }; - const renderContent = () => { // Here we wait for Metadata for the folder content if (isMissingDataAfterDirChange) { @@ -539,7 +525,15 @@ const DirectoryContent = () => { } // Finally if we have elements then render the table - return renderTableContent(); + return ( + + ); }; const renderDialog = (name) => { @@ -662,7 +656,8 @@ const DirectoryContent = () => { return ( <> - + {rows && } + {renderContent()}
formatMetadata(row, childrenMetadata)) ?? [] + .map((row: IElementMetadata) => + formatMetadata(row, childrenMetadata) + ) ?? [] ); }; diff --git a/src/components/utils/renderers/NameCellRenderer.tsx b/src/components/utils/renderers/NameCellRenderer.tsx index 26b4868d3..3344bd2d5 100644 --- a/src/components/utils/renderers/NameCellRenderer.tsx +++ b/src/components/utils/renderers/NameCellRenderer.tsx @@ -52,6 +52,14 @@ const styles = { tooltip: { maxWidth: '1000px', }, + overflow: { + display: 'inline-block', + whiteSpace: 'pre', + textOverflow: 'ellipsis', + overflow: 'hidden', + lineHeight: 'initial', + verticalAlign: 'middle', + }, }; export const NameCellRenderer = ({ data, @@ -74,6 +82,7 @@ export const NameCellRenderer = ({ ); From d992f4168d449e2a22375923399030c308b5e752 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 30 May 2024 17:52:07 +0200 Subject: [PATCH 20/46] Remove unnecessary function --- src/components/utils/renderers/NameCellRenderer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/utils/renderers/NameCellRenderer.tsx b/src/components/utils/renderers/NameCellRenderer.tsx index 3344bd2d5..8ef3e6473 100644 --- a/src/components/utils/renderers/NameCellRenderer.tsx +++ b/src/components/utils/renderers/NameCellRenderer.tsx @@ -36,11 +36,11 @@ const getDisplayedElementName = ( }; const styles = { - tableCell: (theme: Theme) => ({ + tableCell: { fontSize: '1rem', display: 'flex', alignItems: 'center', - }), + }, circularRoot: (theme: Theme) => ({ marginRight: theme.spacing(1), }), From 0bb92d45dd0c6c1ec674c8018948777e99e173b9 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 30 May 2024 18:08:35 +0200 Subject: [PATCH 21/46] Lint --- src/hooks/useDirectoryContent.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index 2d485d22a..23baf78ab 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -20,7 +20,9 @@ export const useDirectoryContent = ( const currentChildren = useSelector( (state: ReduxState) => state.currentChildren ); - const [childrenMetadata, setChildrenMetadata] = useState>({}); + const [childrenMetadata, setChildrenMetadata] = useState< + Record + >({}); const { snackError } = useSnackMessage(); const [rows, setRows] = useState(currentChildren); From d429ad9481089e8a4702a2cb6b1c0d61595ab08f Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Mon, 3 Jun 2024 11:12:23 +0200 Subject: [PATCH 22/46] PR remarks --- src/components/directory-content.jsx | 46 +++++++++++++++++-- .../utils/directory-content-utils.ts | 11 ++++- .../utils/renderers/TypeCellRenderer.tsx | 28 +++++------ 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 47d3230e7..3290e531c 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -50,7 +50,8 @@ import { getColumnsDefinition, computeCheckedElements, formatMetadata, -} from './utils/directory-content-utils.ts'; + isRowUnchecked, +} from './utils/directory-content-utils'; import NoContentDirectory from './no-content-directory'; import { DirectoryContentTable } from './DirectoryContentTable'; @@ -238,7 +239,7 @@ const DirectoryContent = () => { const handleOpenContentMenu = (event) => { setOpenContentMenu(true); - event.stopPropagation(); + event?.stopPropagation(); }; const handleCloseContentMenu = useCallback(() => { @@ -255,6 +256,16 @@ const DirectoryContent = () => { event.stopPropagation(); }; + /* User interactions */ + const contextualMixPolicies = useMemo( + () => ({ + BIG: 'GoogleMicrosoft', // if !selectedUuids.has(selected.Uuid) deselects selectedUuids + ALL: 'All', // union of activeElement.Uuid and selectedUuids (currently implemented) + }), + [] + ); + const contextualMixPolicy = contextualMixPolicies.ALL; + const getRowHovered = () => { return gridRef.current?.api ?.getRenderedNodes() @@ -274,6 +285,30 @@ const DirectoryContent = () => { ?.specificMetadata, ...row.data, }); + if (contextualMixPolicy === contextualMixPolicies.BIG) { + // If some elements were already selected and the active element is not in them, we deselect the already selected elements. + if (isRowUnchecked(row, checkedRows)) { + setCheckedRows([]); + gridRef.current?.api.deselectAll(); + } + } else { + // If some elements were already selected, we add the active element to the selected list if not already in it. + if (isRowUnchecked(row, checkedRows)) { + setCheckedRows((prevCheckedRows) => [ + ...prevCheckedRows, + formatMetadata(row.data, childrenMetadata), + ]); + gridRef.current?.api.setNodesSelected({ + nodes: [ + ...gridRef.current?.api.getSelectedNodes(), + gridRef.current?.api.getRowNode( + row.data.elementUuid + ), + ], + newValue: true, + }); + } + } } setMousePosition({ mouseX: event.clientX + constants.HORIZONTAL_SHIFT, @@ -282,7 +317,12 @@ const DirectoryContent = () => { handleOpenContentMenu(event.event); } }, - [childrenMetadata] + [ + checkedRows, + childrenMetadata, + contextualMixPolicies.BIG, + contextualMixPolicy, + ] ); const onContextMenu = useCallback( diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index 37d8530df..e490836f1 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -7,10 +7,10 @@ import { IntlShape } from 'react-intl'; import { UUID } from 'crypto'; -import { IElementMetadata } from '../../redux/reducer.type'; +import { IElement, IElementMetadata } from '../../redux/reducer.type'; import { AgGridReact } from 'ag-grid-react'; import React from 'react'; -import { ColDef } from 'ag-grid-community'; +import { ColDef, IRowNode } from 'ag-grid-community'; import { NameCellRenderer } from './renderers/NameCellRenderer'; import { DescriptionCellRenderer } from './renderers/DescriptionCellRenderer'; import { TypeCellRenderer } from './renderers/TypeCellRenderer'; @@ -39,6 +39,13 @@ export const computeCheckedElements = ( ); }; +export const isRowUnchecked = (row: IRowNode, checkedRows: IElement[]) => + checkedRows?.length && + row.data?.elementUuid && + !checkedRows.find( + (checkedRow) => checkedRow.elementUuid === row.data.elementUuid + ); + export const defaultColumnDefinition = { sortable: true, resizable: false, diff --git a/src/components/utils/renderers/TypeCellRenderer.tsx b/src/components/utils/renderers/TypeCellRenderer.tsx index 4924d6a02..a126244f8 100644 --- a/src/components/utils/renderers/TypeCellRenderer.tsx +++ b/src/components/utils/renderers/TypeCellRenderer.tsx @@ -59,19 +59,21 @@ export const TypeCellRenderer = ({ childrenMetadata: Record; }) => { const intl = useIntl(); - return ( - - - + childrenMetadata[data?.elementUuid] && ( + + + + ) ); }; From 2eac05f7c8f2d4c3adf887fca1a24adb91f1fc47 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 5 Jun 2024 15:26:06 +0200 Subject: [PATCH 23/46] PR remarks --- src/components/app-wrapper.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/app-wrapper.jsx b/src/components/app-wrapper.jsx index 9c22e8243..68903a895 100644 --- a/src/components/app-wrapper.jsx +++ b/src/components/app-wrapper.jsx @@ -85,7 +85,10 @@ let lightTheme = createTheme({ secondary: '#F4F4F4', hover: '#8E9C9B', }, - aggrid: 'ag-theme-alpine', + aggrid: { + theme: 'ag-theme-alpine', + highlightColor: '#8e9c9b', + }, agGridBackground: { color: 'white', }, @@ -94,7 +97,6 @@ let lightTheme = createTheme({ textTransform: 'none', }, }, - aggridHiglightColor: '#8e9c9b !important', }); lightTheme = createTheme(lightTheme, { @@ -140,7 +142,10 @@ let darkTheme = createTheme({ secondary: '#323232', hover: '#545C5B', }, - aggrid: 'ag-theme-alpine-dark', + aggrid: { + theme: 'ag-theme-alpine-dark', + highlightColor: '#545c5b', + }, agGridBackground: { color: '#383838', }, @@ -149,7 +154,6 @@ let darkTheme = createTheme({ textTransform: 'none', }, }, - aggridHiglightColor: '#545c5b !important', }); darkTheme = createTheme(darkTheme, { From a6e22f78cc25d9b13397b3d3714c87c5fdf81adf Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 5 Jun 2024 15:28:32 +0200 Subject: [PATCH 24/46] PR remarks --- src/components/utils/directory-content-utils.ts | 10 +++++----- .../{DateCellRenderer.tsx => date-cell-renderer.tsx} | 0 ...nCellRenderer.tsx => description-cell-renderer.tsx} | 0 .../{NameCellRenderer.tsx => name-cell-renderer.tsx} | 0 .../{TypeCellRenderer.tsx => type-cell-renderer.tsx} | 0 .../{UserCellRenderer.tsx => user-cell-renderer.tsx} | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename src/components/utils/renderers/{DateCellRenderer.tsx => date-cell-renderer.tsx} (100%) rename src/components/utils/renderers/{DescriptionCellRenderer.tsx => description-cell-renderer.tsx} (100%) rename src/components/utils/renderers/{NameCellRenderer.tsx => name-cell-renderer.tsx} (100%) rename src/components/utils/renderers/{TypeCellRenderer.tsx => type-cell-renderer.tsx} (100%) rename src/components/utils/renderers/{UserCellRenderer.tsx => user-cell-renderer.tsx} (100%) diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index e490836f1..dfba72647 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -11,11 +11,11 @@ import { IElement, IElementMetadata } from '../../redux/reducer.type'; import { AgGridReact } from 'ag-grid-react'; import React from 'react'; import { ColDef, IRowNode } from 'ag-grid-community'; -import { NameCellRenderer } from './renderers/NameCellRenderer'; -import { DescriptionCellRenderer } from './renderers/DescriptionCellRenderer'; -import { TypeCellRenderer } from './renderers/TypeCellRenderer'; -import { UserCellRenderer } from './renderers/UserCellRenderer'; -import { DateCellRenderer } from './renderers/DateCellRenderer'; +import { NameCellRenderer } from './renderers/name-cell-renderer'; +import { DescriptionCellRenderer } from './renderers/description-cell-renderer'; +import { TypeCellRenderer } from './renderers/type-cell-renderer'; +import { UserCellRenderer } from './renderers/user-cell-renderer'; +import { DateCellRenderer } from './renderers/date-cell-renderer'; export const formatMetadata = ( data: IElementMetadata, diff --git a/src/components/utils/renderers/DateCellRenderer.tsx b/src/components/utils/renderers/date-cell-renderer.tsx similarity index 100% rename from src/components/utils/renderers/DateCellRenderer.tsx rename to src/components/utils/renderers/date-cell-renderer.tsx diff --git a/src/components/utils/renderers/DescriptionCellRenderer.tsx b/src/components/utils/renderers/description-cell-renderer.tsx similarity index 100% rename from src/components/utils/renderers/DescriptionCellRenderer.tsx rename to src/components/utils/renderers/description-cell-renderer.tsx diff --git a/src/components/utils/renderers/NameCellRenderer.tsx b/src/components/utils/renderers/name-cell-renderer.tsx similarity index 100% rename from src/components/utils/renderers/NameCellRenderer.tsx rename to src/components/utils/renderers/name-cell-renderer.tsx diff --git a/src/components/utils/renderers/TypeCellRenderer.tsx b/src/components/utils/renderers/type-cell-renderer.tsx similarity index 100% rename from src/components/utils/renderers/TypeCellRenderer.tsx rename to src/components/utils/renderers/type-cell-renderer.tsx diff --git a/src/components/utils/renderers/UserCellRenderer.tsx b/src/components/utils/renderers/user-cell-renderer.tsx similarity index 100% rename from src/components/utils/renderers/UserCellRenderer.tsx rename to src/components/utils/renderers/user-cell-renderer.tsx From 31c5d7192ede5fdf38358a02feea367eb7488658 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 5 Jun 2024 16:42:17 +0200 Subject: [PATCH 25/46] PR remarks --- src/components/DirectoryContentTable.tsx | 7 ++++--- src/components/directory-content.jsx | 21 +++++-------------- .../utils/directory-content-utils.ts | 8 +++---- src/hooks/useDirectoryContent.ts | 8 +------ 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/components/DirectoryContentTable.tsx b/src/components/DirectoryContentTable.tsx index 9d8ea2f94..ec6722d8f 100644 --- a/src/components/DirectoryContentTable.tsx +++ b/src/components/DirectoryContentTable.tsx @@ -11,9 +11,10 @@ import { AgGridReact } from 'ag-grid-react'; import { IElement } from '../redux/reducer.type'; import { GetRowIdParams } from 'ag-grid-community/dist/types/core/interfaces/iCallbackParams'; import { ColDef, GridReadyEvent, RowClassParams } from 'ag-grid-community'; +import { RefObject } from 'react'; interface DirectoryContentTableProps { - gridRef: React.ForwardedRef>; + gridRef: RefObject>; rows: IElement[]; handleRowSelected: () => void; handleCellClick: () => void; @@ -24,8 +25,7 @@ const onGridReady = ({ api }: GridReadyEvent) => { api?.sizeColumnsToFit(); }; -const getRowId = (params: GetRowIdParams): string => - params.data?.elementUuid; +const getRowId = (params: GetRowIdParams) => params.data?.elementUuid; const getRowStyle = (cellData: RowClassParams) => { const style: Record = { fontSize: '1rem' }; @@ -60,6 +60,7 @@ export const DirectoryContentTable = ({ rowSelection="multiple" suppressRowClickSelection onGridReady={onGridReady} + onCellContextMenu={(e) => console.log('HMA', e)} onCellClicked={handleCellClick} onRowSelected={handleRowSelected} animateRows={true} diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 3290e531c..6883abdf6 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -287,26 +287,15 @@ const DirectoryContent = () => { }); if (contextualMixPolicy === contextualMixPolicies.BIG) { // If some elements were already selected and the active element is not in them, we deselect the already selected elements. - if (isRowUnchecked(row, checkedRows)) { - setCheckedRows([]); + if (isRowUnchecked(row.data, checkedRows)) { gridRef.current?.api.deselectAll(); } } else { // If some elements were already selected, we add the active element to the selected list if not already in it. - if (isRowUnchecked(row, checkedRows)) { - setCheckedRows((prevCheckedRows) => [ - ...prevCheckedRows, - formatMetadata(row.data, childrenMetadata), - ]); - gridRef.current?.api.setNodesSelected({ - nodes: [ - ...gridRef.current?.api.getSelectedNodes(), - gridRef.current?.api.getRowNode( - row.data.elementUuid - ), - ], - newValue: true, - }); + if (isRowUnchecked(row.data, checkedRows)) { + gridRef.current?.api + .getRowNode(row.data.elementUuid) + .setSelected(true); } } } diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index dfba72647..d1b406d31 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -10,7 +10,7 @@ import { UUID } from 'crypto'; import { IElement, IElementMetadata } from '../../redux/reducer.type'; import { AgGridReact } from 'ag-grid-react'; import React from 'react'; -import { ColDef, IRowNode } from 'ag-grid-community'; +import { ColDef } from 'ag-grid-community'; import { NameCellRenderer } from './renderers/name-cell-renderer'; import { DescriptionCellRenderer } from './renderers/description-cell-renderer'; import { TypeCellRenderer } from './renderers/type-cell-renderer'; @@ -39,11 +39,11 @@ export const computeCheckedElements = ( ); }; -export const isRowUnchecked = (row: IRowNode, checkedRows: IElement[]) => +export const isRowUnchecked = (row: IElement, checkedRows: IElement[]) => checkedRows?.length && - row.data?.elementUuid && + row?.elementUuid && !checkedRows.find( - (checkedRow) => checkedRow.elementUuid === row.data.elementUuid + (checkedRow) => checkedRow.elementUuid === row.elementUuid ); export const defaultColumnDefinition = { diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index 23baf78ab..e27b00c48 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -24,8 +24,6 @@ export const useDirectoryContent = ( Record >({}); const { snackError } = useSnackMessage(); - - const [rows, setRows] = useState(currentChildren); const previousData = useRef(); previousData.current = currentChildren; @@ -74,9 +72,5 @@ export const useDirectoryContent = ( } }, [handleError, currentChildren, setIsMissingDataAfterDirChange]); - useEffect(() => { - setRows(currentChildren); - }, [currentChildren]); - - return [rows, childrenMetadata]; + return [currentChildren, childrenMetadata]; }; From 1c3d4374a10075d56a15402bdc2a5ed926f52a95 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Wed, 5 Jun 2024 18:10:34 +0200 Subject: [PATCH 26/46] Change context menu handling --- ...tTable.tsx => directory-content-table.tsx} | 4 +- src/components/directory-content.jsx | 40 ++++++++----------- 2 files changed, 19 insertions(+), 25 deletions(-) rename src/components/{DirectoryContentTable.tsx => directory-content-table.tsx} (94%) diff --git a/src/components/DirectoryContentTable.tsx b/src/components/directory-content-table.tsx similarity index 94% rename from src/components/DirectoryContentTable.tsx rename to src/components/directory-content-table.tsx index ec6722d8f..57ec2e378 100644 --- a/src/components/DirectoryContentTable.tsx +++ b/src/components/directory-content-table.tsx @@ -16,6 +16,7 @@ import { RefObject } from 'react'; interface DirectoryContentTableProps { gridRef: RefObject>; rows: IElement[]; + handleCellContextualMenu: () => void; handleRowSelected: () => void; handleCellClick: () => void; colDef: ColDef[]; @@ -47,6 +48,7 @@ const getRowStyle = (cellData: RowClassParams) => { export const DirectoryContentTable = ({ gridRef, rows, + handleCellContextualMenu, handleRowSelected, handleCellClick, colDef, @@ -60,7 +62,7 @@ export const DirectoryContentTable = ({ rowSelection="multiple" suppressRowClickSelection onGridReady={onGridReady} - onCellContextMenu={(e) => console.log('HMA', e)} + onCellContextMenu={handleCellContextualMenu} onCellClicked={handleCellClick} onRowSelected={handleRowSelected} animateRows={true} diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 6883abdf6..0be741c63 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -53,7 +53,7 @@ import { isRowUnchecked, } from './utils/directory-content-utils'; import NoContentDirectory from './no-content-directory'; -import { DirectoryContentTable } from './DirectoryContentTable'; +import { DirectoryContentTable } from './directory-content-table'; const circularProgressSize = '70px'; @@ -266,42 +266,36 @@ const DirectoryContent = () => { ); const contextualMixPolicy = contextualMixPolicies.ALL; - const getRowHovered = () => { - return gridRef.current?.api - ?.getRenderedNodes() - .find((node) => node.hovered); - }; - const onCellContextMenu = useCallback( - (event, row) => { - if (row.data && row.data.uploading !== null) { - if (row.data.type !== 'DIRECTORY') { + (event) => { + if (event.data && event.data.uploading !== null) { + if (event.data.type !== 'DIRECTORY') { setActiveElement({ hasMetadata: - childrenMetadata[row.data.elementUuid] !== + childrenMetadata[event.data.elementUuid] !== undefined, specificMetadata: - childrenMetadata[row.data.elementUuid] + childrenMetadata[event.data.elementUuid] ?.specificMetadata, - ...row.data, + ...event.data, }); if (contextualMixPolicy === contextualMixPolicies.BIG) { // If some elements were already selected and the active element is not in them, we deselect the already selected elements. - if (isRowUnchecked(row.data, checkedRows)) { + if (isRowUnchecked(event.data, checkedRows)) { gridRef.current?.api.deselectAll(); } } else { // If some elements were already selected, we add the active element to the selected list if not already in it. - if (isRowUnchecked(row.data, checkedRows)) { + if (isRowUnchecked(event.data, checkedRows)) { gridRef.current?.api - .getRowNode(row.data.elementUuid) + .getRowNode(event.data.elementUuid) .setSelected(true); } } } setMousePosition({ - mouseX: event.clientX + constants.HORIZONTAL_SHIFT, - mouseY: event.clientY + constants.VERTICAL_SHIFT, + mouseX: event.event.clientX + constants.HORIZONTAL_SHIFT, + mouseY: event.event.clientY + constants.VERTICAL_SHIFT, }); handleOpenContentMenu(event.event); } @@ -316,10 +310,8 @@ const DirectoryContent = () => { const onContextMenu = useCallback( (event) => { - const row = getRowHovered(); - if (row) { - onCellContextMenu(event, row); - } else { + const isRow = !!event.target.closest('.ag-cell'); + if (!isRow) { if (selectedDirectory) { dispatch(setActiveDirectory(selectedDirectory.elementUuid)); } @@ -330,7 +322,7 @@ const DirectoryContent = () => { handleOpenDirectoryMenu(event); } }, - [dispatch, onCellContextMenu, selectedDirectory] + [dispatch, selectedDirectory] ); const handleError = useCallback( @@ -558,6 +550,7 @@ const DirectoryContent = () => { { } broadcastChannel={broadcastChannel} /> - Date: Thu, 6 Jun 2024 09:12:43 +0200 Subject: [PATCH 27/46] Set custom class --- src/components/directory-content-table.tsx | 2 ++ src/components/directory-content.jsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/directory-content-table.tsx b/src/components/directory-content-table.tsx index 57ec2e378..7ccc0add0 100644 --- a/src/components/directory-content-table.tsx +++ b/src/components/directory-content-table.tsx @@ -68,6 +68,8 @@ export const DirectoryContentTable = ({ animateRows={true} columnDefs={colDef} getRowStyle={getRowStyle} + //We set a custom className for rows in order to easily determine if a context menu event is happening on a row or not + rowClass={'custom-row-class'} /> ); }; diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 0be741c63..5c189421e 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -310,7 +310,7 @@ const DirectoryContent = () => { const onContextMenu = useCallback( (event) => { - const isRow = !!event.target.closest('.ag-cell'); + const isRow = !!event.target.closest('.custom-row-class'); if (!isRow) { if (selectedDirectory) { dispatch(setActiveDirectory(selectedDirectory.elementUuid)); From 69116c93b27992ef5ce6a3be9bfa3916d14c9ccc Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 6 Jun 2024 09:25:59 +0200 Subject: [PATCH 28/46] Set custom class const --- src/components/directory-content-table.tsx | 4 +++- src/components/directory-content.jsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/directory-content-table.tsx b/src/components/directory-content-table.tsx index 7ccc0add0..a3116e0c2 100644 --- a/src/components/directory-content-table.tsx +++ b/src/components/directory-content-table.tsx @@ -28,6 +28,8 @@ const onGridReady = ({ api }: GridReadyEvent) => { const getRowId = (params: GetRowIdParams) => params.data?.elementUuid; +export const CUSTOM_ROW_CLASS = 'custom-row-class' + const getRowStyle = (cellData: RowClassParams) => { const style: Record = { fontSize: '1rem' }; if ( @@ -69,7 +71,7 @@ export const DirectoryContentTable = ({ columnDefs={colDef} getRowStyle={getRowStyle} //We set a custom className for rows in order to easily determine if a context menu event is happening on a row or not - rowClass={'custom-row-class'} + rowClass={CUSTOM_ROW_CLASS} /> ); }; diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 5c189421e..57c496aa4 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -53,7 +53,7 @@ import { isRowUnchecked, } from './utils/directory-content-utils'; import NoContentDirectory from './no-content-directory'; -import { DirectoryContentTable } from './directory-content-table'; +import { DirectoryContentTable, CUSTOM_ROW_CLASS } from './directory-content-table'; const circularProgressSize = '70px'; @@ -310,7 +310,7 @@ const DirectoryContent = () => { const onContextMenu = useCallback( (event) => { - const isRow = !!event.target.closest('.custom-row-class'); + const isRow = !!event.target.closest(`.${CUSTOM_ROW_CLASS}`); if (!isRow) { if (selectedDirectory) { dispatch(setActiveDirectory(selectedDirectory.elementUuid)); From 965bf136f07824dc1972a49422cfdd066bd7e932 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 6 Jun 2024 09:26:41 +0200 Subject: [PATCH 29/46] Lint --- src/components/directory-content-table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/directory-content-table.tsx b/src/components/directory-content-table.tsx index a3116e0c2..1d78db885 100644 --- a/src/components/directory-content-table.tsx +++ b/src/components/directory-content-table.tsx @@ -28,7 +28,7 @@ const onGridReady = ({ api }: GridReadyEvent) => { const getRowId = (params: GetRowIdParams) => params.data?.elementUuid; -export const CUSTOM_ROW_CLASS = 'custom-row-class' +export const CUSTOM_ROW_CLASS = 'custom-row-class'; const getRowStyle = (cellData: RowClassParams) => { const style: Record = { fontSize: '1rem' }; From 0dede229174a9188cda4ea17e016d9ac255e0566 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Thu, 6 Jun 2024 16:06:08 +0200 Subject: [PATCH 30/46] Restore type tooltip --- src/components/utils/renderers/type-cell-renderer.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/utils/renderers/type-cell-renderer.tsx b/src/components/utils/renderers/type-cell-renderer.tsx index a126244f8..04e0ed75c 100644 --- a/src/components/utils/renderers/type-cell-renderer.tsx +++ b/src/components/utils/renderers/type-cell-renderer.tsx @@ -49,6 +49,11 @@ const styles = { tooltip: { maxWidth: '1000px', }, + tableCell: { + fontSize: '1rem', + display: 'flex', + alignItems: 'center', + }, }; export const TypeCellRenderer = ({ @@ -61,7 +66,7 @@ export const TypeCellRenderer = ({ const intl = useIntl(); return ( childrenMetadata[data?.elementUuid] && ( - + Date: Thu, 6 Jun 2024 16:19:42 +0200 Subject: [PATCH 31/46] Add comment --- src/components/directory-content.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 57c496aa4..784e604f2 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -310,6 +310,7 @@ const DirectoryContent = () => { const onContextMenu = useCallback( (event) => { + //We check if the context menu was triggered from a row to prevent displaying both the directory and the content context menus const isRow = !!event.target.closest(`.${CUSTOM_ROW_CLASS}`); if (!isRow) { if (selectedDirectory) { From a0d1eb88115b8d9b8edacbcc085e9ba1d9940f0b Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 10:21:12 +0200 Subject: [PATCH 32/46] Lint --- src/components/directory-content.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/directory-content.jsx b/src/components/directory-content.jsx index 784e604f2..af7051699 100644 --- a/src/components/directory-content.jsx +++ b/src/components/directory-content.jsx @@ -53,7 +53,10 @@ import { isRowUnchecked, } from './utils/directory-content-utils'; import NoContentDirectory from './no-content-directory'; -import { DirectoryContentTable, CUSTOM_ROW_CLASS } from './directory-content-table'; +import { + DirectoryContentTable, + CUSTOM_ROW_CLASS, +} from './directory-content-table'; const circularProgressSize = '70px'; From 3a5b7be1edb759176d4af9bb16149b3c1e87e985 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 16:44:03 +0200 Subject: [PATCH 33/46] Fix typing --- src/hooks/useDirectoryContent.ts | 6 +++--- src/redux/reducer.type.ts | 6 ++---- src/utils/rest-api.js | 20 ++++++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index e27b00c48..140620d1b 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -8,9 +8,9 @@ import { useSelector } from 'react-redux'; import React, { useRef, useEffect, useCallback, useState } from 'react'; import { useSnackMessage } from '@gridsuite/commons-ui'; -import { fetchElementsInfos } from '../utils/rest-api'; import { UUID } from 'crypto'; import { IElement, IElementMetadata, ReduxState } from '../redux/reducer.type'; +import { fetchElementsInfos } from '../utils/rest-api'; export const useDirectoryContent = ( setIsMissingDataAfterDirChange: React.Dispatch< @@ -49,8 +49,8 @@ export const useDirectoryContent = ( .map((e) => e.elementUuid); if (childrenToFetchElementsInfos.length > 0) { fetchElementsInfos(childrenToFetchElementsInfos) - .then((res: IElementMetadata[]) => { - res.forEach((e) => { + .then((res) => { + res.forEach((e: IElementMetadata) => { metadata[e.elementUuid] = e; }); }) diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index bbfb54666..3119e7118 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { UUID } from 'crypto'; -import { ElementType } from '@gridsuite/commons-ui'; +import { ElementAttributes, ElementType } from '@gridsuite/commons-ui'; type UserProfile = { sub: string; @@ -38,9 +38,7 @@ export interface IElement { uploading?: boolean; } -export interface IElementMetadata { - elementUuid: UUID; - elementName: string; +export interface IElementMetadata extends ElementAttributes { specificMetadata: { type: string; equipmentType: string; diff --git a/src/utils/rest-api.js b/src/utils/rest-api.js index 9265ba50e..fc5b9ec34 100644 --- a/src/utils/rest-api.js +++ b/src/utils/rest-api.js @@ -858,3 +858,23 @@ export function searchElementsInfos(searchTerm) { urlSearchParams.toString() ); } + +//Temporaly added here to be deleted once IElementMetadata is added to fetchElementsInfos in commons ui +export function fetchElementsInfos(ids, elementTypes, _equipmentTypes) { + console.info('Fetching elements metadata ... '); + + // Add params to Url + const tmp = ids?.filter((id) => id); + const idsParams = tmp?.length ? tmp.map((id) => ['ids', id]) : []; + const elementTypesParams = elementTypes?.length + ? elementTypes.map((type) => ['elementTypes', type]) + : []; + const params = [...idsParams, ...elementTypesParams]; + const urlSearchParams = new URLSearchParams(params).toString(); + + const fetchElementsInfosUrl = + PREFIX_EXPLORE_SERVER_QUERIES + + '/v1/explore/elements/metadata?' + + urlSearchParams; + return backendFetchJson(fetchElementsInfosUrl); +} From b679a20d0453e6be8e157d0d0c63c1dac41de174 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 16:45:11 +0200 Subject: [PATCH 34/46] Typo --- src/utils/rest-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/rest-api.js b/src/utils/rest-api.js index fc5b9ec34..b22b68011 100644 --- a/src/utils/rest-api.js +++ b/src/utils/rest-api.js @@ -859,7 +859,7 @@ export function searchElementsInfos(searchTerm) { ); } -//Temporaly added here to be deleted once IElementMetadata is added to fetchElementsInfos in commons ui +//Temporally added here to be deleted once IElementMetadata is added to fetchElementsInfos in commons ui export function fetchElementsInfos(ids, elementTypes, _equipmentTypes) { console.info('Fetching elements metadata ... '); From f76ced215554784a13173a271d2c000e2c7da0e8 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 16:51:50 +0200 Subject: [PATCH 35/46] Comment --- src/utils/rest-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/rest-api.js b/src/utils/rest-api.js index b22b68011..efa27c9ee 100644 --- a/src/utils/rest-api.js +++ b/src/utils/rest-api.js @@ -859,7 +859,7 @@ export function searchElementsInfos(searchTerm) { ); } -//Temporally added here to be deleted once IElementMetadata is added to fetchElementsInfos in commons ui +//Temporally added here to be deleted once IElementMetadata is added to fetchElementsInfos return type in commons ui export function fetchElementsInfos(ids, elementTypes, _equipmentTypes) { console.info('Fetching elements metadata ... '); From 24c3b9fb9c85feae0db28e53dc3d1d9e0e58aff2 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 16:54:03 +0200 Subject: [PATCH 36/46] Fix --- src/redux/reducer.type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index 3119e7118..68b14d198 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -26,7 +26,7 @@ interface IUser { export interface IElement { elementUuid: UUID; elementName: string; - description: string; + description?: string; type: ElementType; owner: string; subdirectoriesCount: number; From a7201522a07413caa28e59b11b743ea8a094ca70 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 17:08:48 +0200 Subject: [PATCH 37/46] Tweaks --- src/hooks/useDirectoryContent.ts | 11 +++++------ src/redux/reducer.type.ts | 10 +--------- src/utils/rest-api.js | 20 -------------------- 3 files changed, 6 insertions(+), 35 deletions(-) diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index 140620d1b..e64b7234e 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -7,10 +7,9 @@ import { useSelector } from 'react-redux'; import React, { useRef, useEffect, useCallback, useState } from 'react'; -import { useSnackMessage } from '@gridsuite/commons-ui'; +import { ElementAttributes, fetchElementsInfos, useSnackMessage } from '@gridsuite/commons-ui'; import { UUID } from 'crypto'; -import { IElement, IElementMetadata, ReduxState } from '../redux/reducer.type'; -import { fetchElementsInfos } from '../utils/rest-api'; +import { IElement, ReduxState } from '../redux/reducer.type'; export const useDirectoryContent = ( setIsMissingDataAfterDirChange: React.Dispatch< @@ -21,7 +20,7 @@ export const useDirectoryContent = ( (state: ReduxState) => state.currentChildren ); const [childrenMetadata, setChildrenMetadata] = useState< - Record + Record >({}); const { snackError } = useSnackMessage(); const previousData = useRef(); @@ -43,14 +42,14 @@ export const useDirectoryContent = ( return; } - let metadata: Record = {}; + let metadata: Record = {}; let childrenToFetchElementsInfos = Object.values(currentChildren) .filter((e) => !e.uploading) .map((e) => e.elementUuid); if (childrenToFetchElementsInfos.length > 0) { fetchElementsInfos(childrenToFetchElementsInfos) .then((res) => { - res.forEach((e: IElementMetadata) => { + res.forEach((e) => { metadata[e.elementUuid] = e; }); }) diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index 68b14d198..0666afe73 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -26,7 +26,7 @@ interface IUser { export interface IElement { elementUuid: UUID; elementName: string; - description?: string; + description: string; type: ElementType; owner: string; subdirectoriesCount: number; @@ -38,14 +38,6 @@ export interface IElement { uploading?: boolean; } -export interface IElementMetadata extends ElementAttributes { - specificMetadata: { - type: string; - equipmentType: string; - format: string | null; - }; -} - // IDirectory is exactly an IElement, with a specific type value export type IDirectory = IElement & { type: ElementType.DIRECTORY; diff --git a/src/utils/rest-api.js b/src/utils/rest-api.js index efa27c9ee..9265ba50e 100644 --- a/src/utils/rest-api.js +++ b/src/utils/rest-api.js @@ -858,23 +858,3 @@ export function searchElementsInfos(searchTerm) { urlSearchParams.toString() ); } - -//Temporally added here to be deleted once IElementMetadata is added to fetchElementsInfos return type in commons ui -export function fetchElementsInfos(ids, elementTypes, _equipmentTypes) { - console.info('Fetching elements metadata ... '); - - // Add params to Url - const tmp = ids?.filter((id) => id); - const idsParams = tmp?.length ? tmp.map((id) => ['ids', id]) : []; - const elementTypesParams = elementTypes?.length - ? elementTypes.map((type) => ['elementTypes', type]) - : []; - const params = [...idsParams, ...elementTypesParams]; - const urlSearchParams = new URLSearchParams(params).toString(); - - const fetchElementsInfosUrl = - PREFIX_EXPLORE_SERVER_QUERIES + - '/v1/explore/elements/metadata?' + - urlSearchParams; - return backendFetchJson(fetchElementsInfosUrl); -} From 074130a12a317b47f17d9180318b432cd2d097c3 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 17:11:19 +0200 Subject: [PATCH 38/46] Tweaks --- src/components/utils/directory-content-utils.ts | 13 +++++++------ .../utils/renderers/name-cell-renderer.tsx | 7 ++++--- .../utils/renderers/type-cell-renderer.tsx | 10 +++++++--- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index d1b406d31..c34f4485d 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -7,7 +7,7 @@ import { IntlShape } from 'react-intl'; import { UUID } from 'crypto'; -import { IElement, IElementMetadata } from '../../redux/reducer.type'; +import { IElement } from '../../redux/reducer.type'; import { AgGridReact } from 'ag-grid-react'; import React from 'react'; import { ColDef } from 'ag-grid-community'; @@ -16,10 +16,11 @@ import { DescriptionCellRenderer } from './renderers/description-cell-renderer'; import { TypeCellRenderer } from './renderers/type-cell-renderer'; import { UserCellRenderer } from './renderers/user-cell-renderer'; import { DateCellRenderer } from './renderers/date-cell-renderer'; +import type { ElementAttributes } from '@gridsuite/commons-ui'; export const formatMetadata = ( - data: IElementMetadata, - childrenMetadata: Record + data: ElementAttributes, + childrenMetadata: Record ) => ({ ...data, subtype: childrenMetadata[data.elementUuid]?.specificMetadata.type, @@ -28,12 +29,12 @@ export const formatMetadata = ( export const computeCheckedElements = ( gridRef: React.MutableRefObject, - childrenMetadata: Record + childrenMetadata: Record ) => { return ( gridRef.current?.api ?.getSelectedRows() - .map((row: IElementMetadata) => + .map((row: ElementAttributes) => formatMetadata(row, childrenMetadata) ) ?? [] ); @@ -56,7 +57,7 @@ export const defaultColumnDefinition = { flex: 1, }; export const getColumnsDefinition = ( - childrenMetadata: Record, + childrenMetadata: Record, intl: IntlShape ): ColDef[] => [ { diff --git a/src/components/utils/renderers/name-cell-renderer.tsx b/src/components/utils/renderers/name-cell-renderer.tsx index 8ef3e6473..e1aae06ee 100644 --- a/src/components/utils/renderers/name-cell-renderer.tsx +++ b/src/components/utils/renderers/name-cell-renderer.tsx @@ -4,7 +4,7 @@ * 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 { IElement, IElementMetadata } from '../../../redux/reducer.type'; +import { IElement } from '../../../redux/reducer.type'; import { UUID } from 'crypto'; import { IntlShape, useIntl } from 'react-intl'; import { Box, Theme } from '@mui/material'; @@ -13,6 +13,7 @@ import { ElementType, getFileIcon, OverflowableText, + ElementAttributes } from '@gridsuite/commons-ui'; const isElementCaseOrStudy = (objectType: ElementType) => { @@ -21,7 +22,7 @@ const isElementCaseOrStudy = (objectType: ElementType) => { const getDisplayedElementName = ( data: IElement, - childrenMetadata: Record, + childrenMetadata: Record, intl: IntlShape ) => { const { elementName, uploading, elementUuid } = data; @@ -66,7 +67,7 @@ export const NameCellRenderer = ({ childrenMetadata, }: { data: IElement; - childrenMetadata: Record; + childrenMetadata: Record; }) => { const intl = useIntl(); return ( diff --git a/src/components/utils/renderers/type-cell-renderer.tsx b/src/components/utils/renderers/type-cell-renderer.tsx index 04e0ed75c..73c98d0cb 100644 --- a/src/components/utils/renderers/type-cell-renderer.tsx +++ b/src/components/utils/renderers/type-cell-renderer.tsx @@ -4,9 +4,13 @@ * 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 { ElementType, OverflowableText } from '@gridsuite/commons-ui'; +import { + ElementType, + OverflowableText, + ElementAttributes, +} from '@gridsuite/commons-ui'; import { IntlShape, useIntl } from 'react-intl'; -import { IElement, IElementMetadata } from '../../../redux/reducer.type'; +import { IElement } from '../../../redux/reducer.type'; import { UUID } from 'crypto'; import { Box } from '@mui/material'; @@ -61,7 +65,7 @@ export const TypeCellRenderer = ({ childrenMetadata, }: { data: IElement; - childrenMetadata: Record; + childrenMetadata: Record; }) => { const intl = useIntl(); return ( From 5bad6bfb41979a58bac202c72a846f9c3bb9fd44 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 17:12:11 +0200 Subject: [PATCH 39/46] Tweaks --- src/components/utils/renderers/name-cell-renderer.tsx | 2 +- src/hooks/useDirectoryContent.ts | 6 +++++- src/redux/reducer.type.ts | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/utils/renderers/name-cell-renderer.tsx b/src/components/utils/renderers/name-cell-renderer.tsx index e1aae06ee..3c9c0ca3c 100644 --- a/src/components/utils/renderers/name-cell-renderer.tsx +++ b/src/components/utils/renderers/name-cell-renderer.tsx @@ -13,7 +13,7 @@ import { ElementType, getFileIcon, OverflowableText, - ElementAttributes + ElementAttributes, } from '@gridsuite/commons-ui'; const isElementCaseOrStudy = (objectType: ElementType) => { diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index e64b7234e..2b7324af4 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -7,7 +7,11 @@ import { useSelector } from 'react-redux'; import React, { useRef, useEffect, useCallback, useState } from 'react'; -import { ElementAttributes, fetchElementsInfos, useSnackMessage } from '@gridsuite/commons-ui'; +import { + ElementAttributes, + fetchElementsInfos, + useSnackMessage, +} from '@gridsuite/commons-ui'; import { UUID } from 'crypto'; import { IElement, ReduxState } from '../redux/reducer.type'; diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index 0666afe73..560015e91 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { UUID } from 'crypto'; -import { ElementAttributes, ElementType } from '@gridsuite/commons-ui'; +import { ElementType } from '@gridsuite/commons-ui'; type UserProfile = { sub: string; From d126aab86a6dfc930ded8724526d888a08833a15 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 17:14:50 +0200 Subject: [PATCH 40/46] Rollback changes --- src/redux/reducer.type.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index 560015e91..bc804d4a6 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -4,8 +4,8 @@ * 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 { UUID } from 'crypto'; import { ElementType } from '@gridsuite/commons-ui'; +import { UUID } from 'crypto'; type UserProfile = { sub: string; @@ -26,7 +26,6 @@ interface IUser { export interface IElement { elementUuid: UUID; elementName: string; - description: string; type: ElementType; owner: string; subdirectoriesCount: number; @@ -35,7 +34,6 @@ export interface IElement { lastModifiedBy: string; children: any[]; parentUuid: null | UUID; - uploading?: boolean; } // IDirectory is exactly an IElement, with a specific type value From 4b7228785026ea07f24d2828ba2f665b9acca798 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 17:22:59 +0200 Subject: [PATCH 41/46] Tweaks --- .../utils/renderers/description-cell-renderer.tsx | 8 ++++++-- src/components/utils/renderers/name-cell-renderer.tsx | 5 ++--- src/components/utils/renderers/type-cell-renderer.tsx | 3 +-- src/hooks/useDirectoryContent.ts | 4 ++-- src/redux/reducer.type.ts | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/components/utils/renderers/description-cell-renderer.tsx b/src/components/utils/renderers/description-cell-renderer.tsx index 8b3b5e24d..d88c68018 100644 --- a/src/components/utils/renderers/description-cell-renderer.tsx +++ b/src/components/utils/renderers/description-cell-renderer.tsx @@ -4,11 +4,11 @@ * 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 { IElement } from '../../../redux/reducer.type'; import Tooltip from '@mui/material/Tooltip'; import { Box } from '@mui/material'; import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined'; import CreateIcon from '@mui/icons-material/Create'; +import { ElementAttributes } from '@gridsuite/commons-ui'; const styles = { descriptionTooltip: { @@ -22,7 +22,11 @@ const styles = { }, }; -export const DescriptionCellRenderer = ({ data }: { data: IElement }) => { +export const DescriptionCellRenderer = ({ + data, +}: { + data: ElementAttributes; +}) => { const description = data.description; const descriptionLines = description?.split('\n'); if (descriptionLines?.length > 3) { diff --git a/src/components/utils/renderers/name-cell-renderer.tsx b/src/components/utils/renderers/name-cell-renderer.tsx index 3c9c0ca3c..90571f6a7 100644 --- a/src/components/utils/renderers/name-cell-renderer.tsx +++ b/src/components/utils/renderers/name-cell-renderer.tsx @@ -4,7 +4,6 @@ * 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 { IElement } from '../../../redux/reducer.type'; import { UUID } from 'crypto'; import { IntlShape, useIntl } from 'react-intl'; import { Box, Theme } from '@mui/material'; @@ -21,7 +20,7 @@ const isElementCaseOrStudy = (objectType: ElementType) => { }; const getDisplayedElementName = ( - data: IElement, + data: ElementAttributes, childrenMetadata: Record, intl: IntlShape ) => { @@ -66,7 +65,7 @@ export const NameCellRenderer = ({ data, childrenMetadata, }: { - data: IElement; + data: ElementAttributes; childrenMetadata: Record; }) => { const intl = useIntl(); diff --git a/src/components/utils/renderers/type-cell-renderer.tsx b/src/components/utils/renderers/type-cell-renderer.tsx index 73c98d0cb..5a54f1733 100644 --- a/src/components/utils/renderers/type-cell-renderer.tsx +++ b/src/components/utils/renderers/type-cell-renderer.tsx @@ -10,7 +10,6 @@ import { ElementAttributes, } from '@gridsuite/commons-ui'; import { IntlShape, useIntl } from 'react-intl'; -import { IElement } from '../../../redux/reducer.type'; import { UUID } from 'crypto'; import { Box } from '@mui/material'; @@ -64,7 +63,7 @@ export const TypeCellRenderer = ({ data, childrenMetadata, }: { - data: IElement; + data: ElementAttributes; childrenMetadata: Record; }) => { const intl = useIntl(); diff --git a/src/hooks/useDirectoryContent.ts b/src/hooks/useDirectoryContent.ts index 2b7324af4..9f8f4ca2a 100644 --- a/src/hooks/useDirectoryContent.ts +++ b/src/hooks/useDirectoryContent.ts @@ -13,7 +13,7 @@ import { useSnackMessage, } from '@gridsuite/commons-ui'; import { UUID } from 'crypto'; -import { IElement, ReduxState } from '../redux/reducer.type'; +import { ReduxState } from '../redux/reducer.type'; export const useDirectoryContent = ( setIsMissingDataAfterDirChange: React.Dispatch< @@ -27,7 +27,7 @@ export const useDirectoryContent = ( Record >({}); const { snackError } = useSnackMessage(); - const previousData = useRef(); + const previousData = useRef(); previousData.current = currentChildren; const handleError = useCallback( diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index bc804d4a6..516175c69 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -4,7 +4,7 @@ * 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 { ElementType } from '@gridsuite/commons-ui'; +import { ElementAttributes, ElementType } from '@gridsuite/commons-ui'; import { UUID } from 'crypto'; type UserProfile = { @@ -48,7 +48,7 @@ export interface ITreeData { export interface ReduxState { activeDirectory: UUID; - currentChildren: IElement[]; + currentChildren: ElementAttributes[]; selectedDirectory: IElement; treeData: ITreeData; user: IUser; From 05ec3711a2ca018694cb8954295c884dc6433420 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 17:24:06 +0200 Subject: [PATCH 42/46] Tweaks --- src/redux/reducer.type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index 516175c69..d18e35587 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -49,7 +49,7 @@ export interface ITreeData { export interface ReduxState { activeDirectory: UUID; currentChildren: ElementAttributes[]; - selectedDirectory: IElement; + selectedDirectory: ElementAttributes; treeData: ITreeData; user: IUser; } From ab775c93d6b8f52efe9c29233b0292105f1da5a2 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 18:13:28 +0200 Subject: [PATCH 43/46] Generalize ElementAttributes --- src/components/directory-content-table.tsx | 18 +++++++++++------- src/components/search/search-bar.tsx | 10 +++------- .../utils/directory-content-utils.ts | 6 ++++-- src/redux/reducer.type.ts | 15 +-------------- 4 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/components/directory-content-table.tsx b/src/components/directory-content-table.tsx index 1d78db885..07c080077 100644 --- a/src/components/directory-content-table.tsx +++ b/src/components/directory-content-table.tsx @@ -6,31 +6,35 @@ */ import { defaultColumnDefinition } from './utils/directory-content-utils'; -import { CustomAGGrid, ElementType } from '@gridsuite/commons-ui'; +import { + CustomAGGrid, + ElementType, + ElementAttributes, +} from '@gridsuite/commons-ui'; import { AgGridReact } from 'ag-grid-react'; -import { IElement } from '../redux/reducer.type'; import { GetRowIdParams } from 'ag-grid-community/dist/types/core/interfaces/iCallbackParams'; import { ColDef, GridReadyEvent, RowClassParams } from 'ag-grid-community'; import { RefObject } from 'react'; interface DirectoryContentTableProps { - gridRef: RefObject>; - rows: IElement[]; + gridRef: RefObject>; + rows: ElementAttributes[]; handleCellContextualMenu: () => void; handleRowSelected: () => void; handleCellClick: () => void; colDef: ColDef[]; } -const onGridReady = ({ api }: GridReadyEvent) => { +const onGridReady = ({ api }: GridReadyEvent) => { api?.sizeColumnsToFit(); }; -const getRowId = (params: GetRowIdParams) => params.data?.elementUuid; +const getRowId = (params: GetRowIdParams) => + params.data?.elementUuid; export const CUSTOM_ROW_CLASS = 'custom-row-class'; -const getRowStyle = (cellData: RowClassParams) => { +const getRowStyle = (cellData: RowClassParams) => { const style: Record = { fontSize: '1rem' }; if ( cellData.data && diff --git a/src/components/search/search-bar.tsx b/src/components/search/search-bar.tsx index b48765c93..5a536aac4 100644 --- a/src/components/search/search-bar.tsx +++ b/src/components/search/search-bar.tsx @@ -21,6 +21,7 @@ import { useDebounce, useSnackMessage, fetchDirectoryContent, + ElementAttributes, } from '@gridsuite/commons-ui'; import { Search } from '@mui/icons-material'; import { useDispatch, useSelector } from 'react-redux'; @@ -28,12 +29,7 @@ import { setSelectedDirectory, setTreeData } from '../../redux/actions'; import { updatedTree } from '../tree-views-container'; import { useIntl } from 'react-intl'; import { SearchItem } from './search-item'; -import { - IDirectory, - IElement, - ITreeData, - ReduxState, -} from '../../redux/reducer.type'; +import { IDirectory, ITreeData, ReduxState } from '../../redux/reducer.type'; import { UUID } from 'crypto'; export const SEARCH_FETCH_TIMEOUT_MILLIS = 1000; // 1 second @@ -161,7 +157,7 @@ export const SearchBar: FunctionComponent = ({ inputRef }) => { const elementUuidPath = matchingElement?.pathUuid.reverse(); const promises = elementUuidPath.map((e: string) => { return fetchDirectoryContent(e as UUID) - .then((res: IElement[]) => { + .then((res: ElementAttributes[]) => { updateMapData( e, res.filter( diff --git a/src/components/utils/directory-content-utils.ts b/src/components/utils/directory-content-utils.ts index c34f4485d..ab778fdef 100644 --- a/src/components/utils/directory-content-utils.ts +++ b/src/components/utils/directory-content-utils.ts @@ -7,7 +7,6 @@ import { IntlShape } from 'react-intl'; import { UUID } from 'crypto'; -import { IElement } from '../../redux/reducer.type'; import { AgGridReact } from 'ag-grid-react'; import React from 'react'; import { ColDef } from 'ag-grid-community'; @@ -40,7 +39,10 @@ export const computeCheckedElements = ( ); }; -export const isRowUnchecked = (row: IElement, checkedRows: IElement[]) => +export const isRowUnchecked = ( + row: ElementAttributes, + checkedRows: ElementAttributes[] +) => checkedRows?.length && row?.elementUuid && !checkedRows.find( diff --git a/src/redux/reducer.type.ts b/src/redux/reducer.type.ts index d18e35587..6e8c4281d 100644 --- a/src/redux/reducer.type.ts +++ b/src/redux/reducer.type.ts @@ -23,21 +23,8 @@ interface IUser { expires_at: number; } -export interface IElement { - elementUuid: UUID; - elementName: string; - type: ElementType; - owner: string; - subdirectoriesCount: number; - creationDate: string; - lastModificationDate: string; - lastModifiedBy: string; - children: any[]; - parentUuid: null | UUID; -} - // IDirectory is exactly an IElement, with a specific type value -export type IDirectory = IElement & { +export type IDirectory = ElementAttributes & { type: ElementType.DIRECTORY; }; From 99d37593b8fe27e2272dfee2f44970b58ff9d0d6 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Fri, 7 Jun 2024 18:17:03 +0200 Subject: [PATCH 44/46] Remove not needed typing --- src/components/search/search-bar.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/search/search-bar.tsx b/src/components/search/search-bar.tsx index 5a536aac4..fe891ab1a 100644 --- a/src/components/search/search-bar.tsx +++ b/src/components/search/search-bar.tsx @@ -21,7 +21,6 @@ import { useDebounce, useSnackMessage, fetchDirectoryContent, - ElementAttributes, } from '@gridsuite/commons-ui'; import { Search } from '@mui/icons-material'; import { useDispatch, useSelector } from 'react-redux'; @@ -157,7 +156,7 @@ export const SearchBar: FunctionComponent = ({ inputRef }) => { const elementUuidPath = matchingElement?.pathUuid.reverse(); const promises = elementUuidPath.map((e: string) => { return fetchDirectoryContent(e as UUID) - .then((res: ElementAttributes[]) => { + .then((res) => { updateMapData( e, res.filter( From ddeb037a5fc4ec9456a7299888ffbfaca1925191 Mon Sep 17 00:00:00 2001 From: Hugo Marcellin Date: Mon, 10 Jun 2024 16:47:33 +0200 Subject: [PATCH 45/46] Tweak --- src/components/utils/renderers/type-cell-renderer.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/utils/renderers/type-cell-renderer.tsx b/src/components/utils/renderers/type-cell-renderer.tsx index 5a54f1733..1258d67cf 100644 --- a/src/components/utils/renderers/type-cell-renderer.tsx +++ b/src/components/utils/renderers/type-cell-renderer.tsx @@ -73,10 +73,12 @@ export const TypeCellRenderer = ({ Date: Mon, 10 Jun 2024 17:18:43 +0200 Subject: [PATCH 46/46] Update commons ui --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5795ffd79..09424c909 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@gridsuite/commons-ui": "0.59.1", + "@gridsuite/commons-ui": "0.59.2", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "5.0.0-alpha.169", @@ -2955,9 +2955,9 @@ } }, "node_modules/@gridsuite/commons-ui": { - "version": "0.59.1", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.59.1.tgz", - "integrity": "sha512-q82yXqDTRb0L7StWbvnhsmUDw+q1U1ImWgzAHB7mmPoupf1LWucEnA2PR7RAfE6M8Pea9mKp74Cr+SsH6S6DPg==", + "version": "0.59.2", + "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.59.2.tgz", + "integrity": "sha512-w8UpzOWl5MjH+olf5w5ThV4gl2MqCQIVVY3oNac/wNj6vG4YWIFEOANhznYjphD/jFOm5A/yUVX9+jsCJqjWng==", "dependencies": { "@react-querybuilder/dnd": "^7.2.0", "@react-querybuilder/material": "^7.2.0", diff --git a/package.json b/package.json index 661157639..bbe47f757 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@gridsuite/commons-ui": "0.59.1", + "@gridsuite/commons-ui": "0.59.2", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "5.0.0-alpha.169",