Skip to content

Commit

Permalink
ui-manchette: integration manchette + spacetimechart done
Browse files Browse the repository at this point in the history
  • Loading branch information
Math-R committed Jul 9, 2024
1 parent 102e682 commit 0916d3d
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 24 deletions.
123 changes: 99 additions & 24 deletions ui-manchette/src/components/Manchette.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import React, { useEffect } from 'react';
import React, { useEffect, useRef } from 'react';
import OperationalPointList from './OperationalPointList';
import { OperationalPointType, ProjectPathTrainResult, StyledOperationalPointType } from '../types';
import { ZoomIn, ZoomOut } from '@osrd-project/ui-icons';
import { SpaceTimeChart, PathLayer } from '@osrd-project/ui-spacetimechart';
import { calcOperationalPointsToDisplay, operationalPointsHeight } from './helpers';
import { OperationalPoint, PathData } from 'ui-spacetimechart/dist/lib/types';
import { BASE_KM_HEIGHT } from './consts';
import { SpaceScale } from 'ui-spacetimechart/dist/lib/types';

import {
OperationalPoint,
PathData,
SpaceScale,
} from '@osrd-project/ui-spacetimechart/dist/lib/types';
import {
BASE_KM_HEIGHT,
BASE_OP_HEIGHT,
INITIAL_OP_LIST_HEIGHT,

Check warning on line 15 in ui-manchette/src/components/Manchette.tsx

View workflow job for this annotation

GitHub Actions / build

'INITIAL_OP_LIST_HEIGHT' is defined but never used
INITIAL_SPACE_TIME_CHART_HEIGHT,
} from './consts';
import { getDiff } from '../utils/vector';
import { useIsOverflow } from '../hooks/isOverFlowed';
type ManchetteProps = {
operationalPoints: OperationalPointType[];
projectPathTrainResult: ProjectPathTrainResult[];
Expand All @@ -19,13 +28,20 @@ const Manchette: React.FC<ManchetteProps> = ({
projectPathTrainResult,
isProportionnal = false,
}) => {
const manchette = useRef<HTMLDivElement>(null);

const [zoomY, setZoomY] = React.useState(1);

const xZoomLevel = 1;

const [xOffset, setXOffset] = React.useState(0);

const [yOffset, setYOffset] = React.useState(0);
const [panning, setPanning] = React.useState<{ initialOffset: { x: number; y: number } } | null>(
null
);

const [scrollPosition, setScrollPosition] = React.useState(0);

const [operationalPointsToDisplay, setOperationalPointsToDisplay] = React.useState<
StyledOperationalPointType[]
Expand All @@ -35,6 +51,8 @@ const Manchette: React.FC<ManchetteProps> = ({
[]
);

const [panY, setPanY] = React.useState<boolean>(false);

const [scales, setScales] = React.useState<SpaceScale[]>([]);

const zoomYIn = () => {
Expand All @@ -45,6 +63,10 @@ const Manchette: React.FC<ManchetteProps> = ({
if (zoomY > 1) setZoomY(zoomY - 0.5);
};

useIsOverflow(manchette, (isOverflowFromCallback) => {
setPanY(isOverflowFromCallback);
});

const paths: (PathData & { color: string })[] = projectPathTrainResult.map((path, index) => ({
id: path.departure_time,
label: `Train ${index + 1}`,
Expand Down Expand Up @@ -80,13 +102,31 @@ const Manchette: React.FC<ManchetteProps> = ({
from: point.position,
to: ops[i + 1].position,
...(!isProportionnal
? { size: 50 * zoomY }
? { size: BASE_OP_HEIGHT * zoomY }
: { coefficient: ((1000 / BASE_KM_HEIGHT) * 1000) / zoomY }),
};
});
return scales;
};

const handleScroll = () => {
if (manchette.current) {
const { scrollTop } = manchette.current;
if (scrollTop || scrollTop === 0) {
setScrollPosition(scrollTop);
setYOffset(scrollTop);
}
}
};

useEffect(() => {
window.addEventListener('scroll', handleScroll, { passive: true });

return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);

useEffect(() => {
const computedOperationalPoints = calcOperationalPointsToDisplay(
operationalPoints,
Expand All @@ -102,12 +142,15 @@ const Manchette: React.FC<ManchetteProps> = ({

useEffect(() => {
setScales(getScales(operationalPointsChart));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [operationalPointsChart]);

return (
<div className="manchette flex">
<div ref={manchette} className="manchette flex" onScroll={handleScroll}>
<div className="manchette-container">
<OperationalPointList operationalPoints={operationalPointsToDisplay} />
<div className=" bg-ambientB-10" style={{ minHeight: '521px' }}>
<OperationalPointList operationalPoints={operationalPointsToDisplay} />
</div>
<div className="manchette-actions flex items-center">
<div className=" flex items-center ">
<button className="h-full px-3 w-full" onClick={zoomYOut} disabled={zoomY === 1}>
Expand All @@ -121,22 +164,54 @@ const Manchette: React.FC<ManchetteProps> = ({
</div>
</div>
</div>
{scales.length > 0 && (
<SpaceTimeChart
className="inset-0 absolute"
operationalPoints={operationalPointsChart}
spaceOrigin={0}
spaceScales={scales}
timeOrigin={Math.min(...projectPathTrainResult.map((p) => +new Date(p.departure_time)))}
timeScale={60000 / xZoomLevel}
xOffset={xOffset}
yOffset={yOffset}
>
{paths.map((path, i) => (
<PathLayer key={path.id} index={i} path={path} color={path.color} />
))}
</SpaceTimeChart>
)}
<div
className="space-time-chart-container w-full sticky"
style={{ bottom: 0, left: 0, top: 0, height: `${INITIAL_SPACE_TIME_CHART_HEIGHT}px` }}
>
{scales.length > 0 && (
<SpaceTimeChart
className="inset-0 absolute h-full"
style={{ top: 0, height: 'calc(100% - 6px)' }}
operationalPoints={operationalPointsChart}
spaceOrigin={0}
spaceScales={scales}
timeOrigin={Math.min(...projectPathTrainResult.map((p) => +new Date(p.departure_time)))}
timeScale={60000 / xZoomLevel}
xOffset={xOffset}
yOffset={-scrollPosition + 14}
onPan={({ initialPosition, position, isPanning }) => {
const diff = getDiff(initialPosition, position);
if (!isPanning) {
setPanning(null);
} else if (!panning) {
setPanning({ initialOffset: { x: xOffset, y: yOffset } });
} else {
const { initialOffset } = panning;
setXOffset(initialOffset.x + diff.x);
if (panY) {
const newYPos = initialOffset.y - diff.y;

if (
manchette.current &&
newYPos >= 0 &&
newYPos + INITIAL_SPACE_TIME_CHART_HEIGHT <= manchette.current?.scrollHeight
) {
setYOffset(newYPos);
setScrollPosition(yOffset);
if (manchette.current && manchette.current.scrollHeight) {
manchette.current.scrollTop = newYPos;
}
}
}
}
}}
>
{paths.map((path, i) => (
<PathLayer key={path.id} index={i} path={path} color={path.color} />
))}
</SpaceTimeChart>
)}
</div>
</div>
);
};
Expand Down
2 changes: 2 additions & 0 deletions ui-manchette/src/components/consts.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export const BASE_OP_HEIGHT = 32;
export const BASE_KM_HEIGHT = 8;
export const INITIAL_OP_LIST_HEIGHT = 521;
export const INITIAL_SPACE_TIME_CHART_HEIGHT = INITIAL_OP_LIST_HEIGHT + 40;
39 changes: 39 additions & 0 deletions ui-manchette/src/hooks/isOverFlowed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState, useLayoutEffect, useRef, RefObject } from 'react';

Check warning on line 1 in ui-manchette/src/hooks/isOverFlowed.ts

View workflow job for this annotation

GitHub Actions / build

'React' is defined but never used

Check warning on line 1 in ui-manchette/src/hooks/isOverFlowed.ts

View workflow job for this annotation

GitHub Actions / build

'useRef' is defined but never used

interface CallbackFunction {
(hasOverflow: boolean): void;
}

interface CallbackFunction {
(hasOverflow: boolean): void;
}

export const useIsOverflow = (
ref: RefObject<HTMLElement>,
callback: CallbackFunction
): boolean | undefined => {
const [isOverflow, setIsOverflow] = useState<boolean | undefined>(undefined);

useLayoutEffect(() => {
const { current } = ref;

const trigger = () => {
if (current) {
const hasOverflow = current.scrollHeight > current.clientHeight;
setIsOverflow(hasOverflow);

if (callback) callback(hasOverflow);
}
};

if (current) {
if ('ResizeObserver' in window) {
new ResizeObserver(trigger).observe(current);
}

trigger();
}
}, [callback, ref]);

return isOverflow;
};
6 changes: 6 additions & 0 deletions ui-manchette/src/styles/manchette.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@
border: solid 1px;
@apply border-black-25
}

.space-time-chart-container {
height: 561px;
width: 100%;
bottom:0;
}
}
11 changes: 11 additions & 0 deletions ui-manchette/src/utils/vector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type Point = {
x: number;
y: number;
};

export function getDiff(a: Point, b: Point): Point {
return {
x: b.x - a.x,
y: b.y - a.y,
};
}

0 comments on commit 0916d3d

Please sign in to comment.