Skip to content

Commit

Permalink
ui-manchette: first version of proportional manchette
Browse files Browse the repository at this point in the history
This first version is still in development, there is some questions about which operationnal point hide or not
  • Loading branch information
Math-R committed Jun 6, 2024
1 parent 040a612 commit 01d6062
Show file tree
Hide file tree
Showing 15 changed files with 8,991 additions and 15,617 deletions.
20,761 changes: 8,765 additions & 11,996 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@
"rollup": "^4.13.0",
"typescript": "^5.4.2"
}
}
}
1 change: 1 addition & 0 deletions ui-manchette/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
},
"dependencies": {
"classnames": "^2.5.1",
"lodash.isequal": "^4.5.0",
"tailwindcss": "^3.4.1"
},
"peerDependencies": {
Expand Down
19 changes: 14 additions & 5 deletions ui-manchette/src/components/Manchette.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import React from 'react';
import OperationalPointList from './OperationalPointList';
import { OperationalPointType } from '../types/pathPropertiesTypes';
import { OperationalPointType } from '../types';
import { ZoomIn, ZoomOut } from '@osrd-project/ui-icons';

interface ManchetteProps {
type ManchetteProps = {
operationalPoints: OperationalPointType[];
children?: React.ReactNode;
}
isProportionnal?: boolean;
};

const Manchette: React.FC<ManchetteProps> = ({ operationalPoints, children }) => {
const Manchette: React.FC<ManchetteProps> = ({
operationalPoints,
children,
isProportionnal = false,
}) => {
const [zoom, setZoom] = React.useState(1);

const zoomIn = () => {
Expand All @@ -22,7 +27,11 @@ const Manchette: React.FC<ManchetteProps> = ({ operationalPoints, children }) =>
return (
<div className="manchette flex">
<div className="manchette-container">
<OperationalPointList operationalPoints={operationalPoints} zoom={zoom} />
<OperationalPointList
operationalPoints={operationalPoints}
zoom={zoom}
isProportionnal={isProportionnal}
/>
<div className="manchette-actions flex items-center">
<div className=" flex items-center ">
<button className="h-full px-3 w-full" onClick={zoomOut} disabled={zoom === 1}>
Expand Down
13 changes: 5 additions & 8 deletions ui-manchette/src/components/OperationalPoint.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import React from 'react';
import { OperationalPointType } from '../types/pathPropertiesTypes';
import { OperationalPointType } from '../types';
import '@osrd-project/ui-core/dist/theme.css';
import { positionMmtoKm } from './helpers';

const OperationalPoint: React.FC<OperationalPointType> = ({ extensions, id, part, position }) => {
const positionKm = () => {
return Math.round((position / 1000000) * 10) / 10;
};
const OperationalPoint: React.FC<OperationalPointType> = ({ extensions, id, position }) => {
return (
<div className="flex op items-baseline" id={id}>
<div className="op-position justify-self-start text-end">{positionKm()}</div>
<div className="op-position justify-self-start text-end">{positionMmtoKm(position)}</div>

<div className="op-name mr-2 ml-2 justify-self-start">{extensions?.identifier?.name}</div>
<div className="op-name mx-2 justify-self-start">{extensions?.identifier?.name}</div>
<div className="op-separator"></div>
<div className="op-ch font-mono justify-self-end">{extensions?.sncf?.ch}</div>
<div className="op-separator"></div>

<div className="op-type"></div>
<div className="op-separator"></div>
{/* <div className="lines"></div> */}
</div>
);
};
Expand Down
76 changes: 61 additions & 15 deletions ui-manchette/src/components/OperationalPointList.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,83 @@
import React from 'react';
import { OperationalPointType } from '../types/pathPropertiesTypes';
import React, { useEffect } from 'react';
import { isEqual } from 'lodash';
import { OperationalPointType } from '../types';
import OperationalPoint from './OperationalPoint';
import cx from 'classnames';
import { positionMmtoKm } from './helpers';
import { BASE_KM_HEIGHT, BASE_OP_HEIGHT } from './consts';

interface OperationalPointListProps {
type OperationalPointListProps = {
operationalPoints: OperationalPointType[];
isProportionnal?: boolean;
zoom?: number;
width?: number;
}
};

const OperationalPointList: React.FC<OperationalPointListProps> = ({
operationalPoints,
isProportionnal = false,
zoom = 1,
}) => {
const baseHeight = 24;
const operationalPointHeight = (op: OperationalPointType) => {
const [operationalPointsToDisplay, setOperationalPointsToDisplay] = React.useState<
OperationalPointType[]
>([]);

const operationalPointStyle = (op: OperationalPointType, nextOp?: OperationalPointType) => {
if (isProportionnal) {
// WIP : Ratio to find
return '100%';
if (!nextOp) {
return { height: `${BASE_OP_HEIGHT}px` };
} else {
return {
height: `${positionMmtoKm(nextOp.position - op.position) * zoom * BASE_KM_HEIGHT}px`,
};
}
} else {
return baseHeight * zoom;
return { height: `${BASE_OP_HEIGHT * zoom}px` };
}
};

useEffect(() => {
const calcOperationalPointsToDisplay = () => {
if (isProportionnal) {
const result = operationalPoints.reduce(
(accumulatedPoints, nextOp) => {
const lastDisplayedPoint = accumulatedPoints[accumulatedPoints.length - 1];
const distance = positionMmtoKm(nextOp.position - lastDisplayedPoint.position);
if (distance * BASE_KM_HEIGHT * zoom >= BASE_OP_HEIGHT) {
accumulatedPoints.push(nextOp);
}
return accumulatedPoints;
},
[operationalPoints[0]]
);

const lastPoint = operationalPoints[operationalPoints.length - 1];
if (isEqual(result[result.length - 1], lastPoint)) {
result.push(lastPoint);
}

const secondLastIndex = result.length - 2;
if (secondLastIndex >= 0) {
const secondLastPoint = result[secondLastIndex];
const lastDiff = positionMmtoKm(lastPoint.position - secondLastPoint.position);
if (lastDiff * BASE_KM_HEIGHT * zoom < BASE_OP_HEIGHT) {
result.splice(secondLastIndex, 1);
}
}

return result;
} else {
return operationalPoints;
}
};
setOperationalPointsToDisplay(calcOperationalPointsToDisplay());
}, [operationalPoints, isProportionnal, zoom]);

return (
<div className="operational-point-list">
{operationalPoints.map((op, index, { length }) => (
//WIP : length will be usefull for proportionnal display
{operationalPointsToDisplay.map((op, index) => (
<div
key={index}
className={cx('operational-point-wrapper flex flex-col justify-start', {})}
style={{ height: operationalPointHeight(op) }}
className="operational-point-wrapper flex flex-col justify-start"
style={operationalPointStyle(op, operationalPointsToDisplay[index + 1])}
>
<OperationalPoint {...op} />
</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
@@ -0,0 +1,2 @@
export const BASE_OP_HEIGHT = 32;
export const BASE_KM_HEIGHT = 8;
3 changes: 3 additions & 0 deletions ui-manchette/src/components/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const positionMmtoKm = (position: number) => {
return Math.round((position / 1000000) * 10) / 10;
};
11 changes: 9 additions & 2 deletions ui-manchette/src/stories/Manchette.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import { SAMPLE_DATA } from './assets/sampleData';

import Manchette from '../components/Manchette';

const OperationalPointListData = SAMPLE_DATA.operational_points;
const OperationalPointListData = SAMPLE_DATA.operational_points ?? [];

const meta: Meta<typeof Manchette> = {
component: Manchette,
title: 'Manchette',
title: 'Manchette/Manchette',
tags: ['autodocs'],
argTypes: {
operationalPoints: {
control: {
type: 'object',
},
},
},
};

export default meta;
Expand Down
4 changes: 2 additions & 2 deletions ui-manchette/src/stories/OperationalPoint.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { SAMPLE_DATA } from './assets/sampleData';

import OperationalPoint from '../components/OperationalPoint';

const { id, extensions, part, position } = SAMPLE_DATA.operational_points[12];
const { id, extensions, part, position } = SAMPLE_DATA.operational_points?.[0] ?? {};

const meta: Meta<typeof OperationalPoint> = {
component: OperationalPoint,
title: 'OperationalPoint',
title: 'Manchette/OperationalPoint',
tags: ['autodocs'],
};

Expand Down
4 changes: 2 additions & 2 deletions ui-manchette/src/stories/OperationalPointList.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { SAMPLE_DATA } from './assets/sampleData';

import OperationalPointList from '../components/OperationalPointList';

const OperationalPointListData = SAMPLE_DATA.operational_points;
const OperationalPointListData = SAMPLE_DATA.operational_points ?? [];

const meta: Meta<typeof OperationalPointList> = {
component: OperationalPointList,
title: 'OperationalPointList',
title: 'Manchette/OperationalPointList',
tags: ['autodocs'],
};

Expand Down
Loading

0 comments on commit 01d6062

Please sign in to comment.