Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@gridsuite/commons-ui": "0.211.0",
"@gridsuite/commons-ui": "0.212.0",
"@hello-pangea/dnd": "^18.0.1",
"@hookform/resolvers": "^4.1.3",
"@mui/icons-material": "^6.5.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
MAX_COMPOSITE_NESTING_DEPTH,
MODIFICATION_TYPES,
ModificationType,
NameHeaderProps,
NetworkModificationMetadata,
NetworkModificationsTable,
NotificationsUrlKeys,
Expand All @@ -33,7 +32,7 @@ import ContentCutIcon from '@mui/icons-material/ContentCut';
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
import DeleteIcon from '@mui/icons-material/Delete';
import SaveIcon from '@mui/icons-material/Save';
import { Alert, Badge, Box, CircularProgress, debounce, Toolbar, Tooltip } from '@mui/material';
import { Alert, Badge, Box, CircularProgress, Toolbar, Tooltip } from '@mui/material';
import IconButton from '@mui/material/IconButton';

import BatteryCreationDialog from 'components/dialogs/network-modifications/battery/creation/battery-creation-dialog';
Expand Down Expand Up @@ -68,7 +67,7 @@ import VoltageLevelModificationDialog from 'components/dialogs/network-modificat
import VscCreationDialog from 'components/dialogs/network-modifications/hvdc-line/vsc/creation/vsc-creation-dialog';
import VscModificationDialog from 'components/dialogs/network-modifications/hvdc-line/vsc/modification/vsc-modification-dialog';
import NetworkModificationsMenu from 'components/graph/menus/network-modifications/network-modifications-menu';
import { SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { useDispatch, useSelector } from 'react-redux';
import { addNotification, removeNotificationByNode, setModificationsInProgress } from '../../../../redux/actions';
Expand Down Expand Up @@ -1129,26 +1128,14 @@ const NetworkModificationNodeEditor = () => {
[isAnyNodeBuilding, mapDataLoading, isDragging]
);

const createAllColumns = useCallback(
(
isRowDragDisabled: boolean,
modificationsCount: number,
nameHeaderProps: NameHeaderProps,
setModifications: React.Dispatch<SetStateAction<ComposedModificationMetadata[]>>
): ColumnDef<ComposedModificationMetadata>[] => [
...createBaseColumns(
isRowDragDisabled,
modificationsCount,
nameHeaderProps,
setModifications,
handleCellEdit
),
const columns = useMemo<ColumnDef<ComposedModificationMetadata>[]>(
() => [
...createBaseColumns(handleCellEdit),
...(isMonoRootStudy
? []
: createRootNetworksColumns(
rootNetworks,
currentRootNetworkUuid!,
modificationsCount,
modificationsToExclude,
setModificationsToExclude
)),
Expand All @@ -1169,7 +1156,7 @@ const NetworkModificationNodeEditor = () => {

return (
<NetworkModificationsTable
handleCellClick={debounce(handleCellClick, 300)}
handleCellClick={handleCellClick}
modifications={modifications}
onRowDragStart={onRowDragStart}
onRowDragEnd={onRowDragEnd}
Expand All @@ -1179,7 +1166,7 @@ const NetworkModificationNodeEditor = () => {
notificationMessageId={notificationMessageId}
isFetchingModifications={isFetchingModifications}
pendingState={pendingState}
createAllColumns={createAllColumns}
columns={columns}
highlightedModificationUuid={highlightedModificationUuid}
studyUuid={studyUuid}
currentNodeId={currentNode?.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,23 @@ import {
BASE_MODIFICATION_TABLE_COLUMNS,
ComposedModificationMetadata,
computeTagMinSize,
createRootNetworkChipCellSx,
DragHandleCell,
ExcludedNetworkModifications,
NameCell,
NameHeaderProps,
NetworkModificationEditorNameHeader,
networkModificationTableStyles,
SelectCell,
SelectHeaderCell,
} from '@gridsuite/commons-ui';
import React, { SetStateAction } from 'react';
import { ColumnDef } from '@tanstack/react-table';
import DescriptionCell from './renderers/description-cell';
import SwitchCell from './renderers/switch-cell';
import { RootNetworkMetadata } from '../network-modification-menu.type';
import { Badge, Box, Tooltip } from '@mui/material';
import { RemoveRedEye as RemoveRedEyeIcon } from '@mui/icons-material';
import RootNetworkChipCell from './renderers/root-network-chip-cell';
import { FormattedMessage } from 'react-intl';
import {
DescriptionCellRenderer,
DragHandleRenderer,
NameCellRenderer,
NameHeaderRenderer,
RootNetworkCellRenderer,
RootNetworkHeaderRenderer,
SelectCellRenderer,
SelectHeaderRenderer,
SwitchCellRenderer,
} from './renderers/cell-renderers';

/**
* Column definition is broken up in 2 parts : base columns which are always on display and root networks columns.
Expand All @@ -35,15 +33,11 @@ import { FormattedMessage } from 'react-intl';
*/

export const createBaseColumns = (
isRowDragDisabled: boolean,
modificationsCount: number,
nameHeaderProps: NameHeaderProps,
setModifications: React.Dispatch<SetStateAction<ComposedModificationMetadata[]>>,
handleCellNameEdit?: (modification: ComposedModificationMetadata, newName: string) => void
onEditNameCell: (modification: ComposedModificationMetadata, newName?: string) => void
): ColumnDef<ComposedModificationMetadata>[] => [
{
id: BASE_MODIFICATION_TABLE_COLUMNS.DRAG_HANDLE.id,
cell: () => <DragHandleCell isRowDragDisabled={isRowDragDisabled} />,
cell: DragHandleRenderer,
size: 24,
minSize: 24,
meta: {
Expand All @@ -54,8 +48,8 @@ export const createBaseColumns = (
},
{
id: BASE_MODIFICATION_TABLE_COLUMNS.SELECT.id,
header: ({ table }) => <SelectHeaderCell table={table} />,
cell: ({ row, table }) => <SelectCell row={row} table={table} />,
header: SelectHeaderRenderer,
cell: SelectCellRenderer,
size: 32,
minSize: 32,
meta: {
Expand All @@ -64,24 +58,23 @@ export const createBaseColumns = (
},
{
id: BASE_MODIFICATION_TABLE_COLUMNS.NAME.id,
header: () => (
<NetworkModificationEditorNameHeader modificationCount={modificationsCount} {...nameHeaderProps} />
),
cell: ({ row }) => <NameCell row={row} onEditNameCell={handleCellNameEdit} />,
header: NameHeaderRenderer,
cell: NameCellRenderer,
meta: {
cellStyle: networkModificationTableStyles.columnCell.modificationName,
onEditNameCell,
},
minSize: 160,
},
{
id: BASE_MODIFICATION_TABLE_COLUMNS.DESCRIPTION.id,
cell: ({ row }) => <DescriptionCell data={row.original} />,
cell: DescriptionCellRenderer,
size: 40,
minSize: 32,
},
{
id: BASE_MODIFICATION_TABLE_COLUMNS.SWITCH.id,
cell: ({ row }) => <SwitchCell data={row.original} setModifications={setModifications} />,
cell: SwitchCellRenderer,
size: 48,
minSize: 48,
meta: {
Expand All @@ -95,53 +88,26 @@ export const createBaseColumns = (
export const createRootNetworksColumns = (
rootNetworks: RootNetworkMetadata[],
currentRootNetworkUuid: string,
modificationsCount: number,
modificationsToExclude: ExcludedNetworkModifications[],
setModificationsToExclude: React.Dispatch<SetStateAction<ExcludedNetworkModifications[]>>
): ColumnDef<ComposedModificationMetadata>[] => {
const tagMinSizes = rootNetworks.map((rootNetwork) => computeTagMinSize(rootNetwork.tag ?? ''));
const sharedSize = Math.max(Math.min(...tagMinSizes), 56);
const currentRootNetworkTag = rootNetworks.find((item) => item.rootNetworkUuid === currentRootNetworkUuid)?.tag;

return rootNetworks.map((rootNetwork, index) => {
const rootNetworkUuid = rootNetwork.rootNetworkUuid;
const isCurrentRootNetwork = rootNetworkUuid === currentRootNetworkUuid;
const tagMinSize = tagMinSizes[index];

return {
id: rootNetworkUuid,
header: () =>
isCurrentRootNetwork && modificationsCount >= 1 ? (
<Box sx={networkModificationTableStyles.rootNetworkHeader}>
<Tooltip
title={
<FormattedMessage
id={'visualizedRootNetwork'}
values={{ tag: currentRootNetworkTag }}
/>
}
>
<Badge overlap="circular" color="primary" variant="dot">
<RemoveRedEyeIcon />
</Badge>
</Tooltip>
</Box>
) : null,
cell: ({ row }) => (
<Box sx={createRootNetworkChipCellSx(row.original.activated)}>
<RootNetworkChipCell
data={row.original}
rootNetwork={rootNetwork}
modificationsToExclude={modificationsToExclude}
setModificationsToExclude={setModificationsToExclude}
/>
</Box>
),
size: sharedSize,
minSize: tagMinSize,
meta: {
cellStyle: networkModificationTableStyles.columnCell.rootNetworkChip,
},
};
});
return rootNetworks.map((rootNetwork, index) => ({
id: rootNetwork.rootNetworkUuid,
header: RootNetworkHeaderRenderer,
cell: RootNetworkCellRenderer,
size: sharedSize,
minSize: tagMinSizes[index],
meta: {
cellStyle: networkModificationTableStyles.columnCell.rootNetworkChip,
rootNetwork,
modificationsToExclude,
setModificationsToExclude,
isCurrentRootNetwork: rootNetwork.rootNetworkUuid === currentRootNetworkUuid,
currentRootNetworkTag,
},
}));
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* 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 {
ComposedModificationMetadata,
createRootNetworkChipCellSx,
DragHandleCell,
NameCell,
NetworkModificationEditorNameHeader,
networkModificationTableStyles,
SelectCell,
SelectHeaderCell,
} from '@gridsuite/commons-ui';
import { CellContext, HeaderContext } from '@tanstack/react-table';
import { Badge, Box, Tooltip } from '@mui/material';
import { RemoveRedEye as RemoveRedEyeIcon } from '@mui/icons-material';
import { FormattedMessage } from 'react-intl';
import DescriptionCell from './description-cell';
import SwitchCell from './switch-cell';
import RootNetworkChipCell from './root-network-chip-cell';

/**
* Cell/header renderers must keep a stable reference across renders: react-table calls
* `flexRender(columnDef.cell, ctx)`, and when a renderer function is used as a component,
* a new function reference is treated as a different component type — which can
* unmount/remount the cell and reset its local state.
*
* But what matters is the *scope* the renderer is defined in, not whether it is inline or named.
* An inline arrow inside a module-scope constant (e.g. `BASE_COLUMNS`) is created once and is
* just as stable as a named component. The renderers below are hoisted because they are reused
* by `createRootNetworksColumns`, which is a factory called inside a hook — defining them inline
* there would produce a fresh reference on every call.
*
* Dynamic values are routed via react-table's `meta`: table-wide via `table.options.meta`,
* per-column via `column.columnDef.meta`.
*/

type CCtx = CellContext<ComposedModificationMetadata, unknown>;
type HCtx = HeaderContext<ComposedModificationMetadata, unknown>;

export function DragHandleRenderer({ table }: CCtx) {

Check warning on line 45 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4isYcnAWuVcU1k8&open=AZ5BH4isYcnAWuVcU1k8&pullRequest=3931
return <DragHandleCell isRowDragDisabled={table.options.meta?.isRowDragDisabled ?? false} />;
}

export function SelectHeaderRenderer({ table }: HCtx) {

Check warning on line 49 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4itYcnAWuVcU1k9&open=AZ5BH4itYcnAWuVcU1k9&pullRequest=3931
return <SelectHeaderCell table={table} />;
}

export function SelectCellRenderer({ row, table }: CCtx) {

Check warning on line 53 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4itYcnAWuVcU1k-&open=AZ5BH4itYcnAWuVcU1k-&pullRequest=3931
return <SelectCell row={row} table={table} />;
}

export function NameHeaderRenderer({ table }: HCtx) {

Check warning on line 57 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4itYcnAWuVcU1k_&open=AZ5BH4itYcnAWuVcU1k_&pullRequest=3931
const meta = table.options.meta;
const nameHeaderProps = meta?.nameHeaderProps;
if (!nameHeaderProps) {
return null;
}
return (
<NetworkModificationEditorNameHeader modificationCount={meta?.modificationsCount ?? 0} {...nameHeaderProps} />
);
}

export function NameCellRenderer({ row, column }: CCtx) {

Check warning on line 68 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4itYcnAWuVcU1lA&open=AZ5BH4itYcnAWuVcU1lA&pullRequest=3931
return <NameCell row={row} onEditNameCell={column.columnDef.meta?.onEditNameCell} />;
}

export function DescriptionCellRenderer({ row }: CCtx) {

Check warning on line 72 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4itYcnAWuVcU1lB&open=AZ5BH4itYcnAWuVcU1lB&pullRequest=3931
return <DescriptionCell data={row.original} />;
}

export function SwitchCellRenderer({ row }: CCtx) {

Check warning on line 76 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4itYcnAWuVcU1lC&open=AZ5BH4itYcnAWuVcU1lC&pullRequest=3931
return <SwitchCell data={row.original} />;
}

export function RootNetworkHeaderRenderer({ column, table }: HCtx) {

Check warning on line 80 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4itYcnAWuVcU1lD&open=AZ5BH4itYcnAWuVcU1lD&pullRequest=3931
const colMeta = column.columnDef.meta;
const tableMeta = table.options.meta;
if (!colMeta?.isCurrentRootNetwork || (tableMeta?.modificationsCount ?? 0) < 1) {
return null;
}
return (
<Box sx={networkModificationTableStyles.rootNetworkHeader}>
<Tooltip
title={<FormattedMessage id="visualizedRootNetwork" values={{ tag: colMeta.currentRootNetworkTag }} />}
>
<Badge overlap="circular" color="primary" variant="dot">
<RemoveRedEyeIcon />
</Badge>
</Tooltip>
</Box>
);
}

export function RootNetworkCellRenderer({ row, column }: CCtx) {

Check warning on line 99 in src/components/graph/menus/network-modifications/network-modification-table/renderers/cell-renderers.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=gridsuite_gridstudy-app&issues=AZ5BH4itYcnAWuVcU1lE&open=AZ5BH4itYcnAWuVcU1lE&pullRequest=3931
const meta = column.columnDef.meta;
if (!meta?.rootNetwork || !meta.modificationsToExclude || !meta.setModificationsToExclude) {
return null;
}
return (
<Box sx={createRootNetworkChipCellSx(row.original.activated)}>
<RootNetworkChipCell
data={row.original}
rootNetwork={meta.rootNetwork}
modificationsToExclude={meta.modificationsToExclude}
setModificationsToExclude={meta.setModificationsToExclude}
/>
</Box>
);
}
Loading
Loading