diff --git a/ui-manchette-with-spacetimechart/src/assets/sampleData.ts b/ui-manchette-with-spacetimechart/src/assets/sampleData.ts index 933b693b2..9bf1d4336 100644 --- a/ui-manchette-with-spacetimechart/src/assets/sampleData.ts +++ b/ui-manchette-with-spacetimechart/src/assets/sampleData.ts @@ -3,28 +3,136 @@ import { type ProjectPathTrainResult, type Waypoint } from '@osrd-project/ui-man export const SAMPLE_WAYPOINTS: Waypoint[] = [ { - id: 'South_West_station', - name: 'South_West_station', - secondaryCode: 'BV', + id: '1', position: 0, + name: 'Brest', + secondaryCode: 'BV', + }, + { + id: '2', + position: 3983000, + name: 'Le Rody', + secondaryCode: '00', }, { - id: 'Mid_West_station', - name: 'Mid_West_station', + id: '3', + position: 7349000, + name: 'Kerhuon', secondaryCode: 'BV', - position: 13000000, }, { - id: 'Mid_East_station', - name: 'Mid_East_station', + id: '4', + position: 13882000, + name: 'La Forest', + secondaryCode: '00', + }, + { + id: '5', + position: 18756000, + name: 'Landerneau', secondaryCode: 'BV', - position: 27550000, }, { - id: 'North_East_station', - name: 'North_East_station', + id: '6', + position: 23358000, + name: 'La Roche', + secondaryCode: '00', + }, + { + id: '7', + position: 33219000, + name: 'Landivisiau', + secondaryCode: '00', + }, + { + id: '8', + position: 40612000, + name: 'Guimiliau', + secondaryCode: '00', + }, + { + id: '9', + position: 44960000, + name: 'St-Thégonnec', + secondaryCode: '00', + }, + { + id: '10', + position: 50471000, + name: 'Pleyber-Christ', + secondaryCode: '00', + }, + { + id: '11', + position: 59554000, + name: 'Morlaix', + secondaryCode: 'BV', + }, + { + id: '12', + position: 68903000, + name: 'Plouigneau', + secondaryCode: '00', + }, + { + id: '13', + position: 83194000, + name: 'Plounérin', + secondaryCode: '00', + }, + { + id: '14', + position: 91392000, + name: 'Plouaret', + secondaryCode: '00', + }, + { + id: '15', + position: 102478000, + name: 'Belle-Isle-Bégard', + secondaryCode: '00', + }, + { + id: '16', + position: 117570000, + name: 'Guingamp', + secondaryCode: '00', + }, + { + id: '17', + position: 130463000, + name: 'Châtelaudren-Plouagat', + secondaryCode: 'BV', + }, + { + id: '18', + position: 137315000, + name: 'Plouvara-Plerneuf', + secondaryCode: '00', + }, + { + id: '19', + position: 141157000, + name: 'La Méaugon', + secondaryCode: '00', + }, + { + id: '20', + position: 147745000, + name: 'St-Brieuc', + secondaryCode: 'BV', + }, + { + id: '21', + position: 157361000, + name: 'Yffiniac', + secondaryCode: '00', + }, + { + id: '22', + position: 168056000, + name: 'Lamballe', secondaryCode: 'BV', - position: 47050000, }, ]; @@ -32,25 +140,29 @@ export const SAMPLE_PATHS_DATA: ProjectPathTrainResult[] = [ { id: 1, name: 'Train 1', - departureTime: new Date('2024-06-26T13:57:56Z'), + departureTime: new Date('2024-10-23T09:00:00Z'), spaceTimeCurves: [ { positions: [ - 0, 2387, 8642, 17449, 28163, 68719, 118795, 196855, 406366, 706610, 1102281, 1593458, - 2283409, 3769056, 6017136, 6717050, 7000182, 9027079, 9707244, 9996142, 12098691, - 12253708, 12305708, 12368708, 12400708, 12424708, 12440708, 12445708, 12448708, 12449708, - 12449708, 12452095, 12458350, 12467157, 12490130, 12534192, 12586918, 12734703, 12960612, - 13274175, 13680049, 14178452, 14981343, 15960690, 18996492, 19991990, 20987213, 22014758, - 22841279, 23850682, 26994761, 28007168, 29005477, 30000065, 32001426, 34522793, 44638554, - 44776040, 44832040, 44901040, 44937040, 44976040, 44992040, 44997040, 45000040, 45001040, + 0, 973, 3888, 8737, 24205, 47311, 96148, 138058, 214779, 307916, 417225, 587701, 785672, + 1189417, 1740630, 18354735, 18756000, 33419000, 33941065, 46298000, 46456685, 46704905, + 47993924, 57127089, 57572008, 57918672, 58233336, 58516000, 59423000, 59568935, 59686000, + 59902000, 60254993, 60721538, 61496476, 62320048, 68922269, 69447000, 82202000, 83877014, + 85097789, 85140266, 89756800, 90298672, 90868000, 117770000, 118043180, 118327970, + 119238293, 120110374, 121572153, 126517715, 127501896, 128277000, 140917870, 141157000, + 156005000, 156818235, 157861414, 158428394, 159444615, 164321446, 165140000, 165940000, + 166535000, 166831000, 167095000, 167272000, 167480000, 167656000, 167800000, 167912000, + 167956000, 167992000, 168020000, 168040000, 168052000, 168055000, 168056000, ], times: [ - 0, 42000, 44000, 46000, 48000, 54000, 60000, 68000, 86000, 108000, 134000, 164000, 204000, - 286000, 406000, 442000, 456000, 558000, 594000, 610000, 724159, 733630, 737630, 743630, - 747630, 751630, 755630, 757630, 759630, 761630, 821630, 823630, 825630, 827630, 831630, - 837630, 843630, 857630, 875630, 897630, 923630, 953630, 999630, 1053630, 1215630, 1269630, - 1327630, 1389630, 1437630, 1493630, 1661630, 1713630, 1761630, 1807630, 1903630, 2031630, - 2561539, 2569617, 2573617, 2579617, 2583617, 2589617, 2593617, 2595617, 2597617, 2599617, + 0, 2000, 4000, 6000, 10000, 14000, 20000, 24000, 30000, 36000, 42000, 50000, 58000, 72000, + 87695, 514914, 526026, 965920, 980356, 1298104, 1302104, 1308104, 1336119, 1518782, + 1528116, 1536116, 1544116, 1552116, 1579327, 1583595, 1587019, 1593499, 1603499, 1615499, + 1633499, 1650729, 1782773, 1793885, 2080876, 2114460, 2136460, 2137226, 2220323, 2230547, + 2242547, 2847848, 2853848, 2859848, 2877848, 2893848, 2918783, 2999711, 3017045, 3033045, + 3317467, 3323021, 3679370, 3697370, 3717370, 3727370, 3744455, 3824258, 3838480, 3854480, + 3868480, 3876480, 3884480, 3890480, 3898480, 3906480, 3914480, 3922480, 3926480, 3930480, + 3934480, 3938480, 3942480, 3944480, 3946480, ], }, ], @@ -58,25 +170,29 @@ export const SAMPLE_PATHS_DATA: ProjectPathTrainResult[] = [ { id: 2, name: 'Train 2', - departureTime: new Date('2024-06-26T13:42:56Z'), + departureTime: new Date('2024-10-23T09:15:00Z'), spaceTimeCurves: [ { positions: [ - 0, 2387, 8642, 17449, 28163, 68719, 118795, 196855, 406366, 706610, 1102281, 1593458, - 2283409, 3769056, 6017136, 6717050, 7000182, 9027079, 9707244, 9996142, 12098691, - 12253708, 12305708, 12368708, 12400708, 12424708, 12440708, 12445708, 12448708, 12449708, - 12449708, 12452095, 12458350, 12467157, 12490130, 12534192, 12586918, 12734703, 12960612, - 13274175, 13680049, 14178452, 14981343, 15960690, 18996492, 19991990, 20987213, 22014758, - 22841279, 23850682, 26994761, 28007168, 29005477, 30000065, 32001426, 34522793, 44638554, - 44776040, 44832040, 44901040, 44937040, 44976040, 44992040, 44997040, 45000040, 45001040, + 0, 973, 3888, 8737, 24205, 47311, 96148, 138058, 214779, 307916, 417225, 587701, 785672, + 1189417, 1740630, 18354735, 18756000, 33419000, 33941065, 46298000, 46456685, 46704905, + 47993924, 57127089, 57572008, 57918672, 58233336, 58516000, 59423000, 59568935, 59686000, + 59902000, 60254993, 60721538, 61496476, 62320048, 68922269, 69447000, 82202000, 83877014, + 85097789, 85140266, 89756800, 90298672, 90868000, 117770000, 118043180, 118327970, + 119238293, 120110374, 121572153, 126517715, 127501896, 128277000, 140917870, 141157000, + 156005000, 156818235, 157861414, 158428394, 159444615, 164321446, 165140000, 165940000, + 166535000, 166831000, 167095000, 167272000, 167480000, 167656000, 167800000, 167912000, + 167956000, 167992000, 168020000, 168040000, 168052000, 168055000, 168056000, ], times: [ - 0, 42000, 44000, 46000, 48000, 54000, 60000, 68000, 86000, 108000, 134000, 164000, 204000, - 286000, 406000, 442000, 456000, 558000, 594000, 610000, 724159, 733630, 737630, 743630, - 747630, 751630, 755630, 757630, 759630, 761630, 821630, 823630, 825630, 827630, 831630, - 837630, 843630, 857630, 875630, 897630, 923630, 953630, 999630, 1053630, 1215630, 1269630, - 1327630, 1389630, 1437630, 1493630, 1661630, 1713630, 1761630, 1807630, 1903630, 2031630, - 2561539, 2569617, 2573617, 2579617, 2583617, 2589617, 2593617, 2595617, 2597617, 2599617, + 0, 2000, 4000, 6000, 10000, 14000, 20000, 24000, 30000, 36000, 42000, 50000, 58000, 72000, + 87695, 514914, 526026, 965920, 980356, 1298104, 1302104, 1308104, 1336119, 1518782, + 1528116, 1536116, 1544116, 1552116, 1579327, 1583595, 1587019, 1593499, 1603499, 1615499, + 1633499, 1650729, 1782773, 1793885, 2080876, 2114460, 2136460, 2137226, 2220323, 2230547, + 2242547, 2847848, 2853848, 2859848, 2877848, 2893848, 2918783, 2999711, 3017045, 3033045, + 3317467, 3323021, 3679370, 3697370, 3717370, 3727370, 3744455, 3824258, 3838480, 3854480, + 3868480, 3876480, 3884480, 3890480, 3898480, 3906480, 3914480, 3922480, 3926480, 3930480, + 3934480, 3938480, 3942480, 3944480, 3946480, ], }, ], @@ -84,25 +200,29 @@ export const SAMPLE_PATHS_DATA: ProjectPathTrainResult[] = [ { id: 3, name: 'Train 3', - departureTime: new Date('2024-06-26T14:12:56Z'), + departureTime: new Date('2024-10-23T09:30:00Z'), spaceTimeCurves: [ { positions: [ - 0, 2387, 8642, 17449, 28163, 68719, 118795, 196855, 406366, 706610, 1102281, 1593458, - 2283409, 3769056, 6017136, 6717050, 7000182, 9027079, 9707244, 9996142, 12098691, - 12253708, 12305708, 12368708, 12400708, 12424708, 12440708, 12445708, 12448708, 12449708, - 12449708, 12452095, 12458350, 12467157, 12490130, 12534192, 12586918, 12734703, 12960612, - 13274175, 13680049, 14178452, 14981343, 15960690, 18996492, 19991990, 20987213, 22014758, - 22841279, 23850682, 26994761, 28007168, 29005477, 30000065, 32001426, 34522793, 44638554, - 44776040, 44832040, 44901040, 44937040, 44976040, 44992040, 44997040, 45000040, 45001040, + 0, 973, 3888, 8737, 24205, 47311, 96148, 138058, 214779, 307916, 417225, 587701, 785672, + 1189417, 1740630, 18354735, 18756000, 33419000, 33941065, 46298000, 46456685, 46704905, + 47993924, 57127089, 57572008, 57918672, 58233336, 58516000, 59423000, 59568935, 59686000, + 59902000, 60254993, 60721538, 61496476, 62320048, 68922269, 69447000, 82202000, 83877014, + 85097789, 85140266, 89756800, 90298672, 90868000, 117770000, 118043180, 118327970, + 119238293, 120110374, 121572153, 126517715, 127501896, 128277000, 140917870, 141157000, + 156005000, 156818235, 157861414, 158428394, 159444615, 164321446, 165140000, 165940000, + 166535000, 166831000, 167095000, 167272000, 167480000, 167656000, 167800000, 167912000, + 167956000, 167992000, 168020000, 168040000, 168052000, 168055000, 168056000, ], times: [ - 0, 42000, 44000, 46000, 48000, 54000, 60000, 68000, 86000, 108000, 134000, 164000, 204000, - 286000, 406000, 442000, 456000, 558000, 594000, 610000, 724159, 733630, 737630, 743630, - 747630, 751630, 755630, 757630, 759630, 761630, 821630, 823630, 825630, 827630, 831630, - 837630, 843630, 857630, 875630, 897630, 923630, 953630, 999630, 1053630, 1215630, 1269630, - 1327630, 1389630, 1437630, 1493630, 1661630, 1713630, 1761630, 1807630, 1903630, 2031630, - 2561539, 2569617, 2573617, 2579617, 2583617, 2589617, 2593617, 2595617, 2597617, 2599617, + 0, 2000, 4000, 6000, 10000, 14000, 20000, 24000, 30000, 36000, 42000, 50000, 58000, 72000, + 87695, 514914, 526026, 965920, 980356, 1298104, 1302104, 1308104, 1336119, 1518782, + 1528116, 1536116, 1544116, 1552116, 1579327, 1583595, 1587019, 1593499, 1603499, 1615499, + 1633499, 1650729, 1782773, 1793885, 2080876, 2114460, 2136460, 2137226, 2220323, 2230547, + 2242547, 2847848, 2853848, 2859848, 2877848, 2893848, 2918783, 2999711, 3017045, 3033045, + 3317467, 3323021, 3679370, 3697370, 3717370, 3727370, 3744455, 3824258, 3838480, 3854480, + 3868480, 3876480, 3884480, 3890480, 3898480, 3906480, 3914480, 3922480, 3926480, 3930480, + 3934480, 3938480, 3942480, 3944480, 3946480, ], }, ], diff --git a/ui-manchette/src/stories/components/Menu.tsx b/ui-manchette-with-spacetimechart/src/stories/Menu.tsx similarity index 65% rename from ui-manchette/src/stories/components/Menu.tsx rename to ui-manchette-with-spacetimechart/src/stories/Menu.tsx index 67d26f22a..469602c5d 100644 --- a/ui-manchette/src/stories/components/Menu.tsx +++ b/ui-manchette-with-spacetimechart/src/stories/Menu.tsx @@ -3,17 +3,18 @@ import React from 'react'; export type MenuItem = { title: string; icon: React.ReactNode; - onClick: () => void; + onClick: (e: React.MouseEvent) => void; }; type MenuProps = { - menuRef: React.RefObject; items: MenuItem[]; - style?: React.CSSProperties; }; -const Menu = ({ menuRef, items, style }: MenuProps) => ( -
+/** + * Example of waypoint menu that could be passed to the manchette as props + */ +const Menu = ({ items }: MenuProps) => ( +
{items.map(({ title, icon, onClick }) => ( - - +}: ManchetteProps) => { + const [menuPosition, setMenuPosition] = useState(); + const activeWaypointRef = useRef(null); + + // Allow to track the menu position after we might have scrolled in the page + useLayoutEffect(() => { + const manchetteWrapperPosition = + waypointMenuData?.manchetteWrapperRef?.current?.getBoundingClientRect().top; + const waypointPosition = activeWaypointRef?.current?.getBoundingClientRect().top; + + if (!manchetteWrapperPosition || !waypointPosition) { + setMenuPosition(undefined); + } else { + setMenuPosition(waypointPosition - manchetteWrapperPosition); + } + }, [waypointMenuData]); + + return ( +
+ {waypointMenuData?.menu && waypointMenuData.activeWaypointId && ( +
+ {waypointMenuData.menu} +
+ )} +
+ + {children}
-
- + + +
+
+ + Linéaire +
+ +
- -); + ); +}; export default Manchette; diff --git a/ui-manchette/src/components/Waypoint.tsx b/ui-manchette/src/components/Waypoint.tsx index 1283e0bf1..eb91ca958 100644 --- a/ui-manchette/src/components/Waypoint.tsx +++ b/ui-manchette/src/components/Waypoint.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from 'react'; +import React from 'react'; import cx from 'classnames'; @@ -8,33 +8,37 @@ import { positionMmToKm } from '../utils'; type WaypointProps = { waypoint: InteractiveWaypoint; + nameRef?: React.RefObject; isActive: boolean; + isMenuActive?: boolean; }; const Waypoint = ({ waypoint: { name, secondaryCode, id, position, display, onClick }, + nameRef, isActive, + isMenuActive, }: WaypointProps) => { - const opRef = useRef(null); - if (!display) return null; return (
{ - if (onClick) onClick(id, opRef.current); + if (onClick && !isMenuActive) onClick(id); }} >
{positionMmToKm(position)}
-
{name}
+
+ {name} +
{secondaryCode}
diff --git a/ui-manchette/src/components/WaypointList.tsx b/ui-manchette/src/components/WaypointList.tsx index e4910fe96..91a02df6d 100644 --- a/ui-manchette/src/components/WaypointList.tsx +++ b/ui-manchette/src/components/WaypointList.tsx @@ -1,22 +1,28 @@ import React from 'react'; import Waypoint from './Waypoint'; -import { type InteractiveWaypoint } from '../types'; +import type { InteractiveWaypoint } from '../types'; type WaypointListProps = { waypoints: InteractiveWaypoint[]; activeWaypointId?: string; + activeWaypointRef?: React.RefObject; }; -const WaypointList = ({ waypoints, activeWaypointId }: WaypointListProps) => ( +const WaypointList = ({ waypoints, activeWaypointId, activeWaypointRef }: WaypointListProps) => (
{waypoints.map((waypoint) => (
- +
))}
diff --git a/ui-manchette/src/index.ts b/ui-manchette/src/index.ts index 85f614f9a..015b9a2ef 100644 --- a/ui-manchette/src/index.ts +++ b/ui-manchette/src/index.ts @@ -2,6 +2,7 @@ import '@osrd-project/ui-core/dist/theme.css'; import './styles/main.css'; import './components/consts'; -export { default as Waypoint } from './components/Waypoint'; -export { default as WaypointList } from './components/WaypointList'; -export { default as Manchette } from './components/Manchette'; +import Manchette from './components/Manchette'; + +export default Manchette; +export type { WaypointMenuData, Waypoint, ProjectPathTrainResult } from './types'; diff --git a/ui-manchette/src/stories/Manchette.stories.tsx b/ui-manchette/src/stories/Manchette.stories.tsx index 67f9737e7..042dec87c 100644 --- a/ui-manchette/src/stories/Manchette.stories.tsx +++ b/ui-manchette/src/stories/Manchette.stories.tsx @@ -1,12 +1,8 @@ -import React, { useRef, useState } from 'react'; - import '@osrd-project/ui-core/dist/theme.css'; import '@osrd-project/ui-manchette/dist/theme.css'; -import { EyeClosed, Telescope } from '@osrd-project/ui-icons'; import type { Meta, StoryObj } from '@storybook/react'; import { SAMPLE_WAYPOINTS } from './assets/sampleData'; -import Menu, { type MenuItem } from './components/Menu'; import Manchette from '../components/Manchette'; const meta: Meta = { @@ -28,71 +24,11 @@ const meta: Meta = { }, }; -const ManchetteWithWaypointMenu = () => { - const [menuPosition, setMenuPosition] = useState<{ top: number; left: number } | null>(null); - const [activeWaypointId, setActiveWaypointId] = useState(); - - const menuRef = useRef(null); - - const menuItems: MenuItem[] = [ - { - title: 'Action 1', - icon: , - onClick: () => { - setMenuPosition(null); - setActiveWaypointId(undefined); - }, - }, - { - title: 'Action 2', - icon: , - onClick: () => { - setMenuPosition(null); - setActiveWaypointId(undefined); - }, - }, - ]; - - const handleWaypointClick = (id: string, ref: HTMLDivElement | null) => { - if (!ref) return; - const position = ref.getBoundingClientRect(); - setMenuPosition({ top: position.bottom - 2, left: position.left }); - setActiveWaypointId(id); - }; - - return ( - ({ - ...waypoint, - display: true, - onClick: (id, ref) => { - handleWaypointClick(id, ref); - }, - }))} - zoomYIn={() => {}} - zoomYOut={() => {}} - resetZoom={() => {}} - toggleMode={() => {}} - activeWaypointId={activeWaypointId} - > - {menuPosition && ( - - )} - - ); -}; - export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: { waypoints: SAMPLE_WAYPOINTS.map((waypoint) => ({ ...waypoint, display: true })), }, }; - -export const WaypointMenu = () => ; diff --git a/ui-manchette/src/styles/main.css b/ui-manchette/src/styles/main.css index 17bab3d70..c20e89cf9 100644 --- a/ui-manchette/src/styles/main.css +++ b/ui-manchette/src/styles/main.css @@ -2,6 +2,5 @@ @import 'tailwindcss/components'; @import 'tailwindcss/utilities'; @import 'manchette.css'; -@import 'menu.css'; @import 'waypoint.css'; @import 'waypoint-list.css'; diff --git a/ui-manchette/src/styles/manchette.css b/ui-manchette/src/styles/manchette.css index 155f92228..6369f3165 100644 --- a/ui-manchette/src/styles/manchette.css +++ b/ui-manchette/src/styles/manchette.css @@ -1,4 +1,11 @@ .manchette-container { + .menu-wrapper { + position: absolute; + z-index: 12; + left: 8px; + margin-top: 28px; + } + .manchette-actions { align-items: center; background-color: rgba(250, 249, 245, 0.6); @@ -16,7 +23,6 @@ backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); - .zoom-buttons { display: flex; align-items: center; diff --git a/ui-manchette/src/styles/waypoint.css b/ui-manchette/src/styles/waypoint.css index bc4541800..8c9dbafe6 100644 --- a/ui-manchette/src/styles/waypoint.css +++ b/ui-manchette/src/styles/waypoint.css @@ -70,8 +70,8 @@ min-width: 37px; } - &:hover, - &.menu-active { + &:not(.menu-active):hover, + &.waypoint-active { @apply bg-black-5; border-radius: 4px; cursor: pointer; @@ -81,6 +81,10 @@ } } + &.menu-active { + cursor: default; + } + &::after { content: ''; position: absolute; diff --git a/ui-manchette/src/types.ts b/ui-manchette/src/types.ts index 8b89771e8..62bb77973 100644 --- a/ui-manchette/src/types.ts +++ b/ui-manchette/src/types.ts @@ -10,7 +10,13 @@ export type Waypoint = { export type InteractiveWaypoint = Waypoint & { styles?: CSSProperties; display?: boolean; - onClick?: (opId: string, opRef: HTMLDivElement | null) => void; + onClick?: (waypointId: string) => void; +}; + +export type WaypointMenuData = { + menu: React.ReactNode; + activeWaypointId?: string; + manchetteWrapperRef: React.RefObject; }; export type ProjectPathTrainResult = {