Now with unit conversions

This commit is contained in:
Daniel Dunn
2025-04-03 05:32:47 -06:00
parent 6b27595a89
commit 70480d0920
4 changed files with 88 additions and 15 deletions

View File

@@ -1,16 +1,29 @@
import React, { useState, useEffect } from 'react';
import { Grid, TextField } from '@mui/material';
import { Grid, TextField, Select, MenuItem } from '@mui/material';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import Autocomplete from '@mui/material/Autocomplete';
import Qty from 'js-quantities';
import { parse } from 'path';
import { b } from 'vitest/dist/suite-IbNSsUWN.js';
//
const siPrefixes: { [key: string]: number } = {
'': 1,
k: 1000,
M: 1000000,
G: 1000000000,
T: 1000000000000,
m: 0.001,
u: 0.000001,
n: 0.000000001,
p: 0.000000000001
};
export default function NumericInputWithUnit(props: {
value: { value: number; unit: string };
onOwnChange: (value: { value: number; unit: string }, ...baseProps) => void;
defaultPrefix?: string;
}) {
const [inputValue, setInputValue] = useState(props.value.value);
const [prefix, setPrefix] = useState(props.defaultPrefix || '');
const [unit, setUnit] = useState(props.value.unit);
const [unitOptions, setUnitOptions] = useState<string[]>([]);
@@ -34,7 +47,7 @@ export default function NumericInputWithUnit(props: {
setInputValue(newValue);
if (props.onOwnChange) {
try {
const qty = Qty(newValue, unit).to(val.unit);
const qty = Qty(newValue * siPrefixes[prefix], unit).to(val.unit);
props.onOwnChange({ unit: val.unit, value: qty.scalar });
} catch (error) {
console.error('Conversion error', error);
@@ -42,11 +55,31 @@ export default function NumericInputWithUnit(props: {
}
};
const handlePrefixChange = (newPrefix: string) => {
const oldPrefixValue = siPrefixes[prefix];
const newPrefixValue = siPrefixes[newPrefix];
setPrefix(newPrefix);
// Value does not change, it is just re-formatted for display
// handleValueChange({
// value: (inputValue * oldPrefixValue) / newPrefixValue,
// unit: unit
// });
};
const handleUnitChange = (newUnit: string) => {
if (!newUnit) return;
const oldInputValue = inputValue;
const oldUnit = unit;
setUnit(newUnit);
setPrefix('');
try {
const convertedValue = Qty(inputValue, unit).to(newUnit).scalar;
const convertedValue = Qty(
oldInputValue * siPrefixes[prefix],
oldUnit
).to(newUnit).scalar;
setInputValue(convertedValue);
} catch (error) {
console.error('Unit conversion error', error);
@@ -63,20 +96,46 @@ export default function NumericInputWithUnit(props: {
};
return (
<Grid container spacing={2} alignItems="center">
<Grid
container
spacing={2}
alignItems="center"
style={{ minWidth: '20rem' }}
>
<Grid item xs={6}>
<TextFieldWithDesc
{...props.baseProps}
type="number"
fullWidth
value={inputValue}
value={(inputValue / siPrefixes[prefix])
.toFixed(9)
.replace(/(\d*\.\d+?)0+$/, '$1')}
onOwnChange={(value) =>
handleValueChange({ value: parseFloat(value), unit: unit })
}
label="Value"
/>
</Grid>
<Grid item xs={6}>
<Grid item xs={3}>
<Select
fullWidth
label="Prefix"
title="Prefix"
value={prefix}
onChange={(event, newValue) => {
handlePrefixChange(newValue.props.value || '');
}}
>
{Object.keys(siPrefixes).map((key) => (
<MenuItem key={key} value={key}>
{key}
</MenuItem>
))}
</Select>
</Grid>
<Grid item xs={3}>
<Autocomplete
options={unitOptions}
value={unit}

View File

@@ -7,6 +7,8 @@ export interface GenericCalcType {
title: string;
formula: string;
unit: string;
// Si prefix default
defaultPrefix?: string;
}[];
selections?: {
title: string;
@@ -20,7 +22,7 @@ export interface GenericCalcType {
name: string;
title: string;
unit: string;
defaultPrefix?: string;
// If absence, assume it's the default target var
default?: number;
}[];

View File

@@ -59,7 +59,8 @@ const voltagedropinwire: GenericCalcType = {
name: 'p',
title: 'Resistivity',
unit: 'Ω/m3',
default: 1
default: 1,
defaultPrefix: 'n'
},
{
name: 'x',

View File

@@ -13,7 +13,6 @@ import React, { useState } from 'react';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import ToolTextResult from '@components/result/ToolTextResult';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import NumericInputWithUnit from '@components/input/NumericInputWithUnit';
import { UpdateField } from '@components/options/ToolOptions';
import { InitialValuesType } from './types';
@@ -51,7 +50,7 @@ export default async function makeTool(
}>({});
const [extraOutputs, setExtraOutputs] = useState<{
[key: string]: string;
[key: string]: number;
}>({});
const updateVarField = (
@@ -240,8 +239,8 @@ export default async function makeTool(
<TableCell>
<NumericInputWithUnit
title={variable.title}
sx={{ width: '25ch' }}
description={valsBoundToPreset[variable.name] || ''}
defaultPrefix={variable.defaultPrefix}
value={{
value:
values.outputVariable === variable.name
@@ -287,7 +286,17 @@ export default async function makeTool(
{calcData.extraOutputs?.map((extraOutput) => (
<TableRow key={extraOutput.title}>
<TableCell>{extraOutput.title}</TableCell>
<TableCell>{extraOutputs[extraOutput.title]}</TableCell>
<TableCell>
<NumericInputWithUnit
title={extraOutput.title}
disabled={true}
defaultPrefix={extraOutput.defaultPrefix}
value={{
value: extraOutputs[extraOutput.title],
unit: extraOutput.unit
}}
></NumericInputWithUnit>
</TableCell>
<TableCell>{extraOutput.unit}</TableCell>
<TableCell></TableCell>
</TableRow>
@@ -345,7 +354,9 @@ export default async function makeTool(
const result: nerdamer.Expression = expr.evaluate();
if (result) {
extraOutputs[extraOutput.title] = result.toDecimal();
extraOutputs[extraOutput.title] = parseFloat(
result.toDecimal()
);
}
}
}