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: select zone on click #792

Merged
merged 1 commit into from
Jan 8, 2025
Merged
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
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
Loading