Skip to content

Commit 1323383

Browse files
committed
wip
1 parent 298dcc2 commit 1323383

File tree

13 files changed

+641
-31
lines changed

13 files changed

+641
-31
lines changed

ui-manchette-with-spacetimechart/src/assets/sampleData.ts

+41
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,44 @@ export const SAMPLE_PATHS_DATA: ProjectPathTrainResult[] = [
108108
],
109109
},
110110
];
111+
112+
export const SAMPLE_LILLE_PATH: ProjectPathTrainResult[] = [
113+
{
114+
id: 473,
115+
name: 'Lille',
116+
departureTime: new Date('2024-10-31T18:00:00Z'),
117+
spaceTimeCurves: [
118+
{
119+
positions: [
120+
0, 973, 3888, 15512, 34807, 77990, 161783, 302369, 1087000, 1275519, 1507539, 1781998,
121+
2078298, 7105000, 7765210, 8269097, 9513529, 9690951, 9734604, 18952000, 19610271,
122+
20337896, 21913398, 23131090, 23785933, 24468620, 24886989, 25309925, 26314632, 27939817,
123+
29005068, 31693844, 32344754, 32835520, 33835800, 35211506, 37123552, 38723540, 38901023,
124+
39371710, 49050484, 50280606, 51331106, 51656350, 67651442, 68184387, 68538743, 69072700,
125+
69456195, 69896213, 71555215, 72087186, 72486801, 74881914, 76122420, 76627596, 83089250,
126+
83972674, 84499983, 85908992, 86600296, 97103690, 97812718, 98471860, 102261198,
127+
102438723, 102805998, 123575426, 123752939, 124244320, 138716292, 139423550, 140305457,
128+
141009740, 142229981, 142938176, 143670865, 145511645, 145866260, 146275450, 149531454,
129+
149709008, 150036490, 179786416, 179963968, 180299287, 209444300, 210603338, 211703892,
130+
212706446, 213611000, 220144766, 220388896, 220778885, 221561364, 222273026, 222805022,
131+
223334684, 223710680, 223871678, 224058342, 224177340, 224278338, 224361336, 224406668,
132+
224444000, 225002278, 225038332, 225048888, 225064000, 225208283, 225212000, 225215000,
133+
225216000,
134+
],
135+
times: [
136+
0, 2000, 4000, 8000, 12000, 18000, 26000, 35690, 82767, 92767, 102767, 112767, 122192,
137+
272995, 290995, 302914, 330914, 334914, 335899, 543292, 557292, 571292, 599292, 619292,
138+
629292, 639292, 645292, 651292, 665292, 687292, 701292, 735292, 743292, 749292, 761292,
139+
777292, 799016, 817016, 819016, 824321, 933214, 947214, 959214, 962893, 1142842, 1148842,
140+
1152842, 1158864, 1163182, 1168142, 1186806, 1192806, 1197311, 1224256, 1238256, 1243953,
141+
1316649, 1326649, 1332649, 1348649, 1356470, 1474643, 1482643, 1490085, 1532715, 1534715,
142+
1538855, 1772514, 1774514, 1780052, 1942863, 1950863, 1960863, 1968863, 1982679, 1990679,
143+
1998964, 2019673, 2023673, 2028286, 2064916, 2066916, 2070605, 2405292, 2407292, 2411069,
144+
2738954, 2752510, 2766510, 2780510, 2794510, 2901426, 2905426, 2911810, 2925366, 2939366,
145+
2951366, 2965366, 2977366, 2983366, 2991366, 2997366, 3003366, 3009366, 3013366, 3017366,
146+
3084362, 3089472, 3091472, 3095472, 3147409, 3148965, 3150965, 3152965,
147+
],
148+
},
149+
],
150+
},
151+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
3+
export type MenuItem = {
4+
title: string;
5+
icon: React.ReactNode;
6+
onClick: (e: React.MouseEvent) => void;
7+
};
8+
9+
type MenuProps = {
10+
items: MenuItem[];
11+
};
12+
13+
/**
14+
* Example of waypoint menu that could be passed to the manchette as props
15+
*/
16+
const Menu = ({ items }: MenuProps) => (
17+
<div className="menu">
18+
{items.map(({ title, icon, onClick }) => (
19+
<button key={title} type="button" className="menu-item" onClick={onClick}>
20+
<span className="icon">{icon}</span>
21+
<span>{title}</span>
22+
</button>
23+
))}
24+
</div>
25+
);
26+
27+
export default Menu;

ui-manchette-with-spacetimechart/src/stories/base.stories.tsx

+61-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable import/no-unresolved */
2-
import React, { useRef } from 'react';
2+
import React, { useRef, useState } from 'react';
33

4+
import { EyeClosed, Telescope } from '@osrd-project/ui-icons';
45
import { Manchette } from '@osrd-project/ui-manchette';
56
import type { ProjectPathTrainResult, Waypoint } from '@osrd-project/ui-manchette/dist/types';
67
import { PathLayer, SpaceTimeChart } from '@osrd-project/ui-spacetimechart';
@@ -10,6 +11,8 @@ import '@osrd-project/ui-core/dist/theme.css';
1011
import '@osrd-project/ui-manchette/dist/theme.css';
1112
import '@osrd-project/ui-manchette-with-spacetimechart/dist/theme.css';
1213

14+
import Menu, { type MenuItem } from './Menu';
15+
import useElementInView from './useElementInView';
1316
import { SAMPLE_WAYPOINTS, SAMPLE_PATHS_DATA } from '../assets/sampleData';
1417
import useManchettesWithSpaceTimeChart from '../hooks/useManchetteWithSpaceTimeChart';
1518

@@ -18,6 +21,7 @@ type ManchetteWithSpaceTimeWrapperProps = {
1821
projectPathTrainResult: ProjectPathTrainResult[];
1922
selectedTrain: number;
2023
};
24+
2125
const DEFAULT_HEIGHT = 561;
2226

2327
const ManchetteWithSpaceTimeWrapper = ({
@@ -26,13 +30,57 @@ const ManchetteWithSpaceTimeWrapper = ({
2630
selectedTrain,
2731
}: ManchetteWithSpaceTimeWrapperProps) => {
2832
const manchetteWithSpaceTimeChartRef = useRef<HTMLDivElement>(null);
33+
34+
const [activeWaypointIndex, setActiveWaypointPointIndex] = useState<number>();
35+
const [observerData, setObserverData] = useState<{
36+
waypointRef: React.RefObject<HTMLDivElement>;
37+
waypointsContainerRef: React.RefObject<HTMLDivElement>;
38+
}>();
39+
40+
// We need to pass the scrollable element as the container
41+
const isElementInView = useElementInView(
42+
observerData?.waypointRef,
43+
manchetteWithSpaceTimeChartRef
44+
);
45+
46+
const menuItems: MenuItem[] = [
47+
{
48+
title: 'Action 1',
49+
icon: <EyeClosed />,
50+
onClick: (e: React.MouseEvent) => {
51+
e.stopPropagation();
52+
setActiveWaypointPointIndex(undefined);
53+
},
54+
},
55+
{
56+
title: 'Action 2',
57+
icon: <Telescope />,
58+
onClick: (e: React.MouseEvent) => {
59+
e.stopPropagation();
60+
setActiveWaypointPointIndex(undefined);
61+
},
62+
},
63+
];
64+
65+
const handleWaypointClick = (
66+
waypointIndex: number, // Change index for op id
67+
waypointRef: React.RefObject<HTMLDivElement>,
68+
waypointsContainerRef: React.RefObject<HTMLDivElement>
69+
) => {
70+
console.log('coucou', waypointIndex, waypointRef, waypointsContainerRef);
71+
setActiveWaypointPointIndex(waypointIndex);
72+
setObserverData({ waypointRef, waypointsContainerRef });
73+
};
74+
2975
const { manchetteProps, spaceTimeChartProps, handleScroll } = useManchettesWithSpaceTimeChart(
3076
waypoints,
3177
projectPathTrainResult,
3278
manchetteWithSpaceTimeChartRef,
3379
selectedTrain
3480
);
3581

82+
console.log('displayMenu', activeWaypointIndex, isElementInView);
83+
3684
return (
3785
<div className="manchette-space-time-chart-wrapper">
3886
<div
@@ -45,7 +93,18 @@ const ManchetteWithSpaceTimeWrapper = ({
4593
style={{ height: `${DEFAULT_HEIGHT}px` }}
4694
onScroll={handleScroll}
4795
>
48-
<Manchette {...manchetteProps} />
96+
<Manchette
97+
{...manchetteProps}
98+
waypoints={manchetteProps.waypoints.map((op) => ({
99+
...op,
100+
onClick: handleWaypointClick,
101+
}))}
102+
waypointMenuData={{
103+
activeWaypointIndex,
104+
menu: <Menu items={menuItems} />,
105+
isWaypointInView: isElementInView,
106+
}}
107+
/>
49108
<div
50109
className="space-time-chart-container w-full sticky"
51110
style={{ bottom: 0, left: 0, top: 2, height: `${DEFAULT_HEIGHT - 6}px` }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useState, useEffect, type RefObject } from 'react';
2+
3+
const MANCHETTE_ACTIONS_HEIGHT = '-40px';
4+
5+
const useElementInView = <T extends HTMLElement>(
6+
ref?: RefObject<T>,
7+
containerRef?: RefObject<HTMLElement>,
8+
options: IntersectionObserverInit = {
9+
threshold: 1,
10+
rootMargin: `0px 0px ${MANCHETTE_ACTIONS_HEIGHT} 0px`,
11+
}
12+
): boolean => {
13+
const [isInView, setIsInView] = useState(false);
14+
15+
useEffect(() => {
16+
const currentRef = ref?.current;
17+
const root = containerRef?.current || null;
18+
19+
const parent = root?.parentElement;
20+
console.log('parent', parent);
21+
22+
if (!currentRef) return;
23+
24+
const observer = new IntersectionObserver(
25+
([entry]) => {
26+
console.log('entry', entry);
27+
setIsInView(entry.isIntersecting);
28+
},
29+
{
30+
...options,
31+
root,
32+
}
33+
);
34+
35+
observer.observe(currentRef.parentElement!);
36+
37+
return () => observer.disconnect();
38+
}, [ref, containerRef, options]);
39+
40+
return isInView;
41+
};
42+
43+
export default useElementInView;

ui-manchette/src/components/Waypoint.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,20 @@ type WaypointProps = {
1010
waypoint: InteractiveWaypoint;
1111
isActive: boolean;
1212
waypointMenu?: React.ReactNode;
13+
waypointsContainerRef: React.RefObject<HTMLDivElement>;
14+
isWaypointInView?: boolean;
1315
index: number;
1416
};
1517

1618
const Waypoint = ({
1719
waypoint: { name, secondaryCode, id, position, display, onClick },
1820
isActive,
1921
waypointMenu,
22+
waypointsContainerRef,
23+
isWaypointInView,
2024
index,
2125
}: WaypointProps) => {
22-
const opRef = useRef<HTMLDivElement>(null);
26+
const waypointRef = useRef<HTMLDivElement>(null);
2327

2428
if (!display) return null;
2529

@@ -29,9 +33,9 @@ const Waypoint = ({
2933
'menu-active': isActive,
3034
})}
3135
id={id}
32-
ref={opRef}
36+
ref={waypointRef}
3337
onClick={() => {
34-
if (onClick) onClick(index);
38+
if (onClick) onClick(index, waypointRef, waypointsContainerRef);
3539
}}
3640
>
3741
<div className="waypoint-position justify-self-start text-end">
@@ -45,7 +49,7 @@ const Waypoint = ({
4549

4650
<div className="waypoint-type"></div>
4751
<div className="waypoint-separator"></div>
48-
{waypointMenu && isActive && waypointMenu}
52+
{waypointMenu && isActive && isWaypointInView && waypointMenu}
4953
</div>
5054
);
5155
};
+25-19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useRef } from 'react';
22

33
import Waypoint from './Waypoint';
44
import type { WaypointMenuData, InteractiveWaypoint } from '../types';
@@ -8,23 +8,29 @@ type WaypointListProps = {
88
waypointMenuData?: WaypointMenuData;
99
};
1010

11-
const WaypointList = ({ waypoints, waypointMenuData }: WaypointListProps) => (
12-
<div className="waypoint-list ">
13-
{waypoints.map((waypoint, index) => (
14-
<div
15-
key={waypoint.id}
16-
className="waypoint-wrapper flex flex-col justify-start"
17-
style={waypoint.styles}
18-
>
19-
<Waypoint
20-
waypoint={waypoint}
21-
isActive={waypointMenuData?.activeWaypointIndex === index}
22-
waypointMenu={waypointMenuData?.menu}
23-
index={index}
24-
/>
25-
</div>
26-
))}
27-
</div>
28-
);
11+
const WaypointList = ({ waypoints, waypointMenuData }: WaypointListProps) => {
12+
const waypointsContainerRef = useRef<HTMLDivElement>(null);
13+
14+
return (
15+
<div ref={waypointsContainerRef} className="waypoint-list ">
16+
{waypoints.map((waypoint, index) => (
17+
<div
18+
key={waypoint.id}
19+
className="waypoint-wrapper flex flex-col justify-start"
20+
style={waypoint.styles}
21+
>
22+
<Waypoint
23+
waypoint={waypoint}
24+
isActive={waypointMenuData?.activeWaypointIndex === index}
25+
waypointMenu={waypointMenuData?.menu}
26+
waypointsContainerRef={waypointsContainerRef}
27+
isWaypointInView={waypointMenuData?.isWaypointInView}
28+
index={index}
29+
/>
30+
</div>
31+
))}
32+
</div>
33+
);
34+
};
2935

3036
export default WaypointList;

ui-manchette/src/stories/Manchette.stories.tsx

+20-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import '@osrd-project/ui-manchette/dist/theme.css';
55
import { EyeClosed, Telescope } from '@osrd-project/ui-icons';
66
import type { Meta, StoryObj } from '@storybook/react';
77

8-
import { SAMPLE_WAYPOINTS } from './assets/sampleData';
8+
import { SAMPLE_LONG, SAMPLE_WAYPOINTS } from './assets/sampleData';
99
import Menu, { type MenuItem } from './components/Menu';
1010
import Manchette from '../components/Manchette';
11+
import useElementInView from './components/useElementInView';
1112

1213
const meta: Meta<typeof Manchette> = {
1314
component: Manchette,
@@ -30,7 +31,17 @@ const meta: Meta<typeof Manchette> = {
3031

3132
const ManchetteWithWaypointMenu = () => {
3233
const [activeWaypointIndex, setActiveWaypointPointIndex] = useState<number>();
34+
const [observerData, setObserverData] = useState<{
35+
waypointRef: React.RefObject<HTMLDivElement>;
36+
waypointsContainerRef: React.RefObject<HTMLDivElement>;
37+
}>();
3338

39+
const isElementInView = useElementInView(
40+
observerData?.waypointRef,
41+
observerData?.waypointsContainerRef
42+
);
43+
44+
console.log('isElementInView', isElementInView);
3445
const menuItems: MenuItem[] = [
3546
{
3647
title: 'Action 1',
@@ -50,13 +61,18 @@ const ManchetteWithWaypointMenu = () => {
5061
},
5162
];
5263

53-
const handleWaypointClick = (waypointIndex: number) => {
64+
const handleWaypointClick = (
65+
waypointIndex: number,
66+
waypointRef: React.RefObject<HTMLDivElement>,
67+
waypointsContainerRef: React.RefObject<HTMLDivElement>
68+
) => {
5469
setActiveWaypointPointIndex(waypointIndex);
70+
setObserverData({ waypointRef, waypointsContainerRef });
5571
};
5672

5773
return (
5874
<Manchette
59-
waypoints={SAMPLE_WAYPOINTS.map((waypoint) => ({
75+
waypoints={SAMPLE_LONG.map((waypoint) => ({
6076
...waypoint,
6177
display: true,
6278
onClick: handleWaypointClick,
@@ -67,6 +83,7 @@ const ManchetteWithWaypointMenu = () => {
6783
toggleMode={() => {}}
6884
waypointMenuData={{
6985
activeWaypointIndex,
86+
isWaypointInView: isElementInView,
7087
menu: <Menu items={menuItems} />,
7188
}}
7289
/>

0 commit comments

Comments
 (0)