nitro-v36-with-renderer-v36
/
src /
components /
wired /
views /
selectors /
WiredSelectorFurniAltitudeView.tsx
WiredSelectorFurniAltitudeView.tsx 4.4 Ko
Télécharger le fichier
import { FC, useCallback, useEffect, useState } from 'react';
import { LocalizeText, WiredFurniType } from '../../../../api';
import { Slider, Text } from '../../../../common';
import { useWired } from '../../../../hooks';
import { WiredSelectorBaseView } from './WiredSelectorBaseView';
import { Radio } from '../../../../common/Radio';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
const MIN_ALTITUDE = 0;
const MAX_ALTITUDE = 80;
const ALTITUDE_STEP = 0.01;
const ALTITUDE_PATTERN = /^\d*(\.\d{0,2})?$/;
const clampAltitude = (value: number) => {
if (isNaN(value)) return MIN_ALTITUDE;
const clamped = Math.min(MAX_ALTITUDE, Math.max(MIN_ALTITUDE, value));
return parseFloat(clamped.toFixed(2));
};
const formatAltitude = (value: number) => {
const normalized = clampAltitude(value);
const text = normalized.toFixed(2);
return text.replace(/\.00$/, '').replace(/(\.\d)0$/, '$1');
};
export const WiredSelectorFurniAltitudeView: FC<{}> = () => {
const { trigger = null, setIntParams = null, setStringParam = null } = useWired();
const [comparison, setComparison] = useState(1);
const [altitude, setAltitude] = useState(0);
const [altitudeInput, setAltitudeInput] = useState('0');
useEffect(() => {
if (!trigger) return;
// Official layout: [altitude in hundredths, comparison].
const nextAltitude = clampAltitude((trigger.intData[0] ?? 0) / 100);
setComparison(trigger.intData.length > 1 ? trigger.intData[1] : 1);
setAltitude(nextAltitude);
setAltitudeInput(formatAltitude(nextAltitude));
}, [trigger]);
const updateAltitude = (value: number) => {
const nextValue = clampAltitude(value);
setAltitude(nextValue);
setAltitudeInput(formatAltitude(nextValue));
};
const updateAltitudeInput = (value: string) => {
if (!ALTITUDE_PATTERN.test(value)) return;
setAltitudeInput(value);
if (!value.length) {
setAltitude(0);
return;
}
const parsedValue = parseFloat(value);
if (isNaN(parsedValue)) return;
if (parsedValue > MAX_ALTITUDE) {
updateAltitude(MAX_ALTITUDE);
return;
}
setAltitude(clampAltitude(parsedValue));
};
const save = useCallback(() => {
setIntParams([Math.round(altitude * 100), comparison]);
setStringParam('');
}, [altitude, comparison, setIntParams, setStringParam]);
return (
<NativeTextTree>
<WiredSelectorBaseView
hasSpecialInput={true}
requiresFurni={WiredFurniType.STUFF_SELECTION_OPTION_NONE}
save={save}
hideDelay={true}
cardStyle={{ width: 400 }}
>
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-1">
{[0, 1, 2].map((value) => {
return (
<label key={value} className="flex items-center gap-1">
<Radio checked={comparison === value} name="furniAltitudeComparison" onCheckedChange={() => setComparison(value)} />
<Text>{LocalizeText(`wiredfurni.params.comparison.${value}`)}</Text>
</label>
);
})}
</div>
<div className="flex flex-col gap-1">
<Text bold>{LocalizeText('wiredfurni.params.setaltitude')}</Text>
<input
className="form-control form-control-sm"
inputMode="decimal"
type="text"
value={altitudeInput}
onBlur={() => setAltitudeInput(formatAltitude(altitude))}
onChange={(event) => updateAltitudeInput(event.target.value)}
/>
</div>
<div className="flex flex-col gap-1">
<Slider max={MAX_ALTITUDE} min={MIN_ALTITUDE} step={ALTITUDE_STEP} value={altitude} onChange={(event) => updateAltitude(event as number)} />
<Text small>{formatAltitude(altitude)}</Text>
</div>
</div>
</WiredSelectorBaseView>
</NativeTextTree>
);
};
