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

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions front/public/locales/en/simulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,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 @@ -84,6 +84,9 @@
},
"trainList": "Liste des trains",
"waiting": "Chargement en cours…",
"waypointMenu": {
"hide": "Masquer ce PR"
},
"waypointsPanel": {
"name": "nom",
"secondaryCode": "CH",
Expand Down
5 changes: 3 additions & 2 deletions front/src/common/OSRDMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ export type OSRDMenuItem = {
type OSRDMenuProps = {
menuRef: React.RefObject<HTMLDivElement>;
items: OSRDMenuItem[];
style?: React.CSSProperties;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is exposed to allow callers to position the menu, should we instead add a position property? That would deter callers from (ab)using style for things which should stay in CSS files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to set the width, it's not always the same depending of who's calling the menu. I made the call to pass this props instead of pass width and position. You would prefer that ?

};

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

import { KebabHorizontal } from '@osrd-project/ui-icons';
import { Manchette } from '@osrd-project/ui-manchette';
Expand All @@ -7,10 +7,12 @@ import { ConflictLayer, PathLayer, SpaceTimeChart } from '@osrd-project/ui-space
import type { Conflict } from '@osrd-project/ui-spacetimechart';

import type { OperationalPoint, TrainSpaceTimeData } from 'applications/operationalStudies/types';
import OSRDMenu from 'common/OSRDMenu';
import type { WaypointsPanelData } from 'modules/simulationResult/types';

import SettingsPanel from './SettingsPanel';
import ManchetteMenuButton from '../SpaceTimeChart/ManchetteMenuButton';
import useWaypointMenu from '../SpaceTimeChart/useWaypointMenu';
import WaypointsPanel from '../SpaceTimeChart/WaypointsPanel';

type ManchetteWithSpaceTimeChartProps = {
Expand All @@ -29,9 +31,11 @@ const ManchetteWithSpaceTimeChartWrapper = ({
waypointsPanelData,
conflicts = [],
}: ManchetteWithSpaceTimeChartProps) => {
const [heightOfManchetteWithSpaceTimeChart] = useState(DEFAULT_HEIGHT);
const manchetteWithSpaceTimeChartRef = useRef<HTMLDivElement>(null);

const [heightOfManchetteWithSpaceTimeChart] = useState(DEFAULT_HEIGHT);
const [showSettingsPanel, setShowSettingsPanel] = useState(false);
const [settings, setSettings] = useState({ showConflicts: false });
const [waypointsPanelIsOpen, setWaypointsPanelIsOpen] = useState(false);

const { manchetteProps, spaceTimeChartProps, handleScroll } = useManchettesWithSpaceTimeChart(
Expand All @@ -41,8 +45,22 @@ const ManchetteWithSpaceTimeChartWrapper = ({
selectedTrainScheduleId
);

const [showSettingsPanel, setShowSettingsPanel] = useState(false);
const [settings, setSettings] = useState({ showConflicts: false });
const waypointMenuData = useWaypointMenu(
manchetteWithSpaceTimeChartRef,
waypointsPanelData?.setFilteredWaypoints
);

const manchettePropsWithWaypointMenu = useMemo(
() => ({
...manchetteProps,
operationalPoints: manchetteProps.operationalPoints.map((op) => ({
...op,
onClick: waypointMenuData.handleWaypointClick,
})),
activeOperationalPointId: waypointMenuData.activeOperationalPointId,
}),
[manchetteProps, waypointMenuData]
);

return (
<div className="manchette-space-time-chart-wrapper">
Expand All @@ -64,7 +82,18 @@ const ManchetteWithSpaceTimeChartWrapper = ({
style={{ height: `${heightOfManchetteWithSpaceTimeChart}px` }}
onScroll={handleScroll}
>
<Manchette {...manchetteProps} />
<Manchette {...manchettePropsWithWaypointMenu}>
{waypointMenuData.menuPosition && (
<OSRDMenu
menuRef={waypointMenuData.menuRef}
items={waypointMenuData.menuItems}
style={{
top: waypointMenuData.menuPosition.top,
left: waypointMenuData.menuPosition.left,
}}
/>
)}
</Manchette>
<div
className="space-time-chart-container"
style={{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { useRef, useState, type Dispatch, type SetStateAction } from 'react';

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

import type { OperationalPoint } from 'applications/operationalStudies/types';
import type { OSRDMenuItem } from 'common/OSRDMenu';
import useModalFocusTrap from 'utils/hooks/useModalFocusTrap';
import useOutsideClick from 'utils/hooks/useOutsideClick';

const SPACETIME_CHART_HEADER_HEIGHT = 40;

const useWaypointMenu = (
manchetteWrapperRef: React.RefObject<HTMLDivElement>,
setFilteredWaypoints?: Dispatch<SetStateAction<OperationalPoint[]>>
) => {
const { t } = useTranslation('simulation');

const [menuPosition, setMenuPosition] = useState<{ top: number; left: number }>();
const [activeOperationalPointId, setActiveOperationalPointId] = useState<string>();

const menuRef = useRef<HTMLDivElement>(null);

const closeMenu = () => {
setMenuPosition(undefined);
setActiveOperationalPointId(undefined);
};

useOutsideClick(menuRef, closeMenu);
useModalFocusTrap(menuRef, closeMenu);

const menuItems: OSRDMenuItem[] = [
{
title: t('waypointMenu.hide'),
icon: <EyeClosed />,
onClick: () => {
closeMenu();
if (setFilteredWaypoints) {
setFilteredWaypoints((filteredWaypoints) =>
filteredWaypoints.filter((waypoint) => waypoint.id !== activeOperationalPointId)
);
}
},
},
];

const handleWaypointClick = (id: string, ref: HTMLDivElement | null) => {
if (!ref || !manchetteWrapperRef.current) return;
const position = ref.getBoundingClientRect();
const manchetteWrapperPosition = manchetteWrapperRef.current.getBoundingClientRect();

// The position of the clicked waypoint is relative to the viewport so we need to
// substract the position of the manchetteWrapper to get the accurate position
setMenuPosition({
top: position.bottom - manchetteWrapperPosition.top + SPACETIME_CHART_HEADER_HEIGHT,
left: position.left - manchetteWrapperPosition.left,
});
setActiveOperationalPointId(id);
};

return { menuRef, menuPosition, menuItems, activeOperationalPointId, 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 @@ -38,6 +40,6 @@ export type ProjectionData = {

export type WaypointsPanelData = {
filteredWaypoints: OperationalPoint[];
setFilteredWaypoints: (waypoints: OperationalPoint[]) => void;
setFilteredWaypoints: Dispatch<SetStateAction<OperationalPoint[]>>;
projectionPath: TrainScheduleBase['path'];
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@
.manchette {
overflow-y: auto;
overflow-x: hidden;

.osrd-menu {
width: 305px;
margin-top: -2px;
}

// TODO : remove this two styles when https://github.com/OpenRailAssociation/osrd-ui/issues/654 is fixed
.op {
height: 2rem;
line-height: 1.5rem;
padding-block: 0.1875rem 0.3125rem;

&::after {
bottom: 0.9844rem;
}
}
}

.space-time-chart-container {
Expand Down
Loading