mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 02:48:38 -04:00
Initial alpha version of the Ratio Calculator.
This commit is contained in:
parent
f321e9f6f3
commit
fb187e1305
8 changed files with 474 additions and 32 deletions
156
app/package-lock.json
generated
156
app/package-lock.json
generated
|
|
@ -14,6 +14,7 @@
|
|||
"@testing-library/user-event": "^13.5.0",
|
||||
"cypress": "^13.15.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mathjs": "^14.0.1",
|
||||
"query-string": "^8.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
|
|
@ -1814,16 +1815,21 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.20.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
|
||||
"integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
|
|
@ -5930,6 +5936,18 @@
|
|||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="
|
||||
},
|
||||
"node_modules/complex.js": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz",
|
||||
"integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/compressible": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||
|
|
@ -7416,6 +7434,11 @@
|
|||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/escape-latex": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
|
||||
"integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw=="
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
|
|
@ -10346,6 +10369,11 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/javascript-natural-sort": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw=="
|
||||
},
|
||||
"node_modules/jest": {
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
||||
|
|
@ -12813,6 +12841,40 @@
|
|||
"tmpl": "1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/mathjs": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.0.1.tgz",
|
||||
"integrity": "sha512-yyJgLwC6UXuve724np8tHRMYaTtb5UqiOGQkjwbSXgH8y1C/LcJ0pvdNDZLI2LT7r+iExh2Y5HwfAY+oZFtGIQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.7",
|
||||
"complex.js": "^2.2.5",
|
||||
"decimal.js": "^10.4.3",
|
||||
"escape-latex": "^1.2.0",
|
||||
"fraction.js": "^5.2.1",
|
||||
"javascript-natural-sort": "^0.7.1",
|
||||
"seedrandom": "^3.0.5",
|
||||
"tiny-emitter": "^2.1.0",
|
||||
"typed-function": "^4.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"mathjs": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/mathjs/node_modules/fraction.js": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.2.1.tgz",
|
||||
"integrity": "sha512-Ah6t/7YCYjrPUFUFsOsRLMXAdnYM+aQwmojD2Ayb/Ezr82SwES0vuyQ8qZ3QO8n9j7W14VJuVZZet8U3bhSdQQ==",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
|
||||
|
|
@ -16147,6 +16209,11 @@
|
|||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/seedrandom": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
|
||||
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
|
||||
},
|
||||
"node_modules/select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
|
|
@ -17273,6 +17340,11 @@
|
|||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
|
||||
},
|
||||
"node_modules/tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||
},
|
||||
"node_modules/tiny-inflate": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||
|
|
@ -17505,6 +17577,14 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/typed-function": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz",
|
||||
"integrity": "sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
|
|
@ -19927,11 +20007,18 @@
|
|||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.20.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
|
||||
"integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
|
|
@ -22873,6 +22960,11 @@
|
|||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="
|
||||
},
|
||||
"complex.js": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz",
|
||||
"integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g=="
|
||||
},
|
||||
"compressible": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||
|
|
@ -23955,6 +24047,11 @@
|
|||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"escape-latex": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
|
||||
"integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw=="
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
|
|
@ -26024,6 +26121,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"javascript-natural-sort": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw=="
|
||||
},
|
||||
"jest": {
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
|
||||
|
|
@ -27840,6 +27942,29 @@
|
|||
"tmpl": "1.0.5"
|
||||
}
|
||||
},
|
||||
"mathjs": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.0.1.tgz",
|
||||
"integrity": "sha512-yyJgLwC6UXuve724np8tHRMYaTtb5UqiOGQkjwbSXgH8y1C/LcJ0pvdNDZLI2LT7r+iExh2Y5HwfAY+oZFtGIQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.25.7",
|
||||
"complex.js": "^2.2.5",
|
||||
"decimal.js": "^10.4.3",
|
||||
"escape-latex": "^1.2.0",
|
||||
"fraction.js": "^5.2.1",
|
||||
"javascript-natural-sort": "^0.7.1",
|
||||
"seedrandom": "^3.0.5",
|
||||
"tiny-emitter": "^2.1.0",
|
||||
"typed-function": "^4.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"fraction.js": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.2.1.tgz",
|
||||
"integrity": "sha512-Ah6t/7YCYjrPUFUFsOsRLMXAdnYM+aQwmojD2Ayb/Ezr82SwES0vuyQ8qZ3QO8n9j7W14VJuVZZet8U3bhSdQQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"mdn-data": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
|
||||
|
|
@ -30019,6 +30144,11 @@
|
|||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"seedrandom": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
|
||||
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
|
||||
},
|
||||
"select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
|
|
@ -30898,6 +31028,11 @@
|
|||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
|
||||
},
|
||||
"tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||
},
|
||||
"tiny-inflate": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||
|
|
@ -31076,6 +31211,11 @@
|
|||
"is-typed-array": "^1.1.9"
|
||||
}
|
||||
},
|
||||
"typed-function": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz",
|
||||
"integrity": "sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA=="
|
||||
},
|
||||
"typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
"@testing-library/user-event": "^13.5.0",
|
||||
"cypress": "^13.15.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mathjs": "^14.0.1",
|
||||
"query-string": "^8.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {TemperatureCalculator} from "./calculators/TemperatureCalculator";
|
|||
import {ElectricalCalculators} from "./calculators/ElectricalCalculators";
|
||||
import {DipoleAntennaCalculator} from "./calculators/HamCalculators";
|
||||
import {Link} from "react-router-dom";
|
||||
import {RatioCalculators} from "./calculators/RatioCalculator";
|
||||
|
||||
export function CalculatorsPage() {
|
||||
const {calc, setCalc} = useCalcQuery();
|
||||
|
|
@ -14,6 +15,7 @@ export function CalculatorsPage() {
|
|||
{key: 'electrical', icon: 'lightning', button: 'Electrical', contents: <ElectricalCalculators/>},
|
||||
{key: 'antenna', icon: 'signal', button: 'Antenna', contents: <DipoleAntennaCalculator/>},
|
||||
{key: 'temperature', icon: 'thermometer', button: 'Temperature', contents: <TemperatureCalculator/>},
|
||||
{key: ' ratio', icon: 'th large', button: 'Ratio', contents: <RatioCalculators/>},
|
||||
];
|
||||
|
||||
const activeCalculator = calc ?
|
||||
|
|
|
|||
|
|
@ -512,11 +512,11 @@ export function SearchResultsInput({
|
|||
actionIcon,
|
||||
clearable = false,
|
||||
clearDisabled = null,
|
||||
autoFocus = false,
|
||||
results = undefined,
|
||||
handleResultSelect = null,
|
||||
resultRenderer = undefined,
|
||||
loading = false,
|
||||
inputRef = null,
|
||||
...props
|
||||
}) {
|
||||
// A Semantic <Search> input with a Clear button.
|
||||
|
|
@ -544,7 +544,7 @@ export function SearchResultsInput({
|
|||
|
||||
return <Form onSubmit={localOnSubmit} {...props} className='search-container'>
|
||||
<Search category
|
||||
input={{fluid: true, icon: icon}}
|
||||
input={{fluid: true, icon: icon, ref: inputRef}}
|
||||
placeholder={placeholder}
|
||||
type='text'
|
||||
onSearchChange={handleChange}
|
||||
|
|
|
|||
|
|
@ -97,8 +97,9 @@ export function NavBar() {
|
|||
|
||||
// Red/Yellow colors will blend in to some navbar colors, swap the colors for high contrast.
|
||||
const conflictingColors = ['red', 'orange', 'yellow', 'olive', 'pink'];
|
||||
const lowWarningColor = conflictingColors.includes(navColor) ? 'white' : 'yellow';
|
||||
const highWarningColor = conflictingColors.includes(navColor) ? 'black' : 'red';
|
||||
const [lowWarningColor, highWarningColor] = conflictingColors.includes(navColor)
|
||||
? [null, 'black']
|
||||
: ['yellow', 'red'];
|
||||
|
||||
// Generic system load, the least important warning.
|
||||
const {minute_1, mediumLoad, highLoad} = useLoad();
|
||||
|
|
@ -172,7 +173,6 @@ export function NavBar() {
|
|||
processingLink = '/map/manage';
|
||||
}
|
||||
}
|
||||
|
||||
const processingIcon = processingLink &&
|
||||
<Link to={processingLink}>
|
||||
<Icon loading name='circle notch' size='large'/>
|
||||
|
|
|
|||
|
|
@ -400,23 +400,37 @@ export function SearchIconButton() {
|
|||
prevOpen.current = open;
|
||||
}, [open]);
|
||||
|
||||
const inputRef = React.useRef();
|
||||
React.useEffect(() => {
|
||||
// Focus on the Search's <input/> when the modal is opened.
|
||||
if (open) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
let modalContents;
|
||||
if (open) {
|
||||
modalContents = <SearchResultsInput clearable
|
||||
searchStr={searchStr}
|
||||
onChange={setSearchStr}
|
||||
onSubmit={setSearchStr}
|
||||
size='large'
|
||||
placeholder='Search everywhere...'
|
||||
results={suggestionsResults}
|
||||
handleResultSelect={localHandleResultSelect}
|
||||
resultRenderer={resultRenderer}
|
||||
loading={loading}
|
||||
inputRef={inputRef}
|
||||
/>;
|
||||
}
|
||||
|
||||
return <React.Fragment>
|
||||
<a className='item' style={{paddingRight: '0.7em'}} onClick={() => setOpen(true)}>
|
||||
<Icon name='search'/>
|
||||
</a>
|
||||
<Modal open={open} onClose={() => setOpen(false)} centered={false}>
|
||||
<ModalContent>
|
||||
<SearchResultsInput clearable
|
||||
searchStr={searchStr}
|
||||
onChange={setSearchStr}
|
||||
onSubmit={setSearchStr}
|
||||
size='large'
|
||||
placeholder='Search everywhere...'
|
||||
results={suggestionsResults}
|
||||
handleResultSelect={localHandleResultSelect}
|
||||
resultRenderer={resultRenderer}
|
||||
loading={loading}
|
||||
/>
|
||||
{modalContents}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
|
|
|
|||
|
|
@ -64,14 +64,10 @@ function ohmsLawReducer(state, action) {
|
|||
let {volts, amps, ohms, watts, lastUpdated} = state;
|
||||
|
||||
// Only replace last updated when a different input was updated.
|
||||
let newLastUpdated = lastUpdated;
|
||||
if (lastUpdated.length === 0) {
|
||||
newLastUpdated = [type];
|
||||
} else if (lastUpdated.length === 1 && lastUpdated[0] !== type) {
|
||||
newLastUpdated = [...lastUpdated, type];
|
||||
} else if (lastUpdated.length > 1 && lastUpdated[1] !== type) {
|
||||
newLastUpdated = [lastUpdated[1], type];
|
||||
}
|
||||
let newLastUpdated = lastUpdated || [];
|
||||
newLastUpdated = newLastUpdated.filter(i => i !== type);
|
||||
newLastUpdated = [type, ...newLastUpdated];
|
||||
newLastUpdated = newLastUpdated.slice(0, 2);
|
||||
|
||||
// Copy new value into it's respective state. Do not allow negative numbers.
|
||||
if (type === 'amps') {
|
||||
|
|
@ -125,6 +121,7 @@ export function OhmsLawCalculator({setVolts}) {
|
|||
type: 'number',
|
||||
labelPosition: 'right',
|
||||
onSelect: e => e.target.select(), // Select the contents of the Input when a user selects it.
|
||||
autocomplete: 'off',
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
@ -282,6 +279,7 @@ const PowerLossCalculator = ({volts, setVolts}) => {
|
|||
type='number'
|
||||
value={volts}
|
||||
onChange={(e, {value}) => setVolts(parseInt(value))}
|
||||
autocomplete="off"
|
||||
/>;
|
||||
|
||||
const saeToggle = <Toggle
|
||||
|
|
@ -299,6 +297,7 @@ const PowerLossCalculator = ({volts, setVolts}) => {
|
|||
type='number'
|
||||
value={length}
|
||||
onChange={(e, {value}) => setLength(parseInt(value))}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Grid.Column>
|
||||
<Grid.Column mobile={4} tablet={2}>
|
||||
|
|
|
|||
286
app/src/components/calculators/RatioCalculator.js
Normal file
286
app/src/components/calculators/RatioCalculator.js
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
import {Container, Dropdown} from "semantic-ui-react";
|
||||
import {Form, Header} from "../Theme";
|
||||
import Grid from "semantic-ui-react/dist/commonjs/collections/Grid";
|
||||
import {ThemeContext} from "../../contexts/contexts";
|
||||
import React from "react";
|
||||
import {roundDigits} from "../Common";
|
||||
import {createUnit, multiply, unit} from "mathjs";
|
||||
|
||||
const nullUnit = createUnit('null');
|
||||
|
||||
function ratioReducer(prevState, e) {
|
||||
const {value, name} = Object.keys(e).indexOf('target') >= 0 ? e.target : e;
|
||||
let {a, aUnit, b, bUnit, c, cUnit, d, dUnit, base, lastUpdated} = prevState;
|
||||
console.log('ratioReducer', name, value, lastUpdated);
|
||||
|
||||
a = unit(a, aUnit);
|
||||
b = unit(b, bUnit);
|
||||
c = unit(c, cUnit);
|
||||
d = unit(d, dUnit);
|
||||
|
||||
// Keep a stack of which inputs are updated. Limit it to the 3 most recent inputs.
|
||||
let newLastUpdated = lastUpdated || [];
|
||||
if (['a', 'b', 'c', 'd'].includes(name)) {
|
||||
newLastUpdated = newLastUpdated.filter(i => i !== name);
|
||||
newLastUpdated = [name, ...newLastUpdated];
|
||||
newLastUpdated = newLastUpdated.slice(0, 3);
|
||||
}
|
||||
|
||||
console.log('a', a.toString(), 'b', b.toString(), 'c', c.toString(), 'd', d.toString());
|
||||
|
||||
const calculateA = () => a = multiply(b, c).divide(d).to(aUnit);
|
||||
const calculateB = () => b = multiply(a, d).divide(c).to(bUnit);
|
||||
const calculateC = () => c = multiply(a, d).divide(b).to(cUnit);
|
||||
const calculateD = () => d = multiply(b, c).divide(a).to(dUnit);
|
||||
|
||||
if (name === 'aUnit') {
|
||||
aUnit = value;
|
||||
a = a.to(value);
|
||||
} else if (name === 'bUnit') {
|
||||
bUnit = value;
|
||||
b = b.to(value);
|
||||
} else if (name === 'cUnit') {
|
||||
cUnit = value;
|
||||
c = c.to(value);
|
||||
} else if (name === 'dUnit') {
|
||||
dUnit = value;
|
||||
d = d.to(value);
|
||||
} else if (name === 'a') {
|
||||
a = unit(value, aUnit);
|
||||
} else if (name === 'b') {
|
||||
b = unit(value, bUnit);
|
||||
} else if (name === 'c') {
|
||||
c = unit(value, cUnit);
|
||||
} else if (name === 'd') {
|
||||
d = unit(value, dUnit);
|
||||
} else if (name === 'base') {
|
||||
// Base is changing, reset units and values.
|
||||
base = value;
|
||||
aUnit = bUnit = cUnit = dUnit = baseToUnitsMap[base][0]['key'];
|
||||
a = b = c = d = unit(1, aUnit);
|
||||
// Reset last updated.
|
||||
newLastUpdated = [];
|
||||
}
|
||||
|
||||
// Calculate ratio of the input which hasn't been updated recently. But only if all other inputs have values.
|
||||
if (lastUpdated.length === 3) {
|
||||
if (!lastUpdated.includes('a')) {
|
||||
calculateA();
|
||||
} else if (!lastUpdated.includes('b')) {
|
||||
calculateB();
|
||||
} else if (!lastUpdated.includes('c')) {
|
||||
calculateC();
|
||||
} else if (!lastUpdated.includes('d')) {
|
||||
calculateD();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
base,
|
||||
lastUpdated: newLastUpdated,
|
||||
a: roundDigits(a.toNumber(), 3),
|
||||
b: roundDigits(b.toNumber(), 3),
|
||||
c: roundDigits(c.toNumber(), 3),
|
||||
d: roundDigits(d.toNumber(), 3),
|
||||
aUnit,
|
||||
bUnit,
|
||||
cUnit,
|
||||
dUnit,
|
||||
}
|
||||
}
|
||||
|
||||
const baseToUnitsMap = {
|
||||
'length': [
|
||||
{key: 'centimeter', value: 'centimeter', text: 'centimeter'},
|
||||
{key: 'feet', value: 'feet', text: 'feet'},
|
||||
{key: 'inch', value: 'inch', text: 'inch'},
|
||||
{key: 'kilometer', value: 'kilometer', text: 'kilometer'},
|
||||
{key: 'meter', value: 'meter', text: 'meter'},
|
||||
{key: 'mile', value: 'mile', text: 'mile'},
|
||||
],
|
||||
'area': [
|
||||
{key: 'm2', value: 'm2', text: 'm²'},
|
||||
{key: 'sqin', value: 'sqin', text: 'square inch'},
|
||||
{key: 'sqyd', value: 'sqyd', text: 'square yard'},
|
||||
{key: 'sqmi', value: 'sqmi', text: 'square mile'},
|
||||
{key: 'acre', value: 'acre', text: 'acre'},
|
||||
{key: 'hectare', value: 'hectare', text: 'hectare'},
|
||||
],
|
||||
'volume': [
|
||||
{key: 'cc', value: 'cc', text: 'cc'},
|
||||
{key: 'cuin', value: 'cuin', text: 'cuin'},
|
||||
{key: 'cup', value: 'cup', text: 'cup'},
|
||||
{key: 'fluidounce', value: 'fluidounce', text: 'fluidounce'},
|
||||
{key: 'gallon', value: 'gallon', text: 'gallon'},
|
||||
{key: 'liter', value: 'liter', text: 'liter'},
|
||||
{key: 'm3', value: 'm3', text: 'meters³'},
|
||||
{key: 'milliliter', value: 'milliliter', text: 'ml'},
|
||||
{key: 'tablespoon', value: 'tablespoon', text: 'tablespoon'},
|
||||
{key: 'teaspoon', value: 'teaspoon', text: 'teaspoon'},
|
||||
],
|
||||
'mass': [
|
||||
{key: 'grain', value: 'grain', text: 'grain'},
|
||||
{key: 'gram', value: 'gram', text: 'gram'},
|
||||
{key: 'lbs', value: 'lbs', text: 'pound'},
|
||||
{key: 'stone', value: 'stone', text: 'stone'},
|
||||
{key: 'ton', value: 'ton', text: 'ton'},
|
||||
],
|
||||
'energy': [
|
||||
{key: 'joule', value: 'joule', text: 'joule'},
|
||||
{key: 'Wh', value: 'Wh', text: 'Wh'},
|
||||
{key: 'BTU', value: 'BTU', text: 'BTU'},
|
||||
],
|
||||
'power': [
|
||||
{key: 'watt', value: 'watt', text: 'watt'},
|
||||
{key: 'hp', value: 'hp', text: 'hp'},
|
||||
],
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
base: null,
|
||||
lastUpdated: [],
|
||||
a: '',
|
||||
b: '',
|
||||
c: '',
|
||||
d: '',
|
||||
aUnit: nullUnit,
|
||||
bUnit: nullUnit,
|
||||
cUnit: nullUnit,
|
||||
dUnit: nullUnit,
|
||||
};
|
||||
|
||||
const RatioCalculator = () => {
|
||||
const {t} = React.useContext(ThemeContext);
|
||||
|
||||
const [state, dispatch] = React.useReducer(ratioReducer, initialState);
|
||||
|
||||
const unitOptions = baseToUnitsMap[state.base];
|
||||
|
||||
const baseOptions = [
|
||||
{key: null, value: null, text: 'None'},
|
||||
{key: 'length', value: 'length', text: 'meter, feet, inch, etc.', label: 'Length'},
|
||||
{key: 'area', value: 'area', text: 'inch², m², etc.', label: 'Area'},
|
||||
{key: 'volume', value: 'volume', text: 'liter, m³, cup, etc.', label: 'Volume'},
|
||||
{key: 'mass', value: 'mass', text: 'gram, pound, etc.', label: 'Mass'},
|
||||
{key: 'energy', value: 'energy', text: 'joule, Wh, BTU', label: 'Energy'},
|
||||
{key: 'power', value: 'power', text: 'watt, horse power', label: 'Power'},
|
||||
];
|
||||
|
||||
const unitDropdownOptions = {
|
||||
options: unitOptions,
|
||||
className: 'label',
|
||||
onChange: (e, data) => dispatch(data),
|
||||
};
|
||||
|
||||
const aColor = state.lastUpdated.indexOf('a') >= 0 ? '' : 'grey';
|
||||
const bColor = state.lastUpdated.indexOf('b') >= 0 ? '' : 'grey';
|
||||
const cColor = state.lastUpdated.indexOf('c') >= 0 ? '' : 'grey';
|
||||
const dColor = state.lastUpdated.indexOf('d') >= 0 ? '' : 'grey';
|
||||
|
||||
return <Form>
|
||||
<Header as='h1'>Ratio</Header>
|
||||
|
||||
<Grid columns={3}>
|
||||
<Grid.Row columns={1}>
|
||||
<Grid.Column>
|
||||
<Dropdown selection
|
||||
fluid
|
||||
placeholder='base'
|
||||
options={baseOptions}
|
||||
value={state.unit}
|
||||
name='base'
|
||||
onChange={(e, data) => dispatch(data)}
|
||||
/>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row columns={1}>
|
||||
<Grid.Column>
|
||||
<Header as='h2'>A : B = C : D</Header>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={8}>
|
||||
<div className="ui fluid labeled input">
|
||||
<div className={`ui label ${aColor}`}>A</div>
|
||||
<input
|
||||
id="a"
|
||||
name="a"
|
||||
type="number"
|
||||
value={state.a}
|
||||
onChange={e => dispatch(e)}
|
||||
onFocus={e => e.target.select()}
|
||||
/>
|
||||
{state.base &&
|
||||
<Dropdown {...unitDropdownOptions} name='aUnit' value={state.aUnit}/>}
|
||||
</div>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={1}/>
|
||||
<Grid.Column width={7}>
|
||||
<div className="ui fluid labeled input">
|
||||
<div className={`ui label ${cColor}`}>C</div>
|
||||
<input
|
||||
id="c"
|
||||
name="c"
|
||||
type="number"
|
||||
value={state.c}
|
||||
onChange={e => dispatch(e)}
|
||||
onFocus={e => e.target.select()}
|
||||
/>
|
||||
{state.base &&
|
||||
<Dropdown {...unitDropdownOptions} name='cUnit' value={state.cUnit}/>}
|
||||
</div>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={8}>
|
||||
<hr/>
|
||||
</Grid.Column>
|
||||
<Grid.Column textAlign='center' width={1}>
|
||||
<Header as='h1'>=</Header>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={7}>
|
||||
<hr/>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={8}>
|
||||
<div className="ui fluid labeled input">
|
||||
<div className={`ui label ${bColor}`}>B</div>
|
||||
<input
|
||||
id="b"
|
||||
name="b"
|
||||
type="number"
|
||||
value={state.b}
|
||||
onChange={e => dispatch(e)}
|
||||
onFocus={e => e.target.select()}
|
||||
/>
|
||||
{state.base &&
|
||||
<Dropdown {...unitDropdownOptions} name='bUnit' value={state.bUnit}/>}
|
||||
</div>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={1}/>
|
||||
<Grid.Column width={7}>
|
||||
<div className="ui fluid labeled input">
|
||||
<div className={`ui label ${dColor}`}>D</div>
|
||||
<input
|
||||
id="d"
|
||||
name="d"
|
||||
type="number"
|
||||
value={state.d}
|
||||
onChange={e => dispatch(e)}
|
||||
onFocus={e => e.target.select()}
|
||||
/>
|
||||
{state.base &&
|
||||
<Dropdown {...unitDropdownOptions} name='dUnit' value={state.dUnit}/>}
|
||||
</div>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Form>
|
||||
}
|
||||
|
||||
export const RatioCalculators = () => {
|
||||
return <Container>
|
||||
<RatioCalculator/>
|
||||
</Container>
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue