From 88cc5d1642dfc5e9d8fb2f48d363fe6ad4500e0b Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Mon, 23 Mar 2026 13:23:56 +0100 Subject: [PATCH 1/4] adds network-modification-table Signed-off-by: Mathieu DEHARBE --- package-lock.json | 62 +++++ package.json | 2 + src/components/index.ts | 1 + .../columns-definition.tsx | 66 ++++++ .../network-modification-table/index.ts | 11 + .../network-modifications-table.tsx | 158 +++++++++++++ .../renderers/drag-handle-cell.tsx | 21 ++ .../renderers/name-cell.tsx | 41 ++++ ...k-modification-node-editor-name-header.tsx | 76 ++++++ .../renderers/select-cell.tsx | 80 +++++++ .../renderers/select-header-cell.tsx | 40 ++++ .../row/drag-row-clone.tsx | 33 +++ .../row/modification-row.tsx | 69 ++++++ .../network-modification-table/styles.ts | 216 ++++++++++++++++++ .../use-modifications-drag-and-drop.tsx | 81 +++++++ src/utils/types/network-modification-types.ts | 7 + 16 files changed, 964 insertions(+) create mode 100644 src/components/network-modification-table/columns-definition.tsx create mode 100644 src/components/network-modification-table/index.ts create mode 100644 src/components/network-modification-table/network-modifications-table.tsx create mode 100644 src/components/network-modification-table/renderers/drag-handle-cell.tsx create mode 100644 src/components/network-modification-table/renderers/name-cell.tsx create mode 100644 src/components/network-modification-table/renderers/network-modification-node-editor-name-header.tsx create mode 100644 src/components/network-modification-table/renderers/select-cell.tsx create mode 100644 src/components/network-modification-table/renderers/select-header-cell.tsx create mode 100644 src/components/network-modification-table/row/drag-row-clone.tsx create mode 100644 src/components/network-modification-table/row/modification-row.tsx create mode 100644 src/components/network-modification-table/styles.ts create mode 100644 src/components/network-modification-table/use-modifications-drag-and-drop.tsx diff --git a/package-lock.json b/package-lock.json index ca80a2778..6f3f5f4d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "@material-symbols/svg-400": "^0.31.9", "@react-querybuilder/dnd": "^8.11.0", "@react-querybuilder/material": "^8.11.0", + "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.18", "autosuggest-highlight": "^3.3.4", "clsx": "^2.1.1", "jwt-decode": "^4.0.0", @@ -6051,6 +6053,66 @@ "@svgr/core": "*" } }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.23", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.23.tgz", + "integrity": "sha512-XnMRnHQ23piOVj2bzJqHrRrLg4r+F86fuBcwteKfbIjJrtGxb4z7tIvPVAe4B+4UVwo9G4Giuz5fmapcrnZ0OQ==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.23" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.23", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.23.tgz", + "integrity": "sha512-zSz2Z2HNyLjCplANTDyl3BcdQJc2k1+yyFoKhNRmCr7V7dY8o8q5m8uFTI1/Pg1kL+Hgrz6u3Xo6eFUB7l66cg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", diff --git a/package.json b/package.json index 83ee2b939..1ce0974c0 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,8 @@ "react-resizable-panels": "^3.0.6", "reconnecting-websocket": "^4.4.0", "type-fest": "^4.41.0", + "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.18", "uuid": "^13.0.0" }, "peerDependencies": { diff --git a/src/components/index.ts b/src/components/index.ts index a60c49a3b..e0a874b1a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -30,3 +30,4 @@ export * from './menus'; export * from './muiTable'; export * from './resizablePanels'; export * from './network-modifications'; +export * from './network-modification-table'; diff --git a/src/components/network-modification-table/columns-definition.tsx b/src/components/network-modification-table/columns-definition.tsx new file mode 100644 index 000000000..c777cc422 --- /dev/null +++ b/src/components/network-modification-table/columns-definition.tsx @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React, { SetStateAction } from 'react'; +import { ColumnDef } from '@tanstack/react-table'; +import { + NetworkModificationEditorNameHeader, + NetworkModificationEditorNameHeaderProps, +} from './renderers/network-modification-node-editor-name-header'; +import NameCell from './renderers/name-cell'; +import { styles } from './styles'; +import { NetworkModificationMetadata } from '../../hooks'; + +export const BASE_MODIFICATION_TABLE_COLUMNS = { + DRAG_HANDLE: { + id: 'dragHandle', + autoExtensible: false, + }, + SELECT: { + id: 'select', + autoExtensible: false, + }, + NAME: { + id: 'modificationName', + autoExtensible: true, + }, + DESCRIPTION: { + id: 'modificationDescription', + autoExtensible: false, + }, + SWITCH: { + id: 'switch', + autoExtensible: false, + }, +}; + +export const AUTO_EXTENSIBLE_COLUMNS = Object.values(BASE_MODIFICATION_TABLE_COLUMNS) + .filter((column) => column.autoExtensible) + .map((column) => column.id); + +export type NameHeaderProps = Omit; + +// TODO : ceci devrait probablement être à la fois dans gridexplore et gridstudy avec uniquement les constantses en commun +// à séparer à la fin +export const createBaseColumns = ( + isRowDragDisabled: boolean, + modificationsCount: number, + nameHeaderProps: NameHeaderProps, + setModifications: React.Dispatch> +): ColumnDef[] => [ + { + id: BASE_MODIFICATION_TABLE_COLUMNS.NAME.id, + header: () => ( + + ), + cell: ({ row }) => , + meta: { + cellStyle: styles.columnCell.modificationName, + }, + minSize: 160, + }, +]; diff --git a/src/components/network-modification-table/index.ts b/src/components/network-modification-table/index.ts new file mode 100644 index 000000000..7f1e762ef --- /dev/null +++ b/src/components/network-modification-table/index.ts @@ -0,0 +1,11 @@ +/** +import NetworkModificationsTable from './network-modification-table/network-modifications-table'; + * Copyright (c) 2026, 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/. + */ + +export * from './columns-definition'; +export * from './network-modifications-table'; +export * from './use-modifications-drag-and-drop'; \ No newline at end of file diff --git a/src/components/network-modification-table/network-modifications-table.tsx b/src/components/network-modification-table/network-modifications-table.tsx new file mode 100644 index 000000000..aa6eab80d --- /dev/null +++ b/src/components/network-modification-table/network-modifications-table.tsx @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React, { Dispatch, SetStateAction, useEffect, useMemo, useRef } from 'react'; +import { Box, Table, TableBody, TableCell, TableHead, TableRow, useTheme } from '@mui/material'; +import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'; +import { DragDropContext, DragStart, Droppable, DroppableProvided, DropResult } from '@hello-pangea/dnd'; +import { useVirtualizer } from '@tanstack/react-virtual'; +import { UUID } from 'node:crypto'; +import { NetworkModificationEditorNameHeaderProps } from './renderers/network-modification-node-editor-name-header'; +import { createHeaderCellStyle, MODIFICATION_ROW_HEIGHT, styles } from './styles'; +import { AUTO_EXTENSIBLE_COLUMNS, NameHeaderProps } from './columns-definition'; +import ModificationRow from './row/modification-row'; +import { useModificationsDragAndDrop } from './use-modifications-drag-and-drop'; +import { NetworkModificationMetadata } from '../../hooks'; + +interface NetworkModificationsTableProps extends Omit { + modifications: NetworkModificationMetadata[]; + setModifications: Dispatch>; + handleCellClick: (modification: NetworkModificationMetadata) => void; + isRowDragDisabled?: boolean; + onRowDragStart: (event: DragStart) => void; + onRowDragEnd: (event: DropResult) => void; + onRowSelected: (selectedRows: NetworkModificationMetadata[]) => void; + createAllColumns: (isRowDragDisabled: boolean, + modificationsCount: number, + nameHeaderProps: NameHeaderProps, + setModifications: React.Dispatch> + ) => ColumnDef[]; + highlightedModificationUuid: UUID | null; +} + +export function NetworkModificationsTable({ + modifications, + setModifications, + handleCellClick, + isRowDragDisabled = false, + onRowDragStart, + onRowDragEnd, + onRowSelected, + createAllColumns, + highlightedModificationUuid, + ...nameHeaderProps + }: Readonly) { + const theme = useTheme(); + + const containerRef = useRef(null); + const lastClickedIndex = useRef(null); + + const columns = useMemo[]>(() => + createAllColumns(isRowDragDisabled ?? false, + modifications.length, + nameHeaderProps, + setModifications) + , [ + isRowDragDisabled, + modifications, + nameHeaderProps, + setModifications, +]); + + const table = useReactTable({ + data: modifications, + columns, + getCoreRowModel: getCoreRowModel(), + getRowId: (row:any) => row.uuid, + enableRowSelection: true, + meta: { lastClickedIndex, onRowSelected }, + }); + + const { rows } = table.getRowModel(); + + const virtualizer = useVirtualizer({ + count: rows.length, + getScrollElement: () => containerRef.current, + overscan: 5, + estimateSize: () => MODIFICATION_ROW_HEIGHT, + }); + const virtualItems = virtualizer.getVirtualItems(); + + const { handleDragUpdate, handleDragEnd, renderClone } = useModificationsDragAndDrop({ + rows, + containerRef, + onRowDragEnd, + }); + + useEffect(() => { + table.resetRowSelection(); + lastClickedIndex.current = null; + }, [table]); + + useEffect(() => { + if (highlightedModificationUuid && containerRef.current) { + const rowIndex = rows.findIndex((row) => row.original.uuid === highlightedModificationUuid); + if (rowIndex !== -1) { + virtualizer.scrollToIndex(rowIndex, { align: 'start', behavior: 'auto' }); + } + } + }, [highlightedModificationUuid, rows, virtualizer]); + + return ( + + + + {(provided: DroppableProvided) => ( + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {flexRender(header.column.columnDef.header, header.getContext())} + + ))} + + ))} + + + {virtualItems.map((virtualRow) => { + const row = rows[virtualRow.index]; + return ( + + ); + })} + +
+
+ )} +
+
+
+ ); +} diff --git a/src/components/network-modification-table/renderers/drag-handle-cell.tsx b/src/components/network-modification-table/renderers/drag-handle-cell.tsx new file mode 100644 index 000000000..f4f161cb5 --- /dev/null +++ b/src/components/network-modification-table/renderers/drag-handle-cell.tsx @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2026, 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 { FunctionComponent } from 'react'; +import { styles } from '../styles'; +import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; + +const DragHandleCell: FunctionComponent<{ isRowDragDisabled: boolean }> = ({ isRowDragDisabled }) => { + return ( + + {!isRowDragDisabled && } + + ); +}; + +export default DragHandleCell; diff --git a/src/components/network-modification-table/renderers/name-cell.tsx b/src/components/network-modification-table/renderers/name-cell.tsx new file mode 100644 index 000000000..0474dc70a --- /dev/null +++ b/src/components/network-modification-table/renderers/name-cell.tsx @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React, { FunctionComponent, useCallback, useMemo } from 'react'; +import { Row } from '@tanstack/react-table'; +import { useIntl } from 'react-intl'; +import { Box, Tooltip } from '@mui/material'; +import { createModificationNameCellStyle, styles } from '../styles'; +import { NetworkModificationMetadata, useModificationLabelComputer } from '../../../hooks'; +import { mergeSx } from '../../../utils'; + +const NameCell: FunctionComponent<{ row: Row }> = ({ row }) => { + const intl = useIntl(); + const { computeLabel } = useModificationLabelComputer(); + + const getModificationLabel = useCallback( + (modification: NetworkModificationMetadata, formatBold: boolean = true) => { + return intl.formatMessage( + { id: `network_modifications.${modification.messageType}` }, + { ...modification, ...computeLabel(modification, formatBold) } + ); + }, + [computeLabel, intl] + ); + + const label = useMemo(() => getModificationLabel(row.original), [getModificationLabel, row.original]); + + return ( + + + {label} + + + ); +}; + +export default NameCell; diff --git a/src/components/network-modification-table/renderers/network-modification-node-editor-name-header.tsx b/src/components/network-modification-table/renderers/network-modification-node-editor-name-header.tsx new file mode 100644 index 000000000..d35fa27c4 --- /dev/null +++ b/src/components/network-modification-table/renderers/network-modification-node-editor-name-header.tsx @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2025, 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, CircularProgress, Typography } from '@mui/material'; +import { FormattedMessage } from 'react-intl'; +import { FunctionComponent } from 'react'; +import { styles } from '../styles'; + +export interface NetworkModificationEditorNameHeaderProps { + modificationCount?: number; + notificationMessageId?: string; + isFetchingModifications: boolean; + isImpactedByNotification: () => boolean; + pendingState: boolean; +} + +export const NetworkModificationEditorNameHeader: FunctionComponent = ( + props +) => { + const { + modificationCount, + isFetchingModifications, + isImpactedByNotification, + notificationMessageId, + pendingState, + } = props; + + if (isImpactedByNotification() && notificationMessageId) { + return ( + + + + + + + + + ); + } + + if (isFetchingModifications) { + return ( + + + + + + + + + ); + } + + return ( + + {pendingState && ( + + + + )} + + + + + ); +}; diff --git a/src/components/network-modification-table/renderers/select-cell.tsx b/src/components/network-modification-table/renderers/select-cell.tsx new file mode 100644 index 000000000..eee5bf6cd --- /dev/null +++ b/src/components/network-modification-table/renderers/select-cell.tsx @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React, { FunctionComponent, useCallback } from 'react'; +import { Checkbox } from '@mui/material'; +import { Row, Table } from '@tanstack/react-table'; +import { styles } from '../styles'; +import { NetworkModificationMetadata } from '../../../hooks'; + +interface SelectCellRendererProps { + row: Row; + table: Table; +} + +const SelectCell: FunctionComponent = ({ row, table }) => { + const meta: any = table.options.meta; + + const handleChange = useCallback( + (event: React.MouseEvent) => { + const rows = table.getRowModel().rows; + const currentIndex = row.index; + const nextSelection = { ...table.getState().rowSelection }; + + // When shift is held and a previous click exists, select or deselect the contiguous range between + // the two clicks instead of toggling a single row. + if ( + event.shiftKey && + meta?.lastClickedIndex.current !== null && + meta?.lastClickedIndex.current !== undefined + ) { + const lastIndex = meta.lastClickedIndex.current; + const [from, to] = lastIndex < currentIndex ? [lastIndex, currentIndex] : [currentIndex, lastIndex]; + const isRowSelected = row.getIsSelected(); + + rows.slice(from, to + 1).forEach((r) => { + if (r.getCanSelect()) { + r.toggleSelected(!isRowSelected); + if (isRowSelected) { + delete nextSelection[r.id]; + } else { + nextSelection[r.id] = true; + } + } + }); + } else { + row.toggleSelected(); + if (row.getIsSelected()) { + // was selected, now toggled off + delete nextSelection[row.id]; + } else { + // was unselected, now toggled on + nextSelection[row.id] = true; + } + } + + if (meta) { + meta.lastClickedIndex.current = currentIndex; + const selectedRows = rows.filter((r) => nextSelection[r.id]).map((r) => r.original); + meta.onRowSelected?.(selectedRows); + } + }, + [table, row, meta] + ); + + return ( + + ); +}; + +export default SelectCell; diff --git a/src/components/network-modification-table/renderers/select-header-cell.tsx b/src/components/network-modification-table/renderers/select-header-cell.tsx new file mode 100644 index 000000000..0b79571e2 --- /dev/null +++ b/src/components/network-modification-table/renderers/select-header-cell.tsx @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React, { FunctionComponent, useCallback } from 'react'; +import { Checkbox } from '@mui/material'; +import { Table } from '@tanstack/react-table'; +import { NetworkModificationMetadata } from '../../../hooks'; + +interface SelectHeaderCellProps { + table: Table; +} + +const SelectHeaderCell: FunctionComponent = ({ table }) => { + const handleClick = useCallback(() => { + const meta: any = table.options.meta; + if (meta) { + const nextSelectedRows = table.getIsAllRowsSelected() + ? [] + : table.getCoreRowModel().rows.map((r) => r.original); + meta.onRowSelected?.(nextSelectedRows); + meta.lastClickedIndex.current = null; + } + table.toggleAllRowsSelected(); + }, [table]); + + return ( + + ); +}; + +export default SelectHeaderCell; diff --git a/src/components/network-modification-table/row/drag-row-clone.tsx b/src/components/network-modification-table/row/drag-row-clone.tsx new file mode 100644 index 000000000..7ae92fa7a --- /dev/null +++ b/src/components/network-modification-table/row/drag-row-clone.tsx @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026, 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 { flexRender, Row } from '@tanstack/react-table'; +import { createCellStyle, styles } from '../styles'; +import { AUTO_EXTENSIBLE_COLUMNS, BASE_MODIFICATION_TABLE_COLUMNS } from '../columns-definition'; +import { NetworkModificationMetadata } from '../../../hooks'; + +function DragCloneRow({ row }: { row: Row }) { + return ( + + {row + .getVisibleCells() + .filter((cell) => + [BASE_MODIFICATION_TABLE_COLUMNS.DRAG_HANDLE.id, BASE_MODIFICATION_TABLE_COLUMNS.NAME.id].includes( + cell.column.columnDef.id! + ) + ) + .map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ); +} + +export default DragCloneRow; diff --git a/src/components/network-modification-table/row/modification-row.tsx b/src/components/network-modification-table/row/modification-row.tsx new file mode 100644 index 000000000..df2b00d9c --- /dev/null +++ b/src/components/network-modification-table/row/modification-row.tsx @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import React, { memo, useCallback } from 'react'; +import { flexRender, Row } from '@tanstack/react-table'; +import { TableCell, TableRow } from '@mui/material'; +import { Draggable, DraggableProvided, DraggableStateSnapshot } from '@hello-pangea/dnd'; +import { VirtualItem } from '@tanstack/react-virtual'; +import { createCellStyle, createRowSx, styles } from '../styles'; +import { AUTO_EXTENSIBLE_COLUMNS, BASE_MODIFICATION_TABLE_COLUMNS } from '../columns-definition'; +import { NetworkModificationMetadata } from '../../../hooks'; + +interface ModificationRowProps { + virtualRow: VirtualItem; + row: Row; + handleCellClick?: (modification: NetworkModificationMetadata) => void; + isRowDragDisabled: boolean; + highlightedModificationUuid: string | null; +} + +const ModificationRow = memo( + ({ virtualRow, row, handleCellClick, isRowDragDisabled, highlightedModificationUuid }) => { + const isHighlighted = row.original.uuid === highlightedModificationUuid; + + const handleCellClickCallback = useCallback( + (columnId: string) => { + if (columnId === BASE_MODIFICATION_TABLE_COLUMNS.NAME.id) { + handleCellClick?.(row.original); + } + }, + [handleCellClick, row.original] + ); + + return ( + + {(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => { + const { style, ...draggablePropsWithoutStyle } = provided.draggableProps; + return ( + + {row.getVisibleCells().map((cell) => ( + handleCellClickCallback(cell.column.id)} + {...(cell.column.id === BASE_MODIFICATION_TABLE_COLUMNS.DRAG_HANDLE.id + ? provided.dragHandleProps + : undefined)} + > + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ); + }} + + ); + } +); + +export default ModificationRow; diff --git a/src/components/network-modification-table/styles.ts b/src/components/network-modification-table/styles.ts new file mode 100644 index 000000000..eb0b4a5b2 --- /dev/null +++ b/src/components/network-modification-table/styles.ts @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2026, 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 { VirtualItem } from '@tanstack/react-virtual'; +import { SxProps, Theme } from '@mui/material'; +import { alpha } from '@mui/material/styles'; +import { CSSProperties } from 'react'; +import { MuiStyles } from '../../utils'; + +const HIGHLIGHT_COLOR_BASE = 'rgba(144, 202, 249, 0.16)'; +const HIGHLIGHT_COLOR_HOVER = 'rgba(144, 202, 249, 0.24)'; +const ROW_HOVER_COLOR = 'rgba(144, 202, 249, 0.08)'; +const DRAG_OPACITY = 0.5; +const DEACTIVATED_OPACITY = 0.4; + +export const MODIFICATION_ROW_HEIGHT = 41; + +// Static styles + +export const styles = { + tableWrapper: (theme) => ({ + display: 'flex', + flexDirection: 'column', + flexGrow: 1, + margin: theme.spacing(1), + border: `1px solid ${theme.palette.divider}`, + overflow: 'hidden', + minHeight: 0, + }), + container: { + position: 'relative', + flexGrow: 1, + overflow: 'auto', + height: '100%', + }, + table: (theme) => ({ + width: '100%', + tableLayout: 'fixed', + borderCollapse: 'collapse', + backgroundColor: theme.palette.background.paper, + }), + thead: (theme) => ({ + backgroundColor: theme.palette.background.paper, + position: 'sticky', + top: 0, + zIndex: 1, + width: '100%', + '& tr:hover': { + backgroundColor: 'transparent', + }, + }), + tableRow: { + display: 'flex', + alignItems: 'center', + transition: 'none', + opacity: 1, + }, + tableBody: { + position: 'relative', + }, + tableCell: { + fontSize: 'small', + minWidth: 0, + display: 'flex', + }, + dragRowClone: (theme) => ({ + backgroundColor: 'background.paper', + boxShadow: 4, + opacity: 1, + border: '1px solid #f5f5f5', + display: 'flex', + width: 'fit-content', + paddingRight: theme.spacing(1), + }), + overflow: { + whiteSpace: 'pre', + textOverflow: 'ellipsis', + overflow: 'hidden', + }, + selectCheckBox: (theme) => ({ + padding: theme.spacing(0.8), + }), + dragHandle: (theme) => ({ + display: 'flex', + alignItems: 'center', + cursor: 'grab', + opacity: 0, + padding: theme.spacing(0.5), + 'tr:hover &': { opacity: 1 }, + }), + dragIndicatorIcon: { + width: '16px', + height: '16px', + }, + modificationLabel: { + textOverflow: 'ellipsis', + overflow: 'hidden', + whiteSpace: 'nowrap', + }, + rootNetworkHeader: { + width: '100%', + display: 'flex', + justifyContent: 'center', + }, + columnCell: { + select: { padding: 2, justifyContent: 'center' }, + modificationName: { cursor: 'pointer', minWidth: 0, overflow: 'hidden', flex: 1 }, + rootNetworkChip: { textAlign: 'center' }, + }, + modificationNameHeader: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + minWidth: 0, + gap: 2, + '& .MuiTypography-root': { + fontSize: 'inherit', + }, + }, + icon: (theme) => ({ + width: theme.spacing(1), + }), + modificationCircularProgress: (theme) => ({ + marginRight: theme.spacing(2), + color: theme.palette.primary.main, + }), +} as const satisfies MuiStyles; + +// Dynamic styles + +export const DROP_INDICATOR_TOP = 'inset 0 2px 0 #90caf9'; +export const DROP_INDICATOR_BOTTOM = 'inset 0 -2px 0 #90caf9'; + +export const createRowSx = (isHighlighted: boolean, isDragging: boolean, virtualRow: VirtualItem): SxProps => ({ + position: 'absolute', + top: 0, + left: 0, + right: 0, + width: '100%', + height: `${virtualRow.size}px`, + transform: `translateY(${virtualRow.start}px)`, + backgroundColor: isHighlighted ? HIGHLIGHT_COLOR_BASE : 'transparent', + opacity: isDragging ? DRAG_OPACITY : 1, + '&:hover': { + backgroundColor: isHighlighted ? HIGHLIGHT_COLOR_HOVER : ROW_HOVER_COLOR, + }, + ...(isDragging && { zIndex: 1, transform: 'none' }), +}); + +export const createModificationNameCellStyle = (activated: boolean): CSSProperties => ({ + opacity: activated ? 1 : DEACTIVATED_OPACITY, + paddingLeft: '0.8vw', +}); + +export const createRootNetworkChipCellSx = (activated: boolean): SxProps => ({ + width: '100%', + display: 'flex', + justifyContent: 'center', + opacity: activated ? 1 : DEACTIVATED_OPACITY, +}); + +export const createEditDescriptionStyle = (description: string | undefined): SxProps => ({ + opacity: description ? 1 : 0, + cursor: description ? 'pointer' : 'default', + 'tr:hover &': { opacity: 1 }, +}); + +export const createCellStyle = (cell: any, isAutoExtensible: boolean) => { + const size = cell.column.getSize(); + const { minSize } = cell.column.columnDef; + + return { + ...cell.column.columnDef.meta?.cellStyle, + padding: 0, + flex: isAutoExtensible ? `1 1 ${size}px` : `0 1 ${size}px`, + minWidth: minSize ? `${minSize}px` : undefined, + height: `${MODIFICATION_ROW_HEIGHT}px`, + display: 'flex', + alignItems: 'center', + }; +}; + +export const createHeaderCellStyle = ( + header: any, + theme: Theme, + isFirst: boolean, + isLast: boolean, + isAutoExtensible: boolean +) => { + const darkBorder = `1px solid ${alpha(theme.palette.text.secondary, 0.4)}`; + const size = header.column.getSize(); + const { minSize } = header.column.columnDef; + + return { + ...header.column.columnDef.meta?.cellStyle, + flex: isAutoExtensible ? `1 1 ${size}px` : `0 1 ${size}px`, + minWidth: minSize ? `${minSize}px` : undefined, + height: `${MODIFICATION_ROW_HEIGHT}px`, + padding: '2px', + textAlign: 'left', + fontWeight: 600, + display: 'flex', + alignItems: 'center', + paddingTop: '1.5vh', + paddingBottom: '1.5vh', + backgroundColor: theme.palette.background.paper, + borderBottom: darkBorder, + borderTop: darkBorder, + ...(isFirst && { borderLeft: darkBorder }), + ...(isLast && { borderRight: darkBorder }), + }; +}; diff --git a/src/components/network-modification-table/use-modifications-drag-and-drop.tsx b/src/components/network-modification-table/use-modifications-drag-and-drop.tsx new file mode 100644 index 000000000..6e66736d0 --- /dev/null +++ b/src/components/network-modification-table/use-modifications-drag-and-drop.tsx @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2026, 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 { JSX, RefObject, useCallback } from 'react'; +import { Row } from '@tanstack/react-table'; +import { DraggableProvided, DraggableRubric, DraggableStateSnapshot, DragUpdate, DropResult } from '@hello-pangea/dnd'; +import DragCloneRow from './row/drag-row-clone'; +import { DROP_INDICATOR_BOTTOM, DROP_INDICATOR_TOP } from './styles'; +import { NetworkModificationMetadata } from '../../hooks'; + +interface UseModificationsDragAndDropParams { + rows: Row[]; + containerRef: RefObject; + onRowDragEnd?: (result: DropResult) => void; +} + +interface UseModificationsDragAndDropReturn { + handleDragUpdate: (update: DragUpdate) => void; + handleDragEnd: (result: DropResult) => void; + renderClone: ( + provided: DraggableProvided, + snapshot: DraggableStateSnapshot, + rubric: DraggableRubric + ) => JSX.Element; +} + +const clearRowDragIndicators = (container: HTMLDivElement | null): void => { + container?.querySelectorAll('[data-row-id]').forEach((el) => { + el.style.boxShadow = ''; + }); +}; + +export const useModificationsDragAndDrop = ({ + rows, + containerRef, + onRowDragEnd, +}: UseModificationsDragAndDropParams): UseModificationsDragAndDropReturn => { + const handleDragUpdate = useCallback( + (update: DragUpdate) => { + clearRowDragIndicators(containerRef.current); + + const { source, destination } = update; + if (!destination || source.index === destination.index) { + return; + } + + const targetUuid = rows[destination.index]?.original.uuid; + const el = containerRef.current?.querySelector(`[data-row-id="${targetUuid}"]`); + if (el) { + el.style.boxShadow = destination.index > source.index ? DROP_INDICATOR_BOTTOM : DROP_INDICATOR_TOP; + } + }, + [rows, containerRef] + ); + + const handleDragEnd = useCallback( + (result: DropResult) => { + clearRowDragIndicators(containerRef.current); + + if (result.destination && result.source.index !== result.destination.index) { + onRowDragEnd?.(result); + } + }, + [containerRef, onRowDragEnd] + ); + + const renderClone = useCallback( + (provided: DraggableProvided, _snapshot: DraggableStateSnapshot, rubric: DraggableRubric) => ( +
+ +
+ ), + [rows] + ); + + return { handleDragUpdate, handleDragEnd, renderClone }; +}; diff --git a/src/utils/types/network-modification-types.ts b/src/utils/types/network-modification-types.ts index a9490cda3..0fc0c7760 100644 --- a/src/utils/types/network-modification-types.ts +++ b/src/utils/types/network-modification-types.ts @@ -1,3 +1,5 @@ +import type { UUID } from 'node:crypto'; + /** * Copyright (c) 2026, RTE (http://www.rte-france.com) * This Source Code Form is subject to the terms of the Mozilla Public @@ -13,3 +15,8 @@ export type AttributeModification = { value?: T; op: OperationType; }; + +export interface ExcludedNetworkModifications { + rootNetworkUuid: UUID; + modificationUuidsToExclude: UUID[]; +} From 83cbdd8ec6538c772bb38f2ba7c93f5ff618da58 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Mon, 23 Mar 2026 16:41:16 +0100 Subject: [PATCH 2/4] extracts createBaseColumns and reorganizes Signed-off-by: Mathieu DEHARBE --- .../columns-definition.tsx | 29 +------------------ .../network-modification-table/index.ts | 4 ++- .../network-modifications-table.tsx | 16 +++++----- .../{styles.ts => network-table-styles.ts} | 2 +- .../renderers/drag-handle-cell.tsx | 6 ++-- .../renderers/index.ts | 13 +++++++++ .../renderers/name-cell.tsx | 8 ++--- ...k-modification-node-editor-name-header.tsx | 20 ++++++------- .../renderers/select-cell.tsx | 4 +-- .../row/drag-row-clone.tsx | 4 +-- .../row/modification-row.tsx | 5 ++-- .../use-modifications-drag-and-drop.tsx | 2 +- 12 files changed, 51 insertions(+), 62 deletions(-) rename src/components/network-modification-table/{styles.ts => network-table-styles.ts} (99%) create mode 100644 src/components/network-modification-table/renderers/index.ts diff --git a/src/components/network-modification-table/columns-definition.tsx b/src/components/network-modification-table/columns-definition.tsx index c777cc422..78ecd8483 100644 --- a/src/components/network-modification-table/columns-definition.tsx +++ b/src/components/network-modification-table/columns-definition.tsx @@ -5,15 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import React, { SetStateAction } from 'react'; -import { ColumnDef } from '@tanstack/react-table'; import { - NetworkModificationEditorNameHeader, NetworkModificationEditorNameHeaderProps, -} from './renderers/network-modification-node-editor-name-header'; -import NameCell from './renderers/name-cell'; -import { styles } from './styles'; -import { NetworkModificationMetadata } from '../../hooks'; +} from './renderers'; export const BASE_MODIFICATION_TABLE_COLUMNS = { DRAG_HANDLE: { @@ -43,24 +37,3 @@ export const AUTO_EXTENSIBLE_COLUMNS = Object.values(BASE_MODIFICATION_TABLE_COL .map((column) => column.id); export type NameHeaderProps = Omit; - -// TODO : ceci devrait probablement être à la fois dans gridexplore et gridstudy avec uniquement les constantses en commun -// à séparer à la fin -export const createBaseColumns = ( - isRowDragDisabled: boolean, - modificationsCount: number, - nameHeaderProps: NameHeaderProps, - setModifications: React.Dispatch> -): ColumnDef[] => [ - { - id: BASE_MODIFICATION_TABLE_COLUMNS.NAME.id, - header: () => ( - - ), - cell: ({ row }) => , - meta: { - cellStyle: styles.columnCell.modificationName, - }, - minSize: 160, - }, -]; diff --git a/src/components/network-modification-table/index.ts b/src/components/network-modification-table/index.ts index 7f1e762ef..b5f492cd4 100644 --- a/src/components/network-modification-table/index.ts +++ b/src/components/network-modification-table/index.ts @@ -6,6 +6,8 @@ import NetworkModificationsTable from './network-modification-table/network-modi * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +export * from './network-table-styles'; export * from './columns-definition'; export * from './network-modifications-table'; -export * from './use-modifications-drag-and-drop'; \ No newline at end of file +export * from './use-modifications-drag-and-drop'; +export * from './renderers'; diff --git a/src/components/network-modification-table/network-modifications-table.tsx b/src/components/network-modification-table/network-modifications-table.tsx index aa6eab80d..f9f02de82 100644 --- a/src/components/network-modification-table/network-modifications-table.tsx +++ b/src/components/network-modification-table/network-modifications-table.tsx @@ -11,12 +11,12 @@ import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack import { DragDropContext, DragStart, Droppable, DroppableProvided, DropResult } from '@hello-pangea/dnd'; import { useVirtualizer } from '@tanstack/react-virtual'; import { UUID } from 'node:crypto'; -import { NetworkModificationEditorNameHeaderProps } from './renderers/network-modification-node-editor-name-header'; -import { createHeaderCellStyle, MODIFICATION_ROW_HEIGHT, styles } from './styles'; +import { NetworkModificationEditorNameHeaderProps } from './renderers'; import { AUTO_EXTENSIBLE_COLUMNS, NameHeaderProps } from './columns-definition'; import ModificationRow from './row/modification-row'; import { useModificationsDragAndDrop } from './use-modifications-drag-and-drop'; import { NetworkModificationMetadata } from '../../hooks'; +import { createHeaderCellStyle, MODIFICATION_ROW_HEIGHT, networkTableStyles } from './network-table-styles'; interface NetworkModificationsTableProps extends Omit { modifications: NetworkModificationMetadata[]; @@ -104,14 +104,14 @@ export function NetworkModificationsTable({ return ( - + {(provided: DroppableProvided) => ( - - - + +
+ {table.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => ( {virtualItems.map((virtualRow) => { const row = rows[virtualRow.index]; diff --git a/src/components/network-modification-table/styles.ts b/src/components/network-modification-table/network-table-styles.ts similarity index 99% rename from src/components/network-modification-table/styles.ts rename to src/components/network-modification-table/network-table-styles.ts index eb0b4a5b2..3251647b9 100644 --- a/src/components/network-modification-table/styles.ts +++ b/src/components/network-modification-table/network-table-styles.ts @@ -21,7 +21,7 @@ export const MODIFICATION_ROW_HEIGHT = 41; // Static styles -export const styles = { +export const networkTableStyles = { tableWrapper: (theme) => ({ display: 'flex', flexDirection: 'column', diff --git a/src/components/network-modification-table/renderers/drag-handle-cell.tsx b/src/components/network-modification-table/renderers/drag-handle-cell.tsx index f4f161cb5..ed73e25d1 100644 --- a/src/components/network-modification-table/renderers/drag-handle-cell.tsx +++ b/src/components/network-modification-table/renderers/drag-handle-cell.tsx @@ -7,13 +7,13 @@ import { Box } from '@mui/material'; import { FunctionComponent } from 'react'; -import { styles } from '../styles'; import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; +import { networkTableStyles } from '../network-table-styles'; const DragHandleCell: FunctionComponent<{ isRowDragDisabled: boolean }> = ({ isRowDragDisabled }) => { return ( - - {!isRowDragDisabled && } + + {!isRowDragDisabled && } ); }; diff --git a/src/components/network-modification-table/renderers/index.ts b/src/components/network-modification-table/renderers/index.ts new file mode 100644 index 000000000..ce3ca0242 --- /dev/null +++ b/src/components/network-modification-table/renderers/index.ts @@ -0,0 +1,13 @@ +/** + import NetworkModificationsTable from './network-modification-table/network-modifications-table'; + * Copyright (c) 2026, 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/. + */ + +export * from './drag-handle-cell'; +export * from './name-cell'; +export * from './network-modification-node-editor-name-header'; +export * from './select-cell'; +export * from './select-header-cell'; diff --git a/src/components/network-modification-table/renderers/name-cell.tsx b/src/components/network-modification-table/renderers/name-cell.tsx index 0474dc70a..c53f6cf40 100644 --- a/src/components/network-modification-table/renderers/name-cell.tsx +++ b/src/components/network-modification-table/renderers/name-cell.tsx @@ -9,11 +9,11 @@ import React, { FunctionComponent, useCallback, useMemo } from 'react'; import { Row } from '@tanstack/react-table'; import { useIntl } from 'react-intl'; import { Box, Tooltip } from '@mui/material'; -import { createModificationNameCellStyle, styles } from '../styles'; +import { createModificationNameCellStyle, networkTableStyles } from '../network-table-styles'; import { NetworkModificationMetadata, useModificationLabelComputer } from '../../../hooks'; import { mergeSx } from '../../../utils'; -const NameCell: FunctionComponent<{ row: Row }> = ({ row }) => { +export const NameCell: FunctionComponent<{ row: Row }> = ({ row }) => { const intl = useIntl(); const { computeLabel } = useModificationLabelComputer(); @@ -30,9 +30,9 @@ const NameCell: FunctionComponent<{ row: Row }> = ( const label = useMemo(() => getModificationLabel(row.original), [getModificationLabel, row.original]); return ( - + - {label} + {label} ); diff --git a/src/components/network-modification-table/renderers/network-modification-node-editor-name-header.tsx b/src/components/network-modification-table/renderers/network-modification-node-editor-name-header.tsx index d35fa27c4..a66bd988a 100644 --- a/src/components/network-modification-table/renderers/network-modification-node-editor-name-header.tsx +++ b/src/components/network-modification-table/renderers/network-modification-node-editor-name-header.tsx @@ -8,7 +8,7 @@ import { Box, CircularProgress, Typography } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import { FunctionComponent } from 'react'; -import { styles } from '../styles'; +import { networkTableStyles } from '../network-table-styles'; export interface NetworkModificationEditorNameHeaderProps { modificationCount?: number; @@ -31,9 +31,9 @@ export const NetworkModificationEditorNameHeader: FunctionComponent - - + + + @@ -44,9 +44,9 @@ export const NetworkModificationEditorNameHeader: FunctionComponent - - + + + @@ -56,10 +56,10 @@ export const NetworkModificationEditorNameHeader: FunctionComponent + {pendingState && ( - - + + )} diff --git a/src/components/network-modification-table/renderers/select-cell.tsx b/src/components/network-modification-table/renderers/select-cell.tsx index eee5bf6cd..2bbdf3c15 100644 --- a/src/components/network-modification-table/renderers/select-cell.tsx +++ b/src/components/network-modification-table/renderers/select-cell.tsx @@ -8,7 +8,7 @@ import React, { FunctionComponent, useCallback } from 'react'; import { Checkbox } from '@mui/material'; import { Row, Table } from '@tanstack/react-table'; -import { styles } from '../styles'; +import { networkTableStyles } from '../network-table-styles'; import { NetworkModificationMetadata } from '../../../hooks'; interface SelectCellRendererProps { @@ -72,7 +72,7 @@ const SelectCell: FunctionComponent = ({ row, table }) checked={row.getIsSelected()} disabled={!row.getCanSelect()} onClick={handleChange} - sx={styles.selectCheckBox} + sx={networkTableStyles.selectCheckBox} /> ); }; diff --git a/src/components/network-modification-table/row/drag-row-clone.tsx b/src/components/network-modification-table/row/drag-row-clone.tsx index 7ae92fa7a..3ef0a62f1 100644 --- a/src/components/network-modification-table/row/drag-row-clone.tsx +++ b/src/components/network-modification-table/row/drag-row-clone.tsx @@ -7,13 +7,13 @@ import { Box } from '@mui/material'; import { flexRender, Row } from '@tanstack/react-table'; -import { createCellStyle, styles } from '../styles'; +import { createCellStyle, networkTableStyles } from '../network-table-styles'; import { AUTO_EXTENSIBLE_COLUMNS, BASE_MODIFICATION_TABLE_COLUMNS } from '../columns-definition'; import { NetworkModificationMetadata } from '../../../hooks'; function DragCloneRow({ row }: { row: Row }) { return ( - + {row .getVisibleCells() .filter((cell) => diff --git a/src/components/network-modification-table/row/modification-row.tsx b/src/components/network-modification-table/row/modification-row.tsx index df2b00d9c..9a57529c6 100644 --- a/src/components/network-modification-table/row/modification-row.tsx +++ b/src/components/network-modification-table/row/modification-row.tsx @@ -10,9 +10,10 @@ import { flexRender, Row } from '@tanstack/react-table'; import { TableCell, TableRow } from '@mui/material'; import { Draggable, DraggableProvided, DraggableStateSnapshot } from '@hello-pangea/dnd'; import { VirtualItem } from '@tanstack/react-virtual'; -import { createCellStyle, createRowSx, styles } from '../styles'; +import { createCellStyle, createRowSx, networkTableStyles } from '../network-table-styles'; import { AUTO_EXTENSIBLE_COLUMNS, BASE_MODIFICATION_TABLE_COLUMNS } from '../columns-definition'; import { NetworkModificationMetadata } from '../../../hooks'; +import { mergeSx } from '../../../utils'; interface ModificationRowProps { virtualRow: VirtualItem; @@ -44,7 +45,7 @@ const ModificationRow = memo( ref={provided.innerRef} {...draggablePropsWithoutStyle} data-row-id={row.original.uuid} - sx={mergeSx(styles.tableRow, createRowSx(isHighlighted, snapshot.isDragging, virtualRow))} + sx={mergeSx(networkTableStyles.tableRow, createRowSx(isHighlighted, snapshot.isDragging, virtualRow))} > {row.getVisibleCells().map((cell) => ( Date: Tue, 24 Mar 2026 11:05:33 +0100 Subject: [PATCH 3/4] updated in order to work with gridstudy Signed-off-by: Mathieu DEHARBE --- .../columns-definition.tsx | 10 ++++++++++ src/components/network-modification-table/index.ts | 1 + .../network-modifications-table.tsx | 2 +- .../renderers/drag-handle-cell.tsx | 14 +++++++++----- .../renderers/name-cell.tsx | 2 -- .../renderers/select-cell.tsx | 4 +--- .../renderers/select-header-cell.tsx | 4 +--- .../network-modification-table/row/index.ts | 10 ++++++++++ .../row/modification-row.tsx | 4 +--- 9 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 src/components/network-modification-table/row/index.ts diff --git a/src/components/network-modification-table/columns-definition.tsx b/src/components/network-modification-table/columns-definition.tsx index 78ecd8483..d861096f6 100644 --- a/src/components/network-modification-table/columns-definition.tsx +++ b/src/components/network-modification-table/columns-definition.tsx @@ -9,6 +9,16 @@ import { NetworkModificationEditorNameHeaderProps, } from './renderers'; +const CHIP_PADDING_PX = 24; +const CHAR_WIDTH_PX = 8; +const COLUMN_PADDING_PX = 12; +const MIN_COLUMN_SIZE = 40; + +export const computeTagMinSize = (tag: string): number => { + const chipContentWidth = tag.length * CHAR_WIDTH_PX + CHIP_PADDING_PX; + return Math.max(chipContentWidth + COLUMN_PADDING_PX, MIN_COLUMN_SIZE); +}; + export const BASE_MODIFICATION_TABLE_COLUMNS = { DRAG_HANDLE: { id: 'dragHandle', diff --git a/src/components/network-modification-table/index.ts b/src/components/network-modification-table/index.ts index b5f492cd4..3ad6acff6 100644 --- a/src/components/network-modification-table/index.ts +++ b/src/components/network-modification-table/index.ts @@ -11,3 +11,4 @@ export * from './columns-definition'; export * from './network-modifications-table'; export * from './use-modifications-drag-and-drop'; export * from './renderers'; +export * from './row'; diff --git a/src/components/network-modification-table/network-modifications-table.tsx b/src/components/network-modification-table/network-modifications-table.tsx index f9f02de82..43e474c92 100644 --- a/src/components/network-modification-table/network-modifications-table.tsx +++ b/src/components/network-modification-table/network-modifications-table.tsx @@ -13,10 +13,10 @@ import { useVirtualizer } from '@tanstack/react-virtual'; import { UUID } from 'node:crypto'; import { NetworkModificationEditorNameHeaderProps } from './renderers'; import { AUTO_EXTENSIBLE_COLUMNS, NameHeaderProps } from './columns-definition'; -import ModificationRow from './row/modification-row'; import { useModificationsDragAndDrop } from './use-modifications-drag-and-drop'; import { NetworkModificationMetadata } from '../../hooks'; import { createHeaderCellStyle, MODIFICATION_ROW_HEIGHT, networkTableStyles } from './network-table-styles'; +import { ModificationRow } from './row'; interface NetworkModificationsTableProps extends Omit { modifications: NetworkModificationMetadata[]; diff --git a/src/components/network-modification-table/renderers/drag-handle-cell.tsx b/src/components/network-modification-table/renderers/drag-handle-cell.tsx index ed73e25d1..967a4fee3 100644 --- a/src/components/network-modification-table/renderers/drag-handle-cell.tsx +++ b/src/components/network-modification-table/renderers/drag-handle-cell.tsx @@ -6,16 +6,20 @@ */ import { Box } from '@mui/material'; -import { FunctionComponent } from 'react'; import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; import { networkTableStyles } from '../network-table-styles'; -const DragHandleCell: FunctionComponent<{ isRowDragDisabled: boolean }> = ({ isRowDragDisabled }) => { +interface DragHandleCellProps { + isRowDragDisabled: boolean; +} + +export const DragHandleCell = ({ + isRowDragDisabled, + }:Readonly) => { return ( + isRowDragDisabled ? undefined: - {!isRowDragDisabled && } + ); }; - -export default DragHandleCell; diff --git a/src/components/network-modification-table/renderers/name-cell.tsx b/src/components/network-modification-table/renderers/name-cell.tsx index c53f6cf40..a5d621d44 100644 --- a/src/components/network-modification-table/renderers/name-cell.tsx +++ b/src/components/network-modification-table/renderers/name-cell.tsx @@ -37,5 +37,3 @@ export const NameCell: FunctionComponent<{ row: Row ); }; - -export default NameCell; diff --git a/src/components/network-modification-table/renderers/select-cell.tsx b/src/components/network-modification-table/renderers/select-cell.tsx index 2bbdf3c15..88776a654 100644 --- a/src/components/network-modification-table/renderers/select-cell.tsx +++ b/src/components/network-modification-table/renderers/select-cell.tsx @@ -16,7 +16,7 @@ interface SelectCellRendererProps { table: Table; } -const SelectCell: FunctionComponent = ({ row, table }) => { +export const SelectCell: FunctionComponent = ({ row, table }) => { const meta: any = table.options.meta; const handleChange = useCallback( @@ -76,5 +76,3 @@ const SelectCell: FunctionComponent = ({ row, table }) /> ); }; - -export default SelectCell; diff --git a/src/components/network-modification-table/renderers/select-header-cell.tsx b/src/components/network-modification-table/renderers/select-header-cell.tsx index 0b79571e2..74b5cd183 100644 --- a/src/components/network-modification-table/renderers/select-header-cell.tsx +++ b/src/components/network-modification-table/renderers/select-header-cell.tsx @@ -14,7 +14,7 @@ interface SelectHeaderCellProps { table: Table; } -const SelectHeaderCell: FunctionComponent = ({ table }) => { +export const SelectHeaderCell: FunctionComponent = ({ table }) => { const handleClick = useCallback(() => { const meta: any = table.options.meta; if (meta) { @@ -36,5 +36,3 @@ const SelectHeaderCell: FunctionComponent = ({ table }) = /> ); }; - -export default SelectHeaderCell; diff --git a/src/components/network-modification-table/row/index.ts b/src/components/network-modification-table/row/index.ts new file mode 100644 index 000000000..9539848c5 --- /dev/null +++ b/src/components/network-modification-table/row/index.ts @@ -0,0 +1,10 @@ +/** + import NetworkModificationsTable from './network-modification-table/network-modifications-table'; + * Copyright (c) 2026, 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/. + */ + +export * from './drag-row-clone'; +export * from './modification-row'; \ No newline at end of file diff --git a/src/components/network-modification-table/row/modification-row.tsx b/src/components/network-modification-table/row/modification-row.tsx index 9a57529c6..6713ec732 100644 --- a/src/components/network-modification-table/row/modification-row.tsx +++ b/src/components/network-modification-table/row/modification-row.tsx @@ -23,7 +23,7 @@ interface ModificationRowProps { highlightedModificationUuid: string | null; } -const ModificationRow = memo( +export const ModificationRow = memo( ({ virtualRow, row, handleCellClick, isRowDragDisabled, highlightedModificationUuid }) => { const isHighlighted = row.original.uuid === highlightedModificationUuid; @@ -66,5 +66,3 @@ const ModificationRow = memo( ); } ); - -export default ModificationRow; From 6574685501c5cfcdba795589ed0be7f689236092 Mon Sep 17 00:00:00 2001 From: Mathieu DEHARBE Date: Tue, 24 Mar 2026 11:12:36 +0100 Subject: [PATCH 4/4] licenses Signed-off-by: Mathieu DEHARBE --- src/components/network-modification-table/renderers/index.ts | 1 - src/components/network-modification-table/row/index.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/components/network-modification-table/renderers/index.ts b/src/components/network-modification-table/renderers/index.ts index ce3ca0242..6bf320443 100644 --- a/src/components/network-modification-table/renderers/index.ts +++ b/src/components/network-modification-table/renderers/index.ts @@ -1,5 +1,4 @@ /** - import NetworkModificationsTable from './network-modification-table/network-modifications-table'; * Copyright (c) 2026, 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 diff --git a/src/components/network-modification-table/row/index.ts b/src/components/network-modification-table/row/index.ts index 9539848c5..be94a8593 100644 --- a/src/components/network-modification-table/row/index.ts +++ b/src/components/network-modification-table/row/index.ts @@ -1,5 +1,4 @@ /** - import NetworkModificationsTable from './network-modification-table/network-modifications-table'; * Copyright (c) 2026, 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