Skip to content

Commit

Permalink
ui-trackoccupancydiagram: select zone on click
Browse files Browse the repository at this point in the history
- setup mouseContext from spacetimechart
- isolate TrackOccupancyDiagram to import it in a new story component
- use selectedTrainId and setSelectedTrainId as props
- add train selection on click in drawOccupationZone based on mouse position

Co-authored-by: Yohh <[email protected]>
Signed-off-by: Uriel-Sautron <[email protected]>
  • Loading branch information
Uriel-Sautron and Yohh committed Jan 8, 2025
1 parent 0d2c156 commit 012452c
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import React, { useMemo, useState } from 'react';
import React, { useEffect, 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 { useMouseInteractions } from '../../../ui-spacetimechart/src/hooks/useMouseInteractions';
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,
CanvasContext,
MouseContext,
SpaceTimeChartContext,
} from '../../../ui-spacetimechart/src/lib/context';
import type {
MouseContextType,
SpaceTimeChartContextType,
PickingElement,
SpaceTimeChartTheme,
} from '../../../ui-spacetimechart/src/lib/types';
import { OPERATIONAL_POINTS } from '../../../ui-spacetimechart/src/stories/lib/paths';
import {
Expand All @@ -39,6 +45,7 @@ type TrackOccupancyDiagramProps = {
spaceScaleType: 'linear' | 'proportional';
emptyData: boolean;
selectedTrainId: string;
setSelectedTrainId: (id: string) => void;
};

const OP_ID = 'story';
Expand All @@ -53,6 +60,8 @@ const TrackOccupancyDiagram = ({
yOffset,
spaceScaleType,
emptyData,
selectedTrainId,
setSelectedTrainId,
}: TrackOccupancyDiagramProps) => {
const spaceOrigin = 0;
const [root, setRoot] = useState<HTMLDivElement | null>(null);
Expand Down Expand Up @@ -192,11 +201,29 @@ const TrackOccupancyDiagram = ({
}, [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);

const mouseContext = useMemo<MouseContextType>(
() => ({
isHover: false,
position: mouseState.position,
hoveredItem: null,
data: contextState.getData(mouseState.position),
}),
[mouseState.position, contextState]
);

const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

const onClick = () => {
setMousePosition(mouseContext.position);
};

useMouseInteractions(canvasesRoot, mouseContext, { onClick }, contextState);
return (
<div id="track-occupancy-diagram-base-story" className="bg-ambientB-10">
<SpaceTimeChartContext.Provider value={contextState}>
Expand All @@ -209,14 +236,18 @@ const TrackOccupancyDiagram = ({
<TrackOccupancyManchette tracks={tracks} />
</div>
<CanvasContext.Provider value={canvasContext}>
<div className="main-container-canvas">
<TrackOccupancyCanvas
opId={OP_ID}
useDraw={useDraw}
setCanvasesRoot={setCanvasesRoot}
selectedTrainId={SELECTED_TRAIN_ID}
/>
</div>
<MouseContext.Provider value={mouseContext}>
<div className="main-container-canvas">
<TrackOccupancyCanvas
opId={OP_ID}
useDraw={useDraw}
setCanvasesRoot={setCanvasesRoot}
selectedTrainId={selectedTrainId}
setSelectedTrainId={setSelectedTrainId}
mousePosition={mousePosition}
/>
</div>
</MouseContext.Provider>
</CanvasContext.Provider>
</div>
</div>
Expand All @@ -232,9 +263,30 @@ const TrackOccupancyDiagram = ({
);
};

const meta: Meta<typeof TrackOccupancyDiagram> = {
const TrackOccupancyDiagramStory = ({ trainId }: { trainId: number }) => {
const [selectedTrainId, setSelectedTrainId] = useState('0');

useEffect(() => {
setSelectedTrainId(`${trainId}`);
}, [trainId]);

return (
<TrackOccupancyDiagram
xZoomLevel={X_ZOOM_LEVEL}
yZoomLevel={Y_ZOOM_LEVEL}
xOffset={0}
yOffset={0}
spaceScaleType="linear"
emptyData={false}
selectedTrainId={selectedTrainId}
setSelectedTrainId={setSelectedTrainId}
/>
);
};

const meta: Meta<typeof TrackOccupancyDiagramStory> = {
title: 'TrackOccupancyDiagram/Rendering',
component: TrackOccupancyDiagram,
component: TrackOccupancyDiagramStory,
decorators: [(Story) => <Story />],
parameters: {
layout: 'centered',
Expand All @@ -243,28 +295,18 @@ const meta: Meta<typeof TrackOccupancyDiagram> = {
},
},
args: {
xZoomLevel: X_ZOOM_LEVEL,
yZoomLevel: Y_ZOOM_LEVEL,
xOffset: 0,
yOffset: 0,
spaceScaleType: 'linear',
emptyData: false,
selectedTrainId: SELECTED_TRAIN_ID,
trainId: +SELECTED_TRAIN_ID,
},

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

export default meta;

type Story = StoryObj<typeof TrackOccupancyDiagram>;
type Story = StoryObj<typeof TrackOccupancyDiagramStory>;

export const TrackOccupancyDiagramStoryDefault: Story = {
args: {
xOffset: 0,
xZoomLevel: X_ZOOM_LEVEL,
yZoomLevel: Y_ZOOM_LEVEL,
selectedTrainId: SELECTED_TRAIN_ID,
trainId: 5,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@ const TrackOccupancyCanvas = ({
useDraw,
setCanvasesRoot,
selectedTrainId,
setSelectedTrainId,
mousePosition,
}: TrackOccupancyCanvasProps) => (
<div
id={`track-occupancy-canvas-${opId}`}
className="bg-white-100 canvas-container"
ref={setCanvasesRoot}
>
<TracksLayer useDraw={useDraw} />
<OccupancyZonesLayer useDraw={useDraw} selectedTrainId={selectedTrainId} />
<OccupancyZonesLayer
useDraw={useDraw}
selectedTrainId={selectedTrainId}
setSelectedTrainId={setSelectedTrainId}
mousePosition={mousePosition}
/>
</div>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,51 @@ const drawRemainingTrainsBox = ({ ctx, remainingTrainsNb, xPosition }: DrawRemai
const drawOccupationZone = ({
ctx,
zone,
tracks,
arrivalTimePixel,
departureTimePixel,
yPosition,
isThroughTrain,
selectedTrainId,
setSelectedTrainId,
xMousePosition,
yMousePosition,
index,
}: {
ctx: CanvasRenderingContext2D;
zone: OccupancyZone;
tracks: Track[];
arrivalTimePixel: number;
departureTimePixel: number;
yPosition: number;
isThroughTrain: boolean;
selectedTrainId: string;
setSelectedTrainId: (id: string) => void;
index: number;
xMousePosition: number;
yMousePosition: number;
}) => {
let currentSelectedTrainId = selectedTrainId;

const trackN = CANVAS_PADDING + TRACK_HEIGHT_CONTAINER * index + yPosition;
const canvasHeight = CANVAS_PADDING * 2 + TRACK_HEIGHT_CONTAINER * tracks.length;
const trackPosition = canvasHeight - trackN;

const arrowOffset = isThroughTrain ? 4 : 0;

const xCheck =
xMousePosition >= arrivalTimePixel - arrowOffset &&
xMousePosition <= departureTimePixel + arrowOffset;

const yCheck =
Math.abs(yMousePosition) >= trackPosition - OCCUPANCY_ZONE_HEIGHT - 1 - arrowOffset &&
Math.abs(yMousePosition) <= trackPosition + 1 + arrowOffset;

if (xCheck && yCheck) {
setSelectedTrainId(zone.id);
currentSelectedTrainId = zone.id;
}

ctx.fillStyle = zone.color;
ctx.strokeStyle = WHITE_100;
ctx.lineJoin = 'round';
Expand Down Expand Up @@ -156,7 +187,7 @@ const drawOccupationZone = ({
departureTimePixel,
yPosition,
isThroughTrain,
selectedTrainId,
selectedTrainId: currentSelectedTrainId,
});
};

Expand All @@ -168,6 +199,8 @@ export const drawOccupancyZones = ({
occupancyZones,
getTimePixel,
selectedTrainId,
setSelectedTrainId,
mousePosition,
}: {
ctx: CanvasRenderingContext2D;
width: number;
Expand All @@ -176,8 +209,11 @@ export const drawOccupancyZones = ({
occupancyZones: OccupancyZone[] | undefined;
getTimePixel: (time: number) => number;
selectedTrainId: string;
setSelectedTrainId: (id: string) => void;
mousePosition: { x: number; y: number };
}) => {
ctx.clearRect(0, 0, width, height);
ctx.save();

if (!tracks || !occupancyZones || occupancyZones.length === 0) return;

Expand All @@ -189,13 +225,15 @@ export const drawOccupancyZones = ({
const trackTranslate = index === 0 ? CANVAS_PADDING : TRACK_HEIGHT_CONTAINER;
ctx.translate(0, trackTranslate);

const { x: xMousePosition, y: yMousePosition } = mousePosition;

const filteredOccupancyZones = sortedOccupancyZones.filter((zone) => zone.trackId === track.id);

let primaryArrivalTimePixel = 0;
let primaryDepartureTimePixel = 0;
let lastDepartureTimePixel = primaryDepartureTimePixel;
let yPosition = OCCUPANCY_ZONE_Y_START;
let yOffset = 0;
let yOffset = Y_OFFSET_INCREMENT;
let zoneCounter = 0;
let zoneIndex = 0;

Expand Down Expand Up @@ -226,11 +264,16 @@ export const drawOccupancyZones = ({
drawOccupationZone({
ctx,
zone,
tracks,
arrivalTimePixel,
departureTimePixel,
yPosition,
isThroughTrain,
selectedTrainId,
setSelectedTrainId,
index,
xMousePosition,
yMousePosition,
});

zoneIndex++;
Expand All @@ -255,11 +298,16 @@ export const drawOccupancyZones = ({
drawOccupationZone({
ctx,
zone,
tracks,
arrivalTimePixel,
departureTimePixel,
yPosition,
isThroughTrain,
selectedTrainId,
setSelectedTrainId,
index,
xMousePosition,
yMousePosition,
});

zoneCounter++;
Expand Down Expand Up @@ -287,4 +335,5 @@ export const drawOccupancyZones = ({
zoneIndex += remainingTrainsNb;
}
});
ctx.restore();
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const drawTracks = ({
timeScale,
}: DrawTracksProps) => {
ctx.clearRect(0, 0, width, height);
ctx.save();

const minT = timeOrigin - timeScale * timePixelOffset;
const maxT = minT + timeScale * width;
Expand Down Expand Up @@ -94,4 +95,5 @@ export const drawTracks = ({
labelMarks,
});
});
ctx.restore();
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import { drawOccupancyZones } from '../helpers/drawElements/drawOccupancyZones';
const OccupancyZonesLayer = ({
useDraw,
selectedTrainId,
setSelectedTrainId,
mousePosition,
}: {
useDraw: (layer: LayerType, fn: DrawingFunction) => void;
selectedTrainId: string;
setSelectedTrainId: (id: string) => void;
mousePosition: { x: number; y: number };
}) => {
const drawingFunction = useCallback<DrawingFunction>(
(ctx, { getTimePixel, tracks, occupancyZones, trackOccupancyWidth, trackOccupancyHeight }) => {
Expand All @@ -25,9 +29,11 @@ const OccupancyZonesLayer = ({
occupancyZones,
getTimePixel,
selectedTrainId,
setSelectedTrainId,
mousePosition,
});
},
[selectedTrainId]
[mousePosition, selectedTrainId, setSelectedTrainId]
);

useDraw('paths', drawingFunction);
Expand Down
2 changes: 2 additions & 0 deletions ui-trackoccupancydiagram/src/components/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export type TrackOccupancyCanvasProps = {
useDraw: (layer: LayerType, fn: DrawingFunction) => void;
setCanvasesRoot: (root: HTMLDivElement | null) => void;
selectedTrainId: string;
setSelectedTrainId: (id: string) => void;
mousePosition: { x: number; y: number };
};

export type TrackOccupancyManchetteProps = {
Expand Down

0 comments on commit 012452c

Please sign in to comment.