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

Merged
merged 6 commits into from
Dec 16, 2024
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
2 changes: 2 additions & 0 deletions storybook/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { Preview } from '@storybook/react';

import '../styles/global.css';

const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
Expand Down
Binary file added storybook/assets/IBMPlexMono-Regular.ttf
Binary file not shown.
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,233 @@
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 OP_ID = 'story';
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 id="track-occupancy-diagram-base-story" className="bg-ambientB-10">
<SpaceTimeChartContext.Provider value={contextState}>
<div className="main-container">
<div className="bg-ambientB-5 flex flex-col justify-center main-container-header">
<KebabHorizontal />
</div>
<div className="flex">
<div className="main-container-manchette">
<TrackOccupancyManchette tracks={tracks} />
</div>
<CanvasContext.Provider value={canvasContext}>
<div className="main-container-canvas">
<TrackOccupancyCanvas
opId={OP_ID}
useDraw={useDraw}
setCanvasesRoot={setCanvasesRoot}
/>
</div>
</CanvasContext.Provider>
</div>
</div>
<CanvasContext.Provider value={spaceTicksContext}>
<div ref={setRoot} className="relative main-container-time-captions">
<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 +239,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 All @@ -35,5 +257,9 @@ export default meta;
type Story = StoryObj<typeof TrackOccupancyDiagram>;

export const TrackOccupancyDiagramStoryDefault: Story = {
args: {},
args: {
xOffset: 0,
xZoomLevel: X_ZOOM_LEVEL,
yZoomLevel: Y_ZOOM_LEVEL,
},
};
Loading
Loading