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

ui-trackoccupancydiagram: add main layers #715

Draft
wants to merge 4 commits into
base: dev
Choose a base branch
from
Draft
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 storybook/postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const generateBasePostcssConfig = require('../postcss-base.config.cjs');

module.exports = generateBasePostcssConfig();
Original file line number Diff line number Diff line change
@@ -1,18 +1,266 @@
import React from 'react';
import React, { useMemo, useState } from 'react';

import type { Meta, StoryObj } from '@storybook/react';

import { KebabHorizontal } from '../../../ui-icons/src/index';
import TimeCaptions from '../../../ui-spacetimechart/src/components/TimeCaptions';
import { useCanvas, useDraw } from '../../../ui-spacetimechart/src/hooks/useCanvas';
import { useMouseTracking } from '../../../ui-spacetimechart/src/hooks/useMouseTracking';
import { useSize } from '../../../ui-spacetimechart/src/hooks/useSize';
import { DEFAULT_THEME } from '../../../ui-spacetimechart/src/lib/consts';
import { CanvasContext, SpaceTimeChartContext } from '../../../ui-spacetimechart/src/lib/context';
import {
type SpaceTimeChartContextType,
type PickingElement,
type SpaceTimeChartTheme,
} from '../../../ui-spacetimechart/src/lib/types';
import { OPERATIONAL_POINTS } from '../../../ui-spacetimechart/src/stories/lib/paths';
import {
getTimeToPixel,
getSpaceToPixel,
getDataToPoint,
getPixelToTime,
getPixelToSpace,
getPointToData,
spaceScalesToBinaryTree,
} from '../../../ui-spacetimechart/src/utils/scales';
import {
TrackOccupancyManchette,
TrackOccupancyCanvas,
} from '../../../ui-trackoccupancydiagram/src/index';
import occupancyZones from '../samples/TrackOccupancyDiagramSamples/occupancyZones';
import tracks from '../samples/TrackOccupancyDiagramSamples/tracks';

type TrackOccupancyDiagramProps = {
xZoomLevel: number;
yZoomLevel: number;
xOffset: number;
yOffset: number;
spaceScaleType: 'linear' | 'proportional';
emptyData: boolean;
};

const X_ZOOM_LEVEL = 6;
const Y_ZOOM_LEVEL = 3;

const TrackOccupancyDiagram = ({
xZoomLevel,
yZoomLevel,
xOffset,
yOffset,
spaceScaleType,
emptyData,
}: TrackOccupancyDiagramProps) => {
const spaceOrigin = 0;
const [root, setRoot] = useState<HTMLDivElement | null>(null);
const { width, height } = useSize(root);
const [canvasesRoot, setCanvasesRoot] = useState<HTMLDivElement | null>(null);
const { width: trackOccupancyWidth, height: trackOccupancyHeight } = useSize(canvasesRoot);
const timeOrigin = +new Date('2024/04/02');
const timeScale = 60000 / xZoomLevel;
const swapAxis = undefined;
const hideGrid = undefined;
const hidePathsLabels = undefined;
const enableSnapping = undefined;
const showTicks = true;
const fullTheme: SpaceTimeChartTheme = {
...DEFAULT_THEME,
background: 'transparent',
timeGraduationsStyles: {
...DEFAULT_THEME.timeGraduationsStyles,
1: { ...DEFAULT_THEME.timeGraduationsStyles[1], color: 'transparent' },
},
};
const operationalPoints = useMemo(() => (emptyData ? [] : OPERATIONAL_POINTS), [emptyData]);
const spaceScales = useMemo(() => {
if (emptyData) {
return [];
}

return operationalPoints.slice(0, -1).map((point, i) => ({
from: point.position,
to: operationalPoints[i + 1].position,
...(spaceScaleType === 'linear'
? { size: 50 * yZoomLevel }
: { coefficient: 150 / yZoomLevel }),
}));
}, [emptyData, operationalPoints, spaceScaleType, yZoomLevel]);

const fingerprint = useMemo(
() =>
JSON.stringify({
width,
height,
spaceOrigin,
spaceScales,
timeOrigin,
timeScale,
xOffset,
yOffset,
swapAxis,
hideGrid,
hidePathsLabels,
showTicks,
}),
[
width,
height,
spaceOrigin,
spaceScales,
timeOrigin,
timeScale,
xOffset,
yOffset,
swapAxis,
hideGrid,
hidePathsLabels,
showTicks,
]
);

// TODO: when occupancyZones layer and zoom/pan are implemented, clean all unneeded variables from contextState, variables declared before contextState, and props. If needed, create a new context type.
const contextState: SpaceTimeChartContextType = useMemo(() => {
const spaceScaleTree = spaceScalesToBinaryTree(spaceOrigin, spaceScales);
const timeAxis = !swapAxis ? 'x' : 'y';
const spaceAxis = !swapAxis ? 'y' : 'x';

const TrackOccupancyDiagram = () => (
<div>
<TrackOccupancyManchette />
<TrackOccupancyCanvas />
</div>
);
// Data translation helpers:
let timePixelOffset;
let spacePixelOffset;

if (!swapAxis) {
timePixelOffset = xOffset;
spacePixelOffset = yOffset;
} else {
timePixelOffset = yOffset;
spacePixelOffset = xOffset;
}

const getTimePixel = getTimeToPixel(timeOrigin, timePixelOffset, timeScale);
const getSpacePixel = getSpaceToPixel(spacePixelOffset, spaceScaleTree);
const getPoint = getDataToPoint(getTimePixel, getSpacePixel, timeAxis, spaceAxis);
const getTime = getPixelToTime(timeOrigin, timePixelOffset, timeScale);
const getSpace = getPixelToSpace(spaceOrigin, spacePixelOffset, spaceScaleTree);
const getData = getPointToData(getTime, getSpace, timeAxis, spaceAxis);

const pickingElements: PickingElement[] = [];
const resetPickingElements = () => {
pickingElements.length = 0;
};
const registerPickingElement = (element: PickingElement) => {
pickingElements.push(element);
return pickingElements.length - 1;
};

return {
fingerprint,
width,
height,
trackOccupancyHeight,
trackOccupancyWidth,
getTimePixel,
getSpacePixel,
getPoint,
getTime,
getSpace,
getData,
pickingElements,
resetPickingElements,
registerPickingElement,
operationalPoints,
tracks,
occupancyZones,
spaceOrigin,
spaceScaleTree,
timeOrigin,
timeScale,
timePixelOffset,
spacePixelOffset,
timeAxis,
spaceAxis,
swapAxis: !!swapAxis,
enableSnapping: !!enableSnapping,
hideGrid: !!hideGrid,
hidePathsLabels: !!hidePathsLabels,
showTicks: !!showTicks,
theme: fullTheme,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fingerprint]);

const [spaceTicksRoot, setSpaceTicksRoot] = useState<HTMLDivElement | null>(null);
const mouseState = useMouseTracking(root);
const { position } = mouseState;
const { canvasContext } = useCanvas(canvasesRoot, contextState, position);
const { canvasContext: spaceTicksContext } = useCanvas(spaceTicksRoot, contextState, position);

return (
<div
className="bg-ambientB-10"
style={{
height: 680,
width: 1920,
padding: '30px 40px',
}}
>
<SpaceTimeChartContext.Provider value={contextState}>
<div
style={{
width: 1424,
boxShadow:
'0px 2px 4px 0 rgba(0, 0, 0, 0.22), 0 4px 7px -3px rgba(255, 171, 88, 0.17), inset 0 1px 0 0 rgb(255, 255, 255)',
borderRadius: 10,
}}
>
<div
className="bg-ambientB-5 flex flex-col justify-center"
style={{
height: 40,
width: '100%',
paddingLeft: 16,
borderRadius: '10px 10px 0 0',
boxShadow: 'inset 0 1px 0 0 rgb(255, 255, 255), inset 0 -1px 0 0 rgba(0, 0, 0, 0.25)',
}}
>
<KebabHorizontal />
</div>
<div className="flex">
<div
style={{
width: 200,
borderRadius: '0 0 0 10px',
}}
>
<TrackOccupancyManchette tracks={tracks} />
</div>
<CanvasContext.Provider value={canvasContext}>
<div
style={{
width: 1224,
borderRadius: '0 0 10px 0',
position: 'relative',
}}
>
<TrackOccupancyCanvas useDraw={useDraw} setCanvasesRoot={setCanvasesRoot} />
</div>
</CanvasContext.Provider>
</div>
</div>
<CanvasContext.Provider value={spaceTicksContext}>
<div
ref={setRoot}
className="relative"
style={{ marginLeft: 200, width: 1224, height: 33 }}
>
<div ref={setSpaceTicksRoot} className="absolute inset-0">
<TimeCaptions />
</div>
</div>
</CanvasContext.Provider>
</SpaceTimeChartContext.Provider>
</div>
);
};

const meta: Meta<typeof TrackOccupancyDiagram> = {
title: 'TrackOccupancyDiagram/Rendering',
Expand All @@ -24,9 +272,16 @@ const meta: Meta<typeof TrackOccupancyDiagram> = {
default: 'dark',
},
},
args: {},
args: {
xZoomLevel: X_ZOOM_LEVEL,
yZoomLevel: Y_ZOOM_LEVEL,
xOffset: 0,
yOffset: 0,
spaceScaleType: 'linear',
emptyData: false,
},

render: () => <TrackOccupancyDiagram />,
render: (args) => <TrackOccupancyDiagram {...args} />,
tags: ['autodocs'],
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const OccupancyZones = [
{
id: '1',
trackId: '1',
arrivalTrainName: 'train 1',
departureTrainName: 'train 2',
color: 'red',
arrivalTime: new Date(),
departureTime: new Date(),
},
{
id: '2',
trackId: '2',
arrivalTrainName: 'train 2',
departureTrainName: 'train 3',
color: 'blue',
arrivalTime: new Date(),
departureTime: new Date(),
},
{
id: '3',
trackId: '3',
arrivalTrainName: 'train 3',
departureTrainName: 'train 4',
color: 'green',
arrivalTime: new Date(),
departureTime: new Date(),
},
{
id: '4',
trackId: '4',
arrivalTrainName: 'train 4',
departureTrainName: 'train 5',
color: 'yellow',
arrivalTime: new Date(),
departureTime: new Date(),
},
];

export default OccupancyZones;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const tracks = [
{ id: '1', name: 'EV', line: '123456' },
{ id: '2', name: '2', line: '456123' },
{ id: '3', name: '2bis', line: '135246' },
{ id: '4', name: 'Z', line: '654321' },
{ id: '5', name: '1bis', line: '615243' },
{ id: '6', name: '1', line: '523416' },
];

export default tracks;
6 changes: 6 additions & 0 deletions storybook/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import osrdUiPreset from '../tailwind-preset.js';
/** @type {import('tailwindcss').Config} */
export default {
presets: [osrdUiPreset],
content: ['./stories/**/*.{js,jsx,ts,tsx}'],
};
4 changes: 4 additions & 0 deletions ui-spacetimechart/src/components/SpaceTimeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const SpaceTimeChart = (props: SpaceTimeChartProps) => {
enableSnapping,
hideGrid,
hidePathsLabels,
showTicks,
theme,
/* eslint-disable @typescript-eslint/no-unused-vars */
onPan,
Expand Down Expand Up @@ -73,6 +74,7 @@ export const SpaceTimeChart = (props: SpaceTimeChartProps) => {
swapAxis,
hideGrid,
hidePathsLabels,
showTicks,
}),
[
width,
Expand All @@ -86,6 +88,7 @@ export const SpaceTimeChart = (props: SpaceTimeChartProps) => {
swapAxis,
hideGrid,
hidePathsLabels,
showTicks,
]
);

Expand Down Expand Up @@ -148,6 +151,7 @@ export const SpaceTimeChart = (props: SpaceTimeChartProps) => {
enableSnapping: !!enableSnapping,
hideGrid: !!hideGrid,
hidePathsLabels: !!hidePathsLabels,
showTicks: !!showTicks,
theme: fullTheme,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
Loading
Loading