calculator-helpers.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. const DEFAULT_MAGNITUDE_STEPS = [
  2. { min: 1000, decimals: 2 },
  3. { min: 1, decimals: 4 },
  4. { min: 0.001, decimals: 6 },
  5. { min: 0, decimals: 9 }
  6. ]
  7. function getOption(options, index) {
  8. return options[Number(index)] || options[0]
  9. }
  10. function normalizeIndex(index, options, fallback = 0) {
  11. const numericIndex = Number(index)
  12. if (!Number.isInteger(numericIndex) || numericIndex < 0 || numericIndex >= options.length) return fallback
  13. return numericIndex
  14. }
  15. function parseLooseNumber(value, allowNegative = true) {
  16. const text = String(value === undefined || value === null ? '' : value).trim().replace(/,/g, '.')
  17. if (!text) return null
  18. const numberValue = Number(text)
  19. if (!Number.isFinite(numberValue)) return NaN
  20. if (!allowNegative && numberValue < 0) return NaN
  21. return numberValue
  22. }
  23. function parsePositiveNumber(value) {
  24. const numberValue = parseLooseNumber(value, true)
  25. if (numberValue === null) return null
  26. return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : NaN
  27. }
  28. function trimFixed(text) {
  29. return text.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '')
  30. }
  31. function formatMagnitudeNumber(value, options = {}) {
  32. const {
  33. fallbackText = '--',
  34. steps = DEFAULT_MAGNITUDE_STEPS,
  35. zeroText = '0'
  36. } = options
  37. const numberValue = Number(value)
  38. if (!Number.isFinite(numberValue)) return fallbackText
  39. if (numberValue === 0) return zeroText
  40. const absValue = Math.abs(numberValue)
  41. const selectedStep = (steps || DEFAULT_MAGNITUDE_STEPS).find((step) => absValue >= step.min)
  42. || (steps || DEFAULT_MAGNITUDE_STEPS)[(steps || DEFAULT_MAGNITUDE_STEPS).length - 1]
  43. let text = trimFixed(numberValue.toFixed(selectedStep.decimals))
  44. if (text === '0' && absValue > 0) {
  45. text = trimFixed(numberValue.toFixed(12))
  46. }
  47. return text
  48. }
  49. function selectBestUnit(units, baseValue, fallbackIndex = 0) {
  50. const absValue = Math.abs(Number(baseValue))
  51. if (!Number.isFinite(absValue) || absValue <= 0) {
  52. return {
  53. index: fallbackIndex,
  54. unit: getOption(units, fallbackIndex)
  55. }
  56. }
  57. let selectedIndex = 0
  58. for (let index = 0; index < units.length; index += 1) {
  59. if (absValue / units[index].factor >= 1) selectedIndex = index
  60. }
  61. return {
  62. index: selectedIndex,
  63. unit: getOption(units, selectedIndex)
  64. }
  65. }
  66. function formatScaledValue(baseValue, unit, options = {}) {
  67. const {
  68. includeUnit = false
  69. } = options
  70. const selectedUnit = unit || { factor: 1, label: '' }
  71. const valueText = formatMagnitudeNumber(baseValue / selectedUnit.factor, options)
  72. return includeUnit && selectedUnit.label ? `${valueText} ${selectedUnit.label}` : valueText
  73. }
  74. module.exports = {
  75. DEFAULT_MAGNITUDE_STEPS,
  76. formatMagnitudeNumber,
  77. formatScaledValue,
  78. getOption,
  79. normalizeIndex,
  80. parseLooseNumber,
  81. parsePositiveNumber,
  82. selectBestUnit,
  83. trimFixed
  84. }