Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

front: add waypoint menu in manchette #9398

Merged
merged 3 commits into from
Dec 12, 2024
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
56 changes: 28 additions & 28 deletions front/package-lock.json

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

12 changes: 6 additions & 6 deletions front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
"@nivo/line": "^0.80.0",
"@openapi-contrib/openapi-schema-to-json-schema": "^5.1.0",
"@osrd-project/netzgrafik-frontend": "0.0.0-snapshot.37949a66933e8e1552c9b8e54f702ec491afd415",
"@osrd-project/ui-core": "^0.0.56",
"@osrd-project/ui-icons": "^0.0.56",
"@osrd-project/ui-manchette": "^0.0.56",
"@osrd-project/ui-manchette-with-spacetimechart": "^0.0.56",
"@osrd-project/ui-spacetimechart": "^0.0.56",
"@osrd-project/ui-speedspacechart": "^0.0.56",
"@osrd-project/ui-core": "^0.0.57",
"@osrd-project/ui-icons": "^0.0.57",
"@osrd-project/ui-manchette": "^0.0.57",
"@osrd-project/ui-manchette-with-spacetimechart": "^0.0.57",
"@osrd-project/ui-spacetimechart": "^0.0.57",
"@osrd-project/ui-speedspacechart": "^0.0.57",
"@react-pdf/renderer": "^3.4.2",
"@redux-devtools/extension": "^3.3.0",
"@reduxjs/toolkit": "^2.1.0",
Expand Down
3 changes: 3 additions & 0 deletions front/public/locales/en/simulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
},
"trainList": "Train list",
"waiting": "Loading...",
"waypointMenu": {
"hide": "Hide this OP"
},
"waypointsPanel": {
"name": "name",
"secondaryCode": "CH",
Expand Down
3 changes: 3 additions & 0 deletions front/public/locales/fr/simulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
},
"trainList": "Liste des trains",
"waiting": "Chargement en cours…",
"waypointMenu": {
"hide": "Masquer ce PR"
},
"waypointsPanel": {
"name": "nom",
"secondaryCode": "CH",
Expand Down
13 changes: 11 additions & 2 deletions front/src/common/OSRDMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export type OSRDMenuItem = {
title: string;
icon: React.ReactNode;
onClick: () => void;
disabled?: boolean;
disabledMessage?: string;
};

type OSRDMenuProps = {
Expand All @@ -13,8 +15,15 @@ type OSRDMenuProps = {

const OSRDMenu = ({ menuRef, items }: OSRDMenuProps) => (
<div ref={menuRef} className="osrd-menu">
{items.map(({ title, icon, onClick }) => (
<button key={title} type="button" className="menu-item" onClick={onClick}>
{items.map(({ title, icon, disabled, disabledMessage, onClick }) => (
<button
disabled={disabled}
title={disabled ? disabledMessage : undefined}
key={title}
type="button"
className="menu-item"
onClick={onClick}
>
<span className="icon">{icon}</span>
<span>{title}</span>
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo, useRef, useState } from 'react';

import { KebabHorizontal } from '@osrd-project/ui-icons';
import { Manchette } from '@osrd-project/ui-manchette';
import Manchette, { type WaypointMenuData } from '@osrd-project/ui-manchette';
import { useManchettesWithSpaceTimeChart } from '@osrd-project/ui-manchette-with-spacetimechart';
import {
ConflictLayer,
Expand All @@ -11,11 +11,13 @@ import {
OccupancyBlockLayer,
} from '@osrd-project/ui-spacetimechart';
import type { Conflict } from '@osrd-project/ui-spacetimechart';
import cx from 'classnames';
import { compact } from 'lodash';

import type { OperationalPoint, TrainSpaceTimeData } from 'applications/operationalStudies/types';
import upward from 'assets/pictures/workSchedules/ScheduledMaintenanceUp.svg';
import type { PostWorkSchedulesProjectPathApiResponse } from 'common/api/osrdEditoastApi';
import OSRDMenu from 'common/OSRDMenu';
import cutSpaceTimeRect from 'modules/simulationResult/components/SpaceTimeChart/helpers/utils';
import { ASPECT_LABELS_COLORS } from 'modules/simulationResult/consts';
import type {
Expand All @@ -27,6 +29,7 @@ import type {
import SettingsPanel from './SettingsPanel';
import ManchetteMenuButton from '../SpaceTimeChart/ManchetteMenuButton';
import ProjectionLoadingMessage from '../SpaceTimeChart/ProjectionLoadingMessage';
import useWaypointMenu from '../SpaceTimeChart/useWaypointMenu';
import WaypointsPanel from '../SpaceTimeChart/WaypointsPanel';

type ManchetteWithSpaceTimeChartProps = {
Expand Down Expand Up @@ -55,6 +58,7 @@ const ManchetteWithSpaceTimeChartWrapper = ({
projectionLoaderData: { totalTrains, allTrainsProjected },
height = MANCHETTE_WITH_SPACE_TIME_CHART_DEFAULT_HEIGHT,
}: ManchetteWithSpaceTimeChartProps) => {
const manchetteWithSpaceTimeCharWrappertRef = useRef<HTMLDivElement>(null);
const manchetteWithSpaceTimeChartRef = useRef<HTMLDivElement>(null);

const [waypointsPanelIsOpen, setWaypointsPanelIsOpen] = useState(false);
Expand Down Expand Up @@ -178,8 +182,26 @@ const ManchetteWithSpaceTimeChartWrapper = ({
}));
});

const waypointMenuData = useWaypointMenu(waypointsPanelData);

const manchettePropsWithWaypointMenu = useMemo(
() => ({
...manchetteProps,
waypoints: manchetteProps.waypoints.map((waypoint) => ({
...waypoint,
onClick: waypointMenuData.handleWaypointClick,
})),
waypointMenuData: {
menu: <OSRDMenu menuRef={waypointMenuData.menuRef} items={waypointMenuData.menuItems} />,
activeWaypointId: waypointMenuData.activeWaypointId,
manchetteWrapperRef: manchetteWithSpaceTimeCharWrappertRef,
} as WaypointMenuData,
}),
[manchetteProps, waypointMenuData]
);

return (
<div className="manchette-space-time-chart-wrapper">
<div ref={manchetteWithSpaceTimeCharWrappertRef} className="manchette-space-time-chart-wrapper">
<div className="header">
{waypointsPanelData && (
<>
Expand All @@ -204,11 +226,13 @@ const ManchetteWithSpaceTimeChartWrapper = ({
<div className="header-separator" />
<div
ref={manchetteWithSpaceTimeChartRef}
className="manchette flex"
className={cx('manchette flex', {
'no-scroll': !!waypointMenuData.activeWaypointId,
})}
style={{ height }}
onScroll={handleScroll}
>
<Manchette {...manchetteProps} height={height} />
<Manchette {...manchettePropsWithWaypointMenu} height={height} />
<div
className="space-time-chart-container"
style={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const ManchetteMenuButton = ({ setWaypointsPanelIsOpen }: ManchetteMenuButtonPro
},
];

useModalFocusTrap(menuRef, closeMenu);
useModalFocusTrap(menuRef, closeMenu, { focusOnFirstElement: true });

useEffect(() => {
// TODO : refacto useOutsideClick to accept a list of refs and use the hook here
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { useEffect, useRef, useState } from 'react';

import { EyeClosed } from '@osrd-project/ui-icons';
import { omit } from 'lodash';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

import { useOsrdConfSelectors } from 'common/osrdContext';
import type { OSRDMenuItem } from 'common/OSRDMenu';
import type { WaypointsPanelData } from 'modules/simulationResult/types';
import useModalFocusTrap from 'utils/hooks/useModalFocusTrap';

const useWaypointMenu = (waypointsPanelData?: WaypointsPanelData) => {
const { filteredWaypoints, setFilteredWaypoints, projectionPath } = waypointsPanelData || {};
const { t } = useTranslation('simulation');

const { getTimetableID } = useOsrdConfSelectors();
const timetableId = useSelector(getTimetableID);

const [activeWaypointId, setActiveWaypointId] = useState<string>();
const [isClickOnWaypoint, setIsClickOnWaypoint] = useState(false);

const menuRef = useRef<HTMLDivElement>(null);

const closeMenu = () => {
setActiveWaypointId(undefined);
};

useModalFocusTrap(menuRef, closeMenu, { focusOnFirstElement: true });

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
// Avoid closing the menu when clicking on another waypoint
if (activeWaypointId && (event.target as HTMLElement).closest('.waypoint')) {
setIsClickOnWaypoint(true);
}
// Close the menu if the user clicks outside of it
if (!menuRef.current?.contains(event.target as Node)) {
closeMenu();
}
};

if (activeWaypointId) {
document.addEventListener('mousedown', handleClickOutside);
} else {
document.removeEventListener('mousedown', handleClickOutside);
}

return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [activeWaypointId]);

const menuItems: OSRDMenuItem[] = [
{
title: t('waypointMenu.hide'),
icon: <EyeClosed />,
disabled: filteredWaypoints ? filteredWaypoints.length <= 2 : false,
disabledMessage: t('waypointsPanel.warning'),
onClick: () => {
closeMenu();
setFilteredWaypoints?.((prevFilteredWaypoints) => {
const newFilteredWaypoints = prevFilteredWaypoints.filter(
(waypoint) => waypoint.id !== activeWaypointId
);

// We need to removed the id because it can change for waypoints added by map click
const simplifiedPath = projectionPath?.map((waypoint) =>
omit(waypoint, ['id', 'deleted'])
);

// TODO : when switching to the manchette back-end manager, remove all logic using
// cleanScenarioLocalStorage from projet/study/scenario components (single/multi select)
localStorage.setItem(
`${timetableId}-${JSON.stringify(simplifiedPath)}`,
JSON.stringify(newFilteredWaypoints)
);
return newFilteredWaypoints;
});
},
},
];

const handleWaypointClick = (id: string) => {
// Avoid reopening the menu when clicking on another waypoint or on the same one
if (isClickOnWaypoint) {
setIsClickOnWaypoint(false);
return;
}
setActiveWaypointId(id);
};

return { menuRef, menuItems, activeWaypointId, handleWaypointClick };
};

export default useWaypointMenu;
4 changes: 3 additions & 1 deletion front/src/modules/simulationResult/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Dispatch, SetStateAction } from 'react';

import type {
LayerData,
PowerRestrictionValues,
Expand Down Expand Up @@ -41,7 +43,7 @@ export type ProjectionData = {

export type WaypointsPanelData = {
filteredWaypoints: OperationalPoint[];
setFilteredWaypoints: (waypoints: OperationalPoint[]) => void;
setFilteredWaypoints: Dispatch<SetStateAction<OperationalPoint[]>>;
projectionPath: TrainScheduleBase['path'];
};

Expand Down
Loading
Loading