Skip to content

Commit

Permalink
feat: завершение переноса из .js в .tsx
Browse files Browse the repository at this point in the history
  • Loading branch information
VentelR committed Apr 6, 2024
1 parent a29ac29 commit 212cc6f
Show file tree
Hide file tree
Showing 2 changed files with 270 additions and 69 deletions.
207 changes: 204 additions & 3 deletions tgui/packages/tgui/interfaces/AtmosGraphMonitor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { useBackend, useLocalState } from '../backend';
import { Section, Box, Tabs, Icon, Chart } from '../components';
import { Section, Box, Tabs, Icon } from '../components';
import { Window } from '../layouts';
import { toFixed } from '../../common/math';
import { map, zipWith } from 'common/collections';
import { Component, createRef } from 'inferno';
import { pureComponentHooks } from 'common/react';

type SensorsData = {
[key: string]: {
Expand Down Expand Up @@ -120,7 +123,7 @@ const AtmosGraphPage = ({
'К)'}
</Box>
<Section fill height={5} mt={1}>
<Chart.Line
<AtmosChart
fillPositionedParent
data={getDataToSensor(s, temperatureListName)}
rangeX={[
Expand Down Expand Up @@ -149,7 +152,7 @@ const AtmosGraphPage = ({
'кПа)'}
</Box>
<Section fill height={5} mt={1}>
<Chart.Line
<AtmosChart
fillPositionedParent
data={getDataToSensor(s, pressureListName)}
rangeX={[
Expand All @@ -169,3 +172,201 @@ const AtmosGraphPage = ({
</Box>
);
};

// Ниже находится код вдохновленный компонентом CHART.JS
// К удалению, если будут приняты изменения в официальном компоненте

const normalizeData = (data, scale, rangeX, rangeY) => {
if (data.length === 0) {
return [];
}
const min = zipWith(Math.min)(...data);
const max = zipWith(Math.max)(...data);
if (rangeX !== undefined) {
min[0] = rangeX[0];
max[0] = rangeX[1];
}
if (rangeY !== undefined) {
min[1] = rangeY[0];
max[1] = rangeY[1];
}
const normalized = map((point) => {
return zipWith((value, min, max, scale) => {
return ((value - min) / (max - min)) * scale;
})(point, min, max, scale);
})(data);
return normalized;
};

const dataToPolylinePoints = (data) => {
let points = '';
for (let i = 0; i < data.length; i++) {
const point = data[i];
points += point[0] + ',' + point[1] + ' ';
}
return points;
};

type AtmosChartProps = {
data: number[][];
rangeX: number[];
rangeY: number[];
strokeColor?: string;
strokeWidth?: number;
fillColor?: string;
fillPositionedParent?: boolean;
};

type AtmosChartState = {
viewBox: [number, number];
};

class AtmosChart extends Component<AtmosChartProps, AtmosChartState> {
private ref = createRef<HTMLDivElement>();
static defaultHooks: {
onComponentShouldUpdate: (lastProps: any, nextProps: any) => boolean;
};

constructor(props) {
super(props);
this.state = {
viewBox: [400, 800],
};
}

handleResize = () => {
const element = this.ref.current;
this.setState({
viewBox: [element.offsetWidth, element.offsetHeight],
});
};

componentDidMount() {
window.addEventListener('resize', this.handleResize);
this.handleResize();
}

componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
render() {
const {
data = [],
rangeX,
rangeY,
fillColor = 'none',
strokeColor = '#ffffff',
strokeWidth = 2,
...rest
} = this.props;
const { viewBox } = this.state;
const normalized = normalizeData(data, viewBox, rangeX, rangeY);
// Push data outside viewBox and form a fillable polygon
if (normalized.length > 0) {
const first = normalized[0];
const last = normalized[normalized.length - 1];
normalized.push([viewBox[0] + strokeWidth, last[1]]);
normalized.push([viewBox[0] + strokeWidth, -strokeWidth]);
normalized.push([-strokeWidth, -strokeWidth]);
normalized.push([-strokeWidth, first[1]]);
}
const points = dataToPolylinePoints(normalized);
const horizontalLinesCount = 2; // Горизонтальные линии сетки, не имеют физического смысла
const verticalLinesCount = data.length - 2;
const gridColor = 'rgba(255, 255, 255, 0.1)';
const gridWidth = 2;
const pointTextColor = 'rgba(255, 255, 255, 0.8)';
const pointTextSize = '0.8em';
const labelViewBoxSize = 400;

return (
<Box position="relative" {...rest}>
{(props) => (
<div ref={this.ref} {...props}>
<svg
viewBox={`0 0 ${viewBox[0]} ${viewBox[1]}`}
preserveAspectRatio="none"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
overflow: 'hidden',
}}
>
{/* Горизонтальные линии сетки */}
{Array.from({ length: horizontalLinesCount }).map((_, index) => (
<line
key={`horizontal-line-${index}`}
x1={0}
y1={(index + 1) * (viewBox[1] / (horizontalLinesCount + 1))}
x2={viewBox[0]}
y2={(index + 1) * (viewBox[1] / (horizontalLinesCount + 1))}
stroke={gridColor}
strokeWidth={gridWidth}
/>
))}
{/* Вертикальные линии сетки */}
{Array.from({ length: verticalLinesCount }).map((_, index) => (
<line
key={`vertical-line-${index}`}
x1={(index + 1) * (viewBox[0] / (verticalLinesCount + 1))}
y1={0}
x2={(index + 1) * (viewBox[0] / (verticalLinesCount + 1))}
y2={viewBox[1]}
stroke={gridColor}
strokeWidth={gridWidth}
/>
))}
{/* Полилиния графика */}
<polyline
transform={`scale(1, -1) translate(0, -${viewBox[1]})`}
fill={fillColor}
stroke={strokeColor}
strokeWidth={strokeWidth}
points={points}
/>
{/* Точки */}
{data.map(
(point, index) =>
index % 2 === 1 && (
<circle
key={`point-${index}`}
cx={normalized[index][0]}
cy={viewBox[1] - normalized[index][1]}
r={2} // радиус точки
fill="#ffffff" // цвет точки
stroke={strokeColor} // цвет обводки
strokeWidth={1} // ширина обводки
/>
)
)}
{/* Значения точек */}
{data.map(
(point, index) =>
viewBox[0] > labelViewBoxSize &&
index % 2 === 1 && (
<text
key={`point-text-${index}`}
x={normalized[index][0]}
y={viewBox[1] - normalized[index][1]}
fill={pointTextColor}
fontSize={pointTextSize}
dy="1em" // Сдвиг текста вниз, чтобы он не перекрывал точку
dx="-2.5em" // Сдвиг текста влево
textAnchor="middle" // Центрирование текста по x координате
>
{point[1] !== null ? point[1].toFixed(0) : 'N/A'}
</text>
)
)}
</svg>
</div>
)}
</Box>
);
}
}

AtmosChart.defaultHooks = pureComponentHooks;
132 changes: 66 additions & 66 deletions tgui/public/tgui.bundle.js

Large diffs are not rendered by default.

0 comments on commit 212cc6f

Please sign in to comment.