Skip to content

Commit 63703d6

Browse files
committed
wip
1 parent 298dcc2 commit 63703d6

File tree

13 files changed

+2631
-34
lines changed

13 files changed

+2631
-34
lines changed

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

+2,023
Large diffs are not rendered by default.
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

+56-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,6 +30,45 @@ 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+
const isElementInView = useElementInView(
41+
observerData?.waypointRef,
42+
observerData?.waypointsContainerRef
43+
);
44+
45+
const menuItems: MenuItem[] = [
46+
{
47+
title: 'Action 1',
48+
icon: <EyeClosed />,
49+
onClick: (e: React.MouseEvent) => {
50+
e.stopPropagation();
51+
setActiveWaypointPointIndex(undefined);
52+
},
53+
},
54+
{
55+
title: 'Action 2',
56+
icon: <Telescope />,
57+
onClick: (e: React.MouseEvent) => {
58+
e.stopPropagation();
59+
setActiveWaypointPointIndex(undefined);
60+
},
61+
},
62+
];
63+
64+
const handleWaypointClick = (
65+
waypointIndex: number,
66+
waypointRef: React.RefObject<HTMLDivElement>,
67+
waypointsContainerRef: React.RefObject<HTMLDivElement>
68+
) => {
69+
setActiveWaypointPointIndex(waypointIndex);
70+
setObserverData({ waypointRef, waypointsContainerRef });
71+
};
2972
const { manchetteProps, spaceTimeChartProps, handleScroll } = useManchettesWithSpaceTimeChart(
3073
waypoints,
3174
projectPathTrainResult,
@@ -45,7 +88,18 @@ const ManchetteWithSpaceTimeWrapper = ({
4588
style={{ height: `${DEFAULT_HEIGHT}px` }}
4689
onScroll={handleScroll}
4790
>
48-
<Manchette {...manchetteProps} />
91+
<Manchette
92+
{...manchetteProps}
93+
waypoints={manchetteProps.waypoints.map((op) => ({
94+
...op,
95+
onClick: handleWaypointClick,
96+
}))}
97+
waypointMenuData={{
98+
activeWaypointIndex,
99+
menu: <Menu items={menuItems} />,
100+
isWaypointInView: isElementInView,
101+
}}
102+
/>
49103
<div
50104
className="space-time-chart-container w-full sticky"
51105
style={{ bottom: 0, left: 0, top: 2, height: `${DEFAULT_HEIGHT - 6}px` }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useState, useEffect, type RefObject } from 'react';
2+
3+
const useElementInView = <T extends HTMLElement>(
4+
ref?: RefObject<T>,
5+
containerRef?: RefObject<HTMLElement>,
6+
options: IntersectionObserverInit = { threshold: 1, rootMargin: '0px' }
7+
): boolean => {
8+
const [isInView, setIsInView] = useState(false);
9+
10+
useEffect(() => {
11+
const currentRef = ref?.current;
12+
const root = containerRef?.current || null;
13+
14+
if (!currentRef) return;
15+
16+
const observer = new IntersectionObserver(
17+
([entry]) => {
18+
console.log('entry', entry);
19+
setIsInView(entry.isIntersecting);
20+
},
21+
{
22+
...options,
23+
root,
24+
}
25+
);
26+
27+
observer.observe(currentRef);
28+
29+
return () => observer.disconnect();
30+
}, [ref, containerRef, options]);
31+
32+
return isInView;
33+
};
34+
35+
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

+26-4
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,23 @@ 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

73+
const isLastWaypoint =
74+
activeWaypointIndex !== undefined &&
75+
SAMPLE_WAYPOINTS.length - activeWaypointIndex <= 3 &&
76+
SAMPLE_WAYPOINTS.length * 32 >= 561;
77+
5778
return (
5879
<Manchette
59-
waypoints={SAMPLE_WAYPOINTS.map((waypoint) => ({
80+
waypoints={SAMPLE_LONG.map((waypoint) => ({
6081
...waypoint,
6182
display: true,
6283
onClick: handleWaypointClick,
@@ -67,7 +88,8 @@ const ManchetteWithWaypointMenu = () => {
6788
toggleMode={() => {}}
6889
waypointMenuData={{
6990
activeWaypointIndex,
70-
menu: <Menu items={menuItems} />,
91+
isWaypointInView: isElementInView,
92+
menu: <Menu items={menuItems} menuOnTop={isLastWaypoint} />,
7193
}}
7294
/>
7395
);

0 commit comments

Comments
 (0)