Skip to content

Commit 3704e82

Browse files
committed
front: add waypoint menu in manchette
- When clicking on a waypoint in the manchette, display a menu with some actions - Only action for now is to hide the waypoint in the manchette Signed-off-by: SharglutDev <p.filimon75@gmail.com>
1 parent 84fddc6 commit 3704e82

File tree

8 files changed

+177
-39
lines changed

8 files changed

+177
-39
lines changed

front/package-lock.json

+28-28
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

front/package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
"@nivo/line": "^0.80.0",
99
"@openapi-contrib/openapi-schema-to-json-schema": "^5.1.0",
1010
"@osrd-project/netzgrafik-frontend": "0.0.0-snapshot.37949a66933e8e1552c9b8e54f702ec491afd415",
11-
"@osrd-project/ui-core": "^0.0.56",
12-
"@osrd-project/ui-icons": "^0.0.56",
13-
"@osrd-project/ui-manchette": "^0.0.56",
14-
"@osrd-project/ui-manchette-with-spacetimechart": "^0.0.56",
15-
"@osrd-project/ui-spacetimechart": "^0.0.56",
16-
"@osrd-project/ui-speedspacechart": "^0.0.56",
11+
"@osrd-project/ui-core": "^0.0.57",
12+
"@osrd-project/ui-icons": "^0.0.57",
13+
"@osrd-project/ui-manchette": "^0.0.57",
14+
"@osrd-project/ui-manchette-with-spacetimechart": "^0.0.57",
15+
"@osrd-project/ui-spacetimechart": "^0.0.57",
16+
"@osrd-project/ui-speedspacechart": "^0.0.57",
1717
"@react-pdf/renderer": "^3.4.2",
1818
"@redux-devtools/extension": "^3.3.0",
1919
"@reduxjs/toolkit": "^2.1.0",

front/public/locales/en/simulation.json

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@
8888
},
8989
"trainList": "Train list",
9090
"waiting": "Loading...",
91+
"waypointMenu": {
92+
"hide": "Hide this OP"
93+
},
9194
"waypointsPanel": {
9295
"name": "name",
9396
"secondaryCode": "CH",

front/public/locales/fr/simulation.json

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@
8888
},
8989
"trainList": "Liste des trains",
9090
"waiting": "Chargement en cours…",
91+
"waypointMenu": {
92+
"hide": "Masquer ce PR"
93+
},
9194
"waypointsPanel": {
9295
"name": "nom",
9396
"secondaryCode": "CH",

front/src/modules/simulationResult/components/ManchetteWithSpaceTimeChart/ManchetteWithSpaceTimeChart.tsx

+28-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMemo, useRef, useState } from 'react';
22

33
import { KebabHorizontal } from '@osrd-project/ui-icons';
4-
import { Manchette } from '@osrd-project/ui-manchette';
4+
import Manchette, { type WaypointMenuData } from '@osrd-project/ui-manchette';
55
import { useManchettesWithSpaceTimeChart } from '@osrd-project/ui-manchette-with-spacetimechart';
66
import {
77
ConflictLayer,
@@ -11,11 +11,13 @@ import {
1111
OccupancyBlockLayer,
1212
} from '@osrd-project/ui-spacetimechart';
1313
import type { Conflict } from '@osrd-project/ui-spacetimechart';
14+
import cx from 'classnames';
1415
import { compact } from 'lodash';
1516

1617
import type { OperationalPoint, TrainSpaceTimeData } from 'applications/operationalStudies/types';
1718
import upward from 'assets/pictures/workSchedules/ScheduledMaintenanceUp.svg';
1819
import type { PostWorkSchedulesProjectPathApiResponse } from 'common/api/osrdEditoastApi';
20+
import OSRDMenu from 'common/OSRDMenu';
1921
import cutSpaceTimeRect from 'modules/simulationResult/components/SpaceTimeChart/helpers/utils';
2022
import { ASPECT_LABELS_COLORS } from 'modules/simulationResult/consts';
2123
import type {
@@ -27,6 +29,7 @@ import type {
2729
import SettingsPanel from './SettingsPanel';
2830
import ManchetteMenuButton from '../SpaceTimeChart/ManchetteMenuButton';
2931
import ProjectionLoadingMessage from '../SpaceTimeChart/ProjectionLoadingMessage';
32+
import useWaypointMenu from '../SpaceTimeChart/useWaypointMenu';
3033
import WaypointsPanel from '../SpaceTimeChart/WaypointsPanel';
3134

3235
type ManchetteWithSpaceTimeChartProps = {
@@ -55,6 +58,7 @@ const ManchetteWithSpaceTimeChartWrapper = ({
5558
projectionLoaderData: { totalTrains, allTrainsProjected },
5659
height = MANCHETTE_WITH_SPACE_TIME_CHART_DEFAULT_HEIGHT,
5760
}: ManchetteWithSpaceTimeChartProps) => {
61+
const manchetteWithSpaceTimeCharWrappertRef = useRef<HTMLDivElement>(null);
5862
const manchetteWithSpaceTimeChartRef = useRef<HTMLDivElement>(null);
5963

6064
const [waypointsPanelIsOpen, setWaypointsPanelIsOpen] = useState(false);
@@ -178,8 +182,26 @@ const ManchetteWithSpaceTimeChartWrapper = ({
178182
}));
179183
});
180184

185+
const waypointMenuData = useWaypointMenu(waypointsPanelData);
186+
187+
const manchettePropsWithWaypointMenu = useMemo(
188+
() => ({
189+
...manchetteProps,
190+
waypoints: manchetteProps.waypoints.map((waypoint) => ({
191+
...waypoint,
192+
onClick: waypointMenuData.handleWaypointClick,
193+
})),
194+
waypointMenuData: {
195+
menu: <OSRDMenu menuRef={waypointMenuData.menuRef} items={waypointMenuData.menuItems} />,
196+
activeWaypointId: waypointMenuData.activeWaypointId,
197+
manchetteWrapperRef: manchetteWithSpaceTimeCharWrappertRef,
198+
} as WaypointMenuData,
199+
}),
200+
[manchetteProps, waypointMenuData]
201+
);
202+
181203
return (
182-
<div className="manchette-space-time-chart-wrapper">
204+
<div ref={manchetteWithSpaceTimeCharWrappertRef} className="manchette-space-time-chart-wrapper">
183205
<div className="header">
184206
{waypointsPanelData && (
185207
<>
@@ -204,11 +226,13 @@ const ManchetteWithSpaceTimeChartWrapper = ({
204226
<div className="header-separator" />
205227
<div
206228
ref={manchetteWithSpaceTimeChartRef}
207-
className="manchette flex"
229+
className={cx('manchette flex', {
230+
'no-scroll': !!waypointMenuData.activeWaypointId,
231+
})}
208232
style={{ height }}
209233
onScroll={handleScroll}
210234
>
211-
<Manchette {...manchetteProps} height={height} />
235+
<Manchette {...manchettePropsWithWaypointMenu} height={height} />
212236
<div
213237
className="space-time-chart-container"
214238
style={{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
3+
import { EyeClosed } from '@osrd-project/ui-icons';
4+
import { omit } from 'lodash';
5+
import { useTranslation } from 'react-i18next';
6+
import { useSelector } from 'react-redux';
7+
8+
import { useOsrdConfSelectors } from 'common/osrdContext';
9+
import type { OSRDMenuItem } from 'common/OSRDMenu';
10+
import type { WaypointsPanelData } from 'modules/simulationResult/types';
11+
import useModalFocusTrap from 'utils/hooks/useModalFocusTrap';
12+
13+
const useWaypointMenu = (waypointsPanelData?: WaypointsPanelData) => {
14+
const { filteredWaypoints, setFilteredWaypoints, projectionPath } = waypointsPanelData || {};
15+
const { t } = useTranslation('simulation');
16+
17+
const { getTimetableID } = useOsrdConfSelectors();
18+
const timetableId = useSelector(getTimetableID);
19+
20+
const [activeWaypointId, setActiveWaypointId] = useState<string>();
21+
const [isClickOnWaypoint, setIsClickOnWaypoint] = useState(false);
22+
23+
const menuRef = useRef<HTMLDivElement>(null);
24+
25+
const closeMenu = () => {
26+
setActiveWaypointId(undefined);
27+
};
28+
29+
useModalFocusTrap(menuRef, closeMenu, { focusOnFirstElement: true });
30+
31+
useEffect(() => {
32+
const handleClickOutside = (event: MouseEvent) => {
33+
// Avoid closing the menu when clicking on another waypoint
34+
if (activeWaypointId && (event.target as HTMLElement).closest('.waypoint')) {
35+
setIsClickOnWaypoint(true);
36+
}
37+
// Close the menu if the user clicks outside of it
38+
if (!menuRef.current?.contains(event.target as Node)) {
39+
closeMenu();
40+
}
41+
};
42+
43+
if (activeWaypointId) {
44+
document.addEventListener('mousedown', handleClickOutside);
45+
} else {
46+
document.removeEventListener('mousedown', handleClickOutside);
47+
}
48+
49+
return () => {
50+
document.removeEventListener('mousedown', handleClickOutside);
51+
};
52+
}, [activeWaypointId]);
53+
54+
const menuItems: OSRDMenuItem[] = [
55+
{
56+
title: t('waypointMenu.hide'),
57+
icon: <EyeClosed />,
58+
disabled: filteredWaypoints ? filteredWaypoints.length <= 2 : false,
59+
disabledMessage: t('waypointsPanel.warning'),
60+
onClick: () => {
61+
closeMenu();
62+
setFilteredWaypoints?.((prevFilteredWaypoints) => {
63+
const newFilteredWaypoints = prevFilteredWaypoints.filter(
64+
(waypoint) => waypoint.id !== activeWaypointId
65+
);
66+
67+
// We need to removed the id because it can change for waypoints added by map click
68+
const simplifiedPath = projectionPath?.map((waypoint) =>
69+
omit(waypoint, ['id', 'deleted'])
70+
);
71+
72+
// TODO : when switching to the manchette back-end manager, remove all logic using
73+
// cleanScenarioLocalStorage from projet/study/scenario components (single/multi select)
74+
localStorage.setItem(
75+
`${timetableId}-${JSON.stringify(simplifiedPath)}`,
76+
JSON.stringify(newFilteredWaypoints)
77+
);
78+
return newFilteredWaypoints;
79+
});
80+
},
81+
},
82+
];
83+
84+
const handleWaypointClick = (id: string) => {
85+
// Avoid reopening the menu when clicking on another waypoint or on the same one
86+
if (isClickOnWaypoint) {
87+
setIsClickOnWaypoint(false);
88+
return;
89+
}
90+
setActiveWaypointId(id);
91+
};
92+
93+
return { menuRef, menuItems, activeWaypointId, handleWaypointClick };
94+
};
95+
96+
export default useWaypointMenu;

front/src/modules/simulationResult/types.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Dispatch, SetStateAction } from 'react';
2+
13
import type {
24
LayerData,
35
PowerRestrictionValues,
@@ -41,7 +43,7 @@ export type ProjectionData = {
4143

4244
export type WaypointsPanelData = {
4345
filteredWaypoints: OperationalPoint[];
44-
setFilteredWaypoints: (waypoints: OperationalPoint[]) => void;
46+
setFilteredWaypoints: Dispatch<SetStateAction<OperationalPoint[]>>;
4547
projectionPath: TrainScheduleBase['path'];
4648
};
4749

front/src/styles/scss/common/components/_manchetteWithSpaceTimeChart.scss

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
0 4px 9px rgba(0, 0, 0, 0.06),
55
0 1px 2px rgba(0, 0, 0, 0.19);
66
background-color: var(--white100);
7+
position: relative;
78

89
.header {
910
position: relative;
@@ -37,6 +38,15 @@
3738
.manchette {
3839
overflow-y: auto;
3940
overflow-x: hidden;
41+
42+
&.no-scroll {
43+
overflow-y: clip;
44+
}
45+
46+
.osrd-menu {
47+
width: 305px;
48+
transform: translateY(-2px);
49+
}
4050
}
4151

4252
.space-time-chart-container {

0 commit comments

Comments
 (0)