running-tools

A collection of tools for runners and their coaches
git clone https://git.ashermorgan.net/running-tools/
Log | Files | Refs | README

commit 77a65ef6ecf0e5683914bd467e4aa99b83c2e656
parent 1828052daf6a5ed1eb59f78af31dd120f0232755
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Sun, 22 Jun 2025 13:38:41 -0700

Convert remaining utils to TypeScript

Diffstat:
Msrc/components/DoubleOutputTable.vue | 18+++++++++++-------
Msrc/components/PaceInput.vue | 18+++++++-----------
Msrc/components/RaceOptions.vue | 7+------
Msrc/components/SingleOutputTable.vue | 12++++++++----
Msrc/components/SplitOutputTable.vue | 6+++---
Msrc/components/TargetEditor.vue | 6+++---
Dsrc/utils/calculators.js | 178-------------------------------------------------------------------------------
Asrc/utils/calculators.ts | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/utils/targets.ts | 131++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/utils/units.ts | 178+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/views/UnitCalculator.vue | 12++++++------
11 files changed, 435 insertions(+), 365 deletions(-)

diff --git a/src/components/DoubleOutputTable.vue b/src/components/DoubleOutputTable.vue @@ -30,15 +30,19 @@ import { computed } from 'vue'; import type { PropType } from 'vue'; +import { ResultType } from '@/utils/calculators'; +import type { TargetResult } from '@/utils/calculators'; import { formatDuration, formatNumber } from '@/utils/format'; -import { DISTANCE_UNITS, DISTANCE_UNIT_KEYS } from '@/utils/units'; +import type { Target } from '@/utils/targets'; +import { DistanceUnitData } from '@/utils/units'; +import type { Distance, DistanceTime } from '@/utils/units'; const props = defineProps({ /** * The method that generates the target table rows */ calculateResult: { - type: Function, + type: Function as PropType<(x: DistanceTime, y: Target) => TargetResult>, required: true, }, @@ -46,7 +50,7 @@ const props = defineProps({ * The target set */ targets: { - type: Array, + type: Array<Target>, default: () => [], }, @@ -62,7 +66,7 @@ const props = defineProps({ * The input distance */ inputDistance: { - type: Object as PropType<{ distanceValue: number, distanceUnit: DISTANCE_UNIT_KEYS }>, + type: Object as PropType<Distance>, default: () => ({ distanceValue: 5, distanceUnit: 'kilometers', @@ -75,9 +79,9 @@ const props = defineProps({ */ const results = computed(() => { // Calculate results - const results = [[ + const results: Array<Array<string>> = [[ formatNumber(props.inputDistance.distanceValue, 0, 2, false) + ' ' - + DISTANCE_UNITS[props.inputDistance.distanceUnit].symbol + + DistanceUnitData[props.inputDistance.distanceUnit].symbol ]]; props.inputTimes.forEach((input, y) => { @@ -87,7 +91,7 @@ const results = computed(() => { const result = props.calculateResult({ ...props.inputDistance, time: input }, target); if (y === 0) { - results[0].push(result[result.result === 'key' ? 'value' : 'key']); + results[0].push(result[result.result === ResultType.Key ? 'value' : 'key']); } row.push(result[result.result]); diff --git a/src/components/PaceInput.vue b/src/components/PaceInput.vue @@ -5,8 +5,8 @@ <decimal-input v-model="model.distanceValue" :aria-label="label + ' distance value'" :min="0" :digits="2"/> <select v-model="model.distanceUnit" :aria-label="label + ' distance unit'"> - <option v-for="key in DISTANCE_UNIT_KEYS" :key="key" :value="key"> - {{ DISTANCE_UNITS[key].name }} + <option v-for="key in DistanceUnits" :key="key" :value="key"> + {{ DistanceUnitData[key].name }} </option> </select> </div> @@ -20,18 +20,13 @@ <script setup lang="ts"> import type { PropType } from 'vue'; -import { DISTANCE_UNITS, DISTANCE_UNIT_KEYS } from '@/utils/units'; +import { DistanceUnits, DistanceUnitData } from '@/utils/units'; +import type { DistanceTime } from '@/utils/units'; import DecimalInput from '@/components/DecimalInput.vue'; import TimeInput from '@/components/TimeInput.vue'; import useObjectModel from '@/composables/useObjectModel'; -interface Pace { - distanceValue: number, - distanceUnit: DISTANCE_UNIT_KEYS, - time: number, -}; - const props = defineProps({ /** * The prefix for each field's aria-label @@ -45,7 +40,7 @@ const props = defineProps({ * The component value */ modelValue: { - type: Object as PropType<Pace>, + type: Object as PropType<DistanceTime>, default: () => ({ distanceValue: 5, distanceUnit: 'kilometers', @@ -56,7 +51,8 @@ const props = defineProps({ // Generate internal ref tied to modelValue prop const emit = defineEmits(['update:modelValue']); -const model = useObjectModel<Pace>(() => props.modelValue, (x) => emit('update:modelValue', x)); +const model = useObjectModel<DistanceTime>(() => props.modelValue, + (x) => emit('update:modelValue', x)); </script> <style scoped> diff --git a/src/components/RaceOptions.vue b/src/components/RaceOptions.vue @@ -20,16 +20,11 @@ <script setup lang="ts"> import type { PropType } from 'vue'; -import { RacePredictionModel } from '@/utils/races'; +import type { RaceOptions } from '@/utils/calculators'; import DecimalInput from '@/components/DecimalInput.vue'; import useObjectModel from '@/composables/useObjectModel'; -interface RaceOptions { - model: RacePredictionModel, - riegelExponent: number, -} - const props = defineProps({ /** * The component value diff --git a/src/components/SingleOutputTable.vue b/src/components/SingleOutputTable.vue @@ -36,15 +36,19 @@ </div> </template> -<script setup> +<script setup lang="ts"> import { computed } from 'vue'; +import type { PropType } from 'vue'; + +import type { TargetResult } from '@/utils/calculators'; +import type { Target } from '@/utils/targets'; const props = defineProps({ /** * The method that generates the target table rows */ calculateResult: { - type: Function, + type: Function as PropType<(x: Target) => TargetResult>, required: true, }, @@ -52,7 +56,7 @@ const props = defineProps({ * The target set */ targets: { - type: Array, + type: Array<Target>, default: () => [], }, @@ -70,7 +74,7 @@ const props = defineProps({ */ const results = computed(() => { // Calculate results - const result = []; + const result: Array<TargetResult> = []; props.targets.forEach((row) => { // Add result result.push(props.calculateResult(row)); diff --git a/src/components/SplitOutputTable.vue b/src/components/SplitOutputTable.vue @@ -19,7 +19,7 @@ <tr v-for="(item, index) in results" :key="index"> <td> {{ formatNumber(item.distanceValue, 0, 2, false) }} - {{ DISTANCE_UNITS[item.distanceUnit].symbol }} + {{ DistanceUnitData[item.distanceUnit].symbol }} </td> <td> @@ -32,7 +32,7 @@ <td> {{ formatDuration(item.pace, 3, 0, true) }} - / {{ DISTANCE_UNITS[getDefaultDistanceUnit(defaultUnitSystem)] + / {{ DistanceUnitData[getDefaultDistanceUnit(defaultUnitSystem)] .symbol }} </td> </tr> @@ -50,7 +50,7 @@ import { computed } from 'vue'; import { formatDuration, formatNumber } from '@/utils/format'; -import { DISTANCE_UNITS, convertDistance, getDefaultDistanceUnit } from '@/utils/units'; +import { DistanceUnitData, convertDistance, getDefaultDistanceUnit } from '@/utils/units'; import TimeInput from '@/components/TimeInput.vue'; import useObjectModel from '@/composables/useObjectModel'; diff --git a/src/components/TargetEditor.vue b/src/components/TargetEditor.vue @@ -32,7 +32,7 @@ <decimal-input v-model="item.splitValue" aria-label="Split distance value" :min="0" :digits="2"/> <select v-model="item.splitUnit" aria-label="Split distance unit"> - <option v-for="(value, key) in DISTANCE_UNITS" :key="key" :value="key"> + <option v-for="(value, key) in DistanceUnitData" :key="key" :value="key"> {{ value.name }} </option> </select> @@ -46,7 +46,7 @@ <decimal-input v-model="item.distanceValue" aria-label="Target distance value" :min="0" :digits="2"/> <select v-model="item.distanceUnit" aria-label="Target distance unit"> - <option v-for="(value, key) in DISTANCE_UNITS" :key="key" :value="key"> + <option v-for="(value, key) in DistanceUnitData" :key="key" :value="key"> {{ value.name }} </option> </select> @@ -90,7 +90,7 @@ import VueFeather from 'vue-feather'; import { workoutTargetToString } from '@/utils/targets'; -import { DISTANCE_UNITS, getDefaultDistanceUnit } from '@/utils/units'; +import { DistanceUnitData, getDefaultDistanceUnit } from '@/utils/units'; import DecimalInput from '@/components/DecimalInput.vue'; import TimeInput from '@/components/TimeInput.vue'; diff --git a/src/utils/calculators.js b/src/utils/calculators.js @@ -1,178 +0,0 @@ -import { formatDuration, formatNumber } from '@/utils/format'; -import * as paceUtils from '@/utils/paces'; -import * as raceUtils from '@/utils/races'; -import { workoutTargetToString } from '@/utils/targets'; -import { DISTANCE_UNITS, convertDistance, getDefaultDistanceUnit } from '@/utils/units'; - -/** - * Format a distance/time result as a key/value result - * @param {Object} result The distance/time result - * @param {String} defaultUnitSystem The default unit system (imperial or metric) - * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations - * @returns {Object} The key/value result - */ -export function formatDistTimeResult(result, defaultUnitSystem, preciseDurations = true) { - // Calculate numerical pace - const pace = result.time / convertDistance(result.distanceValue, result.distanceUnit, - getDefaultDistanceUnit(defaultUnitSystem)); - - return { - // Convert distance to key string - key: formatNumber(result.distanceValue, 0, 2, result.result === 'distance') + ' ' + - DISTANCE_UNITS[result.distanceUnit].symbol, - - // Convert time to time string - value: formatDuration(result.time, 3, preciseDurations ? 2 : 0, result.result === 'time'), - - // Convert pace to pace string - pace: formatDuration(pace, 3, 0, true) + ' / ' - + DISTANCE_UNITS[getDefaultDistanceUnit(defaultUnitSystem)].symbol, - - // Convert dist/time result to key/value - result: result.result === 'time' ? 'value' : 'key', - - // Use time (in seconds) as sort key - sort: result.time, - }; -} - -/** - * Calculate paces from a target - * @param {Object} input The input pace - * @param {Object} target The pace target - * @param {String} defaultUnitSystem The default unit system (imperial or metric) - * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations - * @returns {Object} The result - */ -export function calculatePaceResults(input, target, defaultUnitSystem, preciseDurations = true) { - const result = { - distanceValue: target.distanceValue, - distanceUnit: target.distanceUnit, - time: target.time, - result: target.type === 'distance' ? 'time' : 'distance', - }; - - const d1 = convertDistance(input.distanceValue, input.distanceUnit, 'meters'); - - // Add missing value to result - if (target.type === 'distance') { - // Convert target distance into meters - const d2 = convertDistance(target.distanceValue, target.distanceUnit, 'meters'); - - // Calculate time to travel distance at input pace - result.time = paceUtils.calculateTime(d1, input.time, d2); - } else { - // Calculate distance traveled in time at input pace - const d2 = paceUtils.calculateDistance(input.time, d1, target.time); - - // Convert output distance into default distance unit - const units = getDefaultDistanceUnit(defaultUnitSystem); - result.distanceValue = convertDistance(d2, 'meters', units); - result.distanceUnit = units; - } - - // Return result - return formatDistTimeResult(result, defaultUnitSystem, preciseDurations); -} - -/** - * Predict race results from a target - * @param {Object} input The input race - * @param {Object} target The race target - * @param {Object} options The race prediction options - * @param {String} defaultUnitSystem The default unit system (imperial or metric) - * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations - * @returns {Object} The result - */ -export function calculateRaceResults(input, target, options, defaultUnitSystem, - preciseDurations = true) { - - const result = { - distanceValue: target.distanceValue, - distanceUnit: target.distanceUnit, - time: target.time, - result: target.type === 'distance' ? 'time' : 'distance', - }; - - const d1 = convertDistance(input.distanceValue, input.distanceUnit, 'meters'); - - // Add missing value to result - if (target.type === 'distance') { - // Convert target distance into meters - const d2 = convertDistance(target.distanceValue, target.distanceUnit, 'meters'); - - // Get prediction - result.time = raceUtils.predictTime(d1, input.time, d2, options.model, options.riegelExponent); - } else { - // Get prediction - let distance = raceUtils.predictDistance(input.time, d1, target.time, options.model, - options.riegelExponent); - - // Convert output distance into default distance unit - distance = convertDistance(distance, 'meters', - getDefaultDistanceUnit(defaultUnitSystem)); - - // Update result - result.distanceValue = distance; - result.distanceUnit = getDefaultDistanceUnit(defaultUnitSystem); - } - - // Return result - return formatDistTimeResult(result, defaultUnitSystem, preciseDurations); -} - -/** - * Calculate race statistics from an input race - * @param {Object} input The input race - * @returns {Object} The race statistics - */ -export function calculateRaceStats(input) { - const d1 = convertDistance(input.distanceValue, input.distanceUnit, 'meters'); - - return { - purdyPoints: raceUtils.getPurdyPoints(d1, input.time), - vo2Max: raceUtils.getVO2Max(d1, input.time), - vo2: raceUtils.getVO2(d1, input.time), - vo2MaxPercentage: raceUtils.getVO2Percentage(input.time) * 100, - } -} - -/** - * Predict workout results from a target - * @param {Object} input The input race - * @param {Object} target The workout target - * @param {Object} options The workout options - * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations - * @returns {Object} The result - */ -export function calculateWorkoutResults(input, target, options, preciseDurations = true) { - // Initialize distance and time variables - const d1 = convertDistance(input.distanceValue, input.distanceUnit, 'meters'); - const t1 = input.time; - const d3 = convertDistance(target.splitValue, target.splitUnit, 'meters'); - let d2, t2, t3; - - // Calculate result - if (target.type === 'distance') { - // Convert target distance into meters - d2 = convertDistance(target.distanceValue, target.distanceUnit, 'meters'); - - // Get workout split prediction - t2 = raceUtils.predictTime(d1, input.time, d2, options.model, options.riegelExponent); - } else { - t2 = target.time; - - // Get workout split prediction - d2 = raceUtils.predictDistance(t1, d1, t2, options.model, options.riegelExponent); - } - t3 = paceUtils.calculateTime(d2, t2, d3); - - // Return result - return { - key: (options.customTargetNames && target.customName) || workoutTargetToString(target), - value: formatDuration(t3, 3, preciseDurations ? 2 : 0, true), - pace: '', // Pace not used in workout calculator - result: 'value', - sort: t3, - } -} diff --git a/src/utils/calculators.ts b/src/utils/calculators.ts @@ -0,0 +1,234 @@ +import { formatDuration, formatNumber } from '@/utils/format'; +import * as paceUtils from '@/utils/paces'; +import * as raceUtils from '@/utils/races'; +import { TargetType, workoutTargetToString } from '@/utils/targets'; +import type { StandardTarget, WorkoutTarget } from '@/utils/targets'; +import { DistanceUnits, DistanceUnitData, UnitSystems, convertDistance, + getDefaultDistanceUnit } from '@/utils/units'; +import type { DistanceTime } from '@/utils/units'; + +export enum ResultType { + Key = 'key', + Value = 'value', +}; + +interface PreResult { + distanceValue: number, + distanceUnit: DistanceUnits, + result: TargetType, + time: number, +}; + +export interface TargetResult { + key: string, + value: string, + pace: string, + result: ResultType, + sort: number, +}; + +export interface RaceOptions { + model: raceUtils.RacePredictionModel, + riegelExponent: number, +} + +export interface RaceStats { + purdyPoints: number, + vo2Max: number, + vo2: number, + vo2MaxPercentage: number, +} + +export interface WorkoutOptions extends RaceOptions { + customTargetNames: boolean, +} + +/** + * Format a distance/time result as a key/value result + * @param {PreResult} result The distance/time result + * @param {UnitSystems} defaultUnitSystem The default unit system (imperial or metric) + * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations + * @returns {TargetResult} The key/value result + */ +export function formatTargetResult(result: PreResult, defaultUnitSystem: UnitSystems, + preciseDurations: boolean = true): TargetResult { + // Calculate numerical pace + const pace = result.time / convertDistance(result.distanceValue, result.distanceUnit, + getDefaultDistanceUnit(defaultUnitSystem)); + + return { + // Convert distance to key string + key: formatNumber(result.distanceValue, 0, 2, result.result === 'distance') + ' ' + + DistanceUnitData[result.distanceUnit].symbol, + + // Convert time to time string + value: formatDuration(result.time, 3, preciseDurations ? 2 : 0, result.result === 'time'), + + // Convert pace to pace string + pace: formatDuration(pace, 3, 0, true) + ' / ' + + DistanceUnitData[getDefaultDistanceUnit(defaultUnitSystem)].symbol, + + // Convert dist/time result to key/value + result: result.result === TargetType.Time ? ResultType.Value : ResultType.Key, + + // Use time (in seconds) as sort key + sort: result.time, + }; +} + +/** + * Calculate paces from a target + * @param {DistanceTime } input The input pace + * @param {StandardTarget} target The pace target + * @param {UnitSystems} defaultUnitSystem The default unit system (imperial or metric) + * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations + * @returns {TargetResult} The result + */ +export function calculatePaceResults(input: DistanceTime, target: StandardTarget, + defaultUnitSystem: UnitSystems, + preciseDurations: boolean = true): TargetResult { + const result: PreResult = { + distanceValue: 0, + distanceUnit: DistanceUnits.Meters, + time: 0, + result: target.type === TargetType.Distance ? TargetType.Time : TargetType.Distance, + }; + + const d1 = convertDistance(input.distanceValue, input.distanceUnit, DistanceUnits.Meters); + + // Add missing value to result + if (target.type === 'distance') { + // Add target distance to result + result.distanceValue = target.distanceValue; + result.distanceUnit = target.distanceUnit; + + // Convert target distance into meters + const d2 = convertDistance(target.distanceValue, target.distanceUnit, DistanceUnits.Meters); + + // Calculate time to travel distance at input pace + result.time = paceUtils.calculateTime(d1, input.time, d2); + } else { + // Add target time to result + result.time = target.time; + + // Calculate distance traveled in time at input pace + const d2 = paceUtils.calculateDistance(input.time, d1, target.time); + + // Convert output distance into default distance unit + const units = getDefaultDistanceUnit(defaultUnitSystem); + result.distanceValue = convertDistance(d2, DistanceUnits.Meters, units); + result.distanceUnit = units; + } + + // Return result + return formatTargetResult(result, defaultUnitSystem, preciseDurations); +} + +/** + * Predict race results from a target + * @param {DistanceTime} input The input race + * @param {StandardTarget} target The race target + * @param {RaceOptions} options The race prediction options + * @param {UnitSystems} defaultUnitSystem The default unit system (imperial or metric) + * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations + * @returns {TargetResult} The result + */ +export function calculateRaceResults(input: DistanceTime, target: StandardTarget, + options: RaceOptions, defaultUnitSystem: UnitSystems, + preciseDurations: boolean = true): TargetResult { + + const result: PreResult = { + distanceValue: 0, + distanceUnit: DistanceUnits.Meters, + time: 0, + result: target.type === TargetType.Distance ? TargetType.Time : TargetType.Distance, + }; + + const d1 = convertDistance(input.distanceValue, input.distanceUnit, DistanceUnits.Meters); + + // Add missing value to result + if (target.type === 'distance') { + // Add target distance to result + result.distanceValue = target.distanceValue; + result.distanceUnit = target.distanceUnit; + + // Convert target distance into meters + const d2 = convertDistance(target.distanceValue, target.distanceUnit, DistanceUnits.Meters); + + // Get prediction + result.time = raceUtils.predictTime(d1, input.time, d2, options.model, options.riegelExponent); + } else { + // Add target time to result + result.time = target.time; + + // Get prediction + const distance = raceUtils.predictDistance(input.time, d1, target.time, options.model, + options.riegelExponent); + + // Convert output distance into default distance unit + const units = getDefaultDistanceUnit(defaultUnitSystem); + result.distanceValue = convertDistance(distance, DistanceUnits.Meters, units); + result.distanceUnit = getDefaultDistanceUnit(defaultUnitSystem); + } + + // Return result + return formatTargetResult(result, defaultUnitSystem, preciseDurations); +} + +/** + * Calculate race statistics from an input race + * @param {DistanceTime} input The input race + * @returns {RaceStats} The race statistics + */ +export function calculateRaceStats(input: DistanceTime): RaceStats { + const d1 = convertDistance(input.distanceValue, input.distanceUnit, DistanceUnits.Meters); + + return { + purdyPoints: raceUtils.getPurdyPoints(d1, input.time), + vo2Max: raceUtils.getVO2Max(d1, input.time), + vo2: raceUtils.getVO2(d1, input.time), + vo2MaxPercentage: raceUtils.getVO2Percentage(input.time) * 100, + } +} + +/** + * Predict workout results from a target + * @param {DistanceTime} input The input race + * @param {WorkoutTarget} target The workout target + * @param {WorkoutOptions} options The workout options + * @param {Boolean} preciseDurations Whether to return precise, unrounded, durations + * @returns {TargetResult} The result + */ +export function calculateWorkoutResults(input: DistanceTime, target: WorkoutTarget, + options: WorkoutOptions, + preciseDurations: boolean = true): TargetResult { + // Initialize distance and time variables + const d1 = convertDistance(input.distanceValue, input.distanceUnit, DistanceUnits.Meters); + const t1 = input.time; + const d3 = convertDistance(target.splitValue, target.splitUnit, DistanceUnits.Meters); + let d2, t2; + + // Calculate result + if (target.type === 'distance') { + // Convert target distance into meters + d2 = convertDistance(target.distanceValue, target.distanceUnit, DistanceUnits.Meters); + + // Get workout split prediction + t2 = raceUtils.predictTime(d1, input.time, d2, options.model, options.riegelExponent); + } else { + t2 = target.time; + + // Get workout split prediction + d2 = raceUtils.predictDistance(t1, d1, t2, options.model, options.riegelExponent); + } + const t3 = paceUtils.calculateTime(d2, t2, d3); + + // Return result + return { + key: (options.customTargetNames && target.customName) || workoutTargetToString(target), + value: formatDuration(t3, 3, preciseDurations ? 2 : 0, true), + pace: '', // Pace not used in workout calculator + result: ResultType.Value, + sort: t3, + } +} diff --git a/src/utils/targets.ts b/src/utils/targets.ts @@ -1,5 +1,5 @@ import { formatDuration, formatNumber } from '@/utils/format'; -import { DISTANCE_UNITS, DISTANCE_UNIT_KEYS, convertDistance } from '@/utils/units'; +import { DistanceUnits, DistanceUnitData, convertDistance } from '@/utils/units'; /* * Enumeration for the two basic types of targets: those defined by distance vs time @@ -15,7 +15,7 @@ export enum TargetType { interface DistanceTarget { type: TargetType.Distance, distanceValue: number, - distanceUnit: DISTANCE_UNIT_KEYS, + distanceUnit: DistanceUnits, }; /** @@ -59,7 +59,8 @@ export interface SplitTargetSet { */ export type WorkoutTarget = StandardTarget & { splitValue: number, - splitUnit: DISTANCE_UNIT_KEYS, + splitUnit: DistanceUnits, + customName?: string, }; /* @@ -83,8 +84,8 @@ export type Target = StandardTarget | SplitTarget | WorkoutTarget; export function sort(targets: Array<Target>): Array<Target> { return [ ...targets.filter((item) => item.type === TargetType.Distance) - .sort((a, b) => convertDistance(a.distanceValue, a.distanceUnit, DISTANCE_UNIT_KEYS.meters) - - convertDistance(b.distanceValue, b.distanceUnit, DISTANCE_UNIT_KEYS.meters)), + .sort((a, b) => convertDistance(a.distanceValue, a.distanceUnit, DistanceUnits.Meters) + - convertDistance(b.distanceValue, b.distanceUnit, DistanceUnits.Meters)), ...targets.filter((item) => item.type === TargetType.Time) .sort((a, b) => a.time - b.time), @@ -98,12 +99,12 @@ export function sort(targets: Array<Target>): Array<Target> { */ export function workoutTargetToString(target: WorkoutTarget): string { let result = formatNumber(target.splitValue, 0, 2, false) + ' ' + - DISTANCE_UNITS[target.splitUnit].symbol; + DistanceUnitData[target.splitUnit].symbol; if (target.type === TargetType.Time) { result += ' @ ' + formatDuration(target.time, 3, 2, false); } else if (target.distanceValue != target.splitValue || target.distanceUnit != target.splitUnit) { result += ' @ ' + formatNumber(target.distanceValue, 0, 2, false) + ' ' + - DISTANCE_UNITS[target.distanceUnit].symbol; + DistanceUnitData[target.distanceUnit].symbol; } return result; } @@ -114,36 +115,36 @@ export function workoutTargetToString(target: WorkoutTarget): string { const common_pace_targets: StandardTargetSet = { name: 'Common Pace Targets', targets: sort([ - { type: TargetType.Distance, distanceValue: 100, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 200, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 300, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 400, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 600, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 800, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 1000, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 1200, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 1500, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 1600, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 3200, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - - { type: TargetType.Distance, distanceValue: 2, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 3, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 4, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 5, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 6, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 8, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 10, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - - { type: TargetType.Distance, distanceValue: 1, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 2, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 3, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 5, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 6, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 8, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 10, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - - { type: TargetType.Distance, distanceValue: 0.5, distanceUnit: DISTANCE_UNIT_KEYS.marathons }, - { type: TargetType.Distance, distanceValue: 1, distanceUnit: DISTANCE_UNIT_KEYS.marathons }, + { type: TargetType.Distance, distanceValue: 100, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 200, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 300, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 400, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 600, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 800, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 1000, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 1200, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 1500, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 1600, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 3200, distanceUnit: DistanceUnits.Meters }, + + { type: TargetType.Distance, distanceValue: 2, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 3, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 4, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 5, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 6, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 8, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 10, distanceUnit: DistanceUnits.Kilometers }, + + { type: TargetType.Distance, distanceValue: 1, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 2, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 3, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 5, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 6, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 8, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 10, distanceUnit: DistanceUnits.Miles }, + + { type: TargetType.Distance, distanceValue: 0.5, distanceUnit: DistanceUnits.Marathons }, + { type: TargetType.Distance, distanceValue: 1, distanceUnit: DistanceUnits.Marathons }, { type: TargetType.Time, time: 600 }, { type: TargetType.Time, time: 1800 }, @@ -157,24 +158,24 @@ const common_pace_targets: StandardTargetSet = { const common_race_targets: StandardTargetSet = { name: 'Common Race Targets', targets: sort([ - { type: TargetType.Distance, distanceValue: 400, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 800, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 1500, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 1600, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 1, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 3000, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 3200, distanceUnit: DISTANCE_UNIT_KEYS.meters }, - { type: TargetType.Distance, distanceValue: 2, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - - { type: TargetType.Distance, distanceValue: 3, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 5, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 6, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 8, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 10, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - { type: TargetType.Distance, distanceValue: 15, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, - - { type: TargetType.Distance, distanceValue: 0.5, distanceUnit: DISTANCE_UNIT_KEYS.marathons }, - { type: TargetType.Distance, distanceValue: 1, distanceUnit: DISTANCE_UNIT_KEYS.marathons }, + { type: TargetType.Distance, distanceValue: 400, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 800, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 1500, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 1600, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 1, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 3000, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 3200, distanceUnit: DistanceUnits.Meters }, + { type: TargetType.Distance, distanceValue: 2, distanceUnit: DistanceUnits.Miles }, + + { type: TargetType.Distance, distanceValue: 3, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 5, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 6, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 8, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 10, distanceUnit: DistanceUnits.Kilometers }, + { type: TargetType.Distance, distanceValue: 15, distanceUnit: DistanceUnits.Kilometers }, + + { type: TargetType.Distance, distanceValue: 0.5, distanceUnit: DistanceUnits.Marathons }, + { type: TargetType.Distance, distanceValue: 1, distanceUnit: DistanceUnits.Marathons }, ]), }; @@ -185,9 +186,9 @@ const common_race_targets: StandardTargetSet = { const five_k_mile_splits: SplitTargetSet = { name: '5K Mile Splits', targets: [ - { type: TargetType.Distance, distanceValue: 1, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 2, distanceUnit: DISTANCE_UNIT_KEYS.miles }, - { type: TargetType.Distance, distanceValue: 5, distanceUnit: DISTANCE_UNIT_KEYS.kilometers }, + { type: TargetType.Distance, distanceValue: 1, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 2, distanceUnit: DistanceUnits.Miles }, + { type: TargetType.Distance, distanceValue: 5, distanceUnit: DistanceUnits.Kilometers }, ], }; @@ -198,20 +199,20 @@ const common_workout_targets: WorkoutTargetSet = { name: 'Common Workout Targets', targets: [ { - splitValue: 400, splitUnit: DISTANCE_UNIT_KEYS.meters, - type: TargetType.Distance, distanceValue: 1, distanceUnit: DISTANCE_UNIT_KEYS.miles, + splitValue: 400, splitUnit: DistanceUnits.Meters, + type: TargetType.Distance, distanceValue: 1, distanceUnit: DistanceUnits.Miles, }, { - splitValue: 800, splitUnit: DISTANCE_UNIT_KEYS.meters, - type: TargetType.Distance, distanceValue: 5, distanceUnit: DISTANCE_UNIT_KEYS.kilometers, + splitValue: 800, splitUnit: DistanceUnits.Meters, + type: TargetType.Distance, distanceValue: 5, distanceUnit: DistanceUnits.Kilometers, }, { - splitValue: 1600, splitUnit: DISTANCE_UNIT_KEYS.meters, + splitValue: 1600, splitUnit: DistanceUnits.Meters, type: TargetType.Time, time: 3600, }, { - splitValue: 1, splitUnit: DISTANCE_UNIT_KEYS.miles, - type: TargetType.Distance, distanceValue: 1, distanceUnit: DISTANCE_UNIT_KEYS.marathons, + splitValue: 1, splitUnit: DistanceUnits.Miles, + type: TargetType.Distance, distanceValue: 1, distanceUnit: DistanceUnits.Marathons, }, ], }; diff --git a/src/utils/units.ts b/src/utils/units.ts @@ -1,41 +1,23 @@ -export enum TIME_UNIT_KEYS { - seconds = 'seconds', - minutes = 'minutes', - hours = 'hours', -} -export enum DISTANCE_UNIT_KEYS { - meters = 'meters', - yards = 'yards', - kilometers = 'kilometers', - miles = 'miles', - marathons = 'marathons', -} -export enum SPEED_UNIT_KEYS { - meters_per_second = 'meters_per_second', - kilometers_per_hour = 'kilometers_per_hour', - miles_per_hour = 'miles_per_hour', -} -export enum PACE_UNIT_KEYS { - seconds_per_meter = 'seconds_per_meter', - time_per_kilometer = 'seconds_per_kilometer', - time_per_mile = 'seconds_per_mile', -} - /** - * The time units + * The supported time units */ -export const TIME_UNITS = { - seconds: { +export enum TimeUnits { + Seconds = 'seconds', + Minutes = 'minutes', + Hours = 'hours', +} +export const TimeUnitData = { + [TimeUnits.Seconds]: { name: 'Seconds', symbol: 's', value: 1, }, - minutes: { + [TimeUnits.Minutes]: { name: 'Minutes', symbol: 'min', value: 60, }, - hours: { + [TimeUnits.Hours]: { name: 'Hours', symbol: 'hr', value: 3600, @@ -43,30 +25,37 @@ export const TIME_UNITS = { }; /** - * The distance units + * The supported distance units */ -export const DISTANCE_UNITS = { - meters: { +export enum DistanceUnits { + Meters = 'meters', + Yards = 'yards', + Kilometers = 'kilometers', + Miles = 'miles', + Marathons = 'marathons', +} +export const DistanceUnitData = { + [DistanceUnits.Meters]: { name: 'Meters', symbol: 'm', value: 1, }, - yards: { + [DistanceUnits.Yards]: { name: 'Yards', symbol: 'yd', value: 0.9144, }, - kilometers: { + [DistanceUnits.Kilometers]: { name: 'Kilometers', symbol: 'km', value: 1000, }, - miles: { + [DistanceUnits.Miles]: { name: 'Miles', symbol: 'mi', value: 1609.3499, }, - marathons: { + [DistanceUnits.Marathons]: { name: 'Marathons', symbol: 'Mar', value: 42195, @@ -74,9 +63,14 @@ export const DISTANCE_UNITS = { }; /** - * The speed units + * The supported speed units */ -export const SPEED_UNITS = { +export enum SpeedUnits { + MetersPerSecond = 'meters_per_second', + KilometersPerHour = 'kilometers_per_hour', + MilesPerHour = 'miles_per_hour', +} +export const SpeedUnitData = { meters_per_second: { name: 'Meters per Second', symbol: 'm/s', @@ -85,36 +79,55 @@ export const SPEED_UNITS = { kilometers_per_hour: { name: 'Kilometers per Hour', symbol: 'kph', - value: DISTANCE_UNITS.kilometers.value / TIME_UNITS.hours.value, + value: DistanceUnitData[DistanceUnits.Kilometers].value / TimeUnitData[TimeUnits.Hours].value, }, miles_per_hour: { name: 'Miles per Hour', symbol: 'mph', - value: DISTANCE_UNITS.miles.value / TIME_UNITS.hours.value, + value: DistanceUnitData[DistanceUnits.Miles].value / TimeUnitData[TimeUnits.Hours].value, }, }; /** - * The value of each pace unit in seconds per meter + * The supported pace units */ -export const PACE_UNITS = { - seconds_per_meter: { +export enum PaceUnits { + SecondsPerMeter = 'seconds_per_meter', + TimePerKilometer = 'seconds_per_kilometer', + TimePerMile = 'seconds_per_mile', +} +export const PaceUnitData = { + [PaceUnits.SecondsPerMeter]: { name: 'Seconds per Meter', symbol: 's/m', value: 1, }, - seconds_per_kilometer: { + [PaceUnits.TimePerKilometer]: { name: 'Time per Kilometer', symbol: '/ km', - value: TIME_UNITS.seconds.value / DISTANCE_UNITS.kilometers.value, + value: TimeUnitData[TimeUnits.Seconds].value / DistanceUnitData[DistanceUnits.Kilometers].value, }, - seconds_per_mile: { + [PaceUnits.TimePerMile]: { name: 'Time per Mile', symbol: '/ mi', - value: TIME_UNITS.seconds.value / DISTANCE_UNITS.miles.value, + value: TimeUnitData[TimeUnits.Seconds].value / DistanceUnitData[DistanceUnits.Miles].value, }, }; +export enum UnitSystems { + Metric = 'metric', + Imperial = 'imperial', +}; + +export interface Distance { + distanceValue: number, + distanceUnit: DistanceUnits, +} + +export interface DistanceTime extends Distance { + time: number, +} + /** * Convert between time units * @param {number} inputValue The input value @@ -122,9 +135,9 @@ export const PACE_UNITS = { * @param {string} outputUnit The unit of the output * @returns {number} The output */ -export function convertTime(inputValue: number, inputUnit: TIME_UNIT_KEYS, - outputUnit: TIME_UNIT_KEYS): number { - return (inputValue * TIME_UNITS[inputUnit].value) / TIME_UNITS[outputUnit].value; +export function convertTime(inputValue: number, inputUnit: TimeUnits, + outputUnit: TimeUnits): number { + return (inputValue * TimeUnitData[inputUnit].value) / TimeUnitData[outputUnit].value; } /** @@ -134,9 +147,9 @@ export function convertTime(inputValue: number, inputUnit: TIME_UNIT_KEYS, * @param {string} outputUnit The unit of the output * @returns {number} The output */ -export function convertDistance(inputValue: number, inputUnit: DISTANCE_UNIT_KEYS, - outputUnit: DISTANCE_UNIT_KEYS): number { - return (inputValue * DISTANCE_UNITS[inputUnit].value) / DISTANCE_UNITS[outputUnit].value; +export function convertDistance(inputValue: number, inputUnit: DistanceUnits, + outputUnit: DistanceUnits): number { + return (inputValue * DistanceUnitData[inputUnit].value) / DistanceUnitData[outputUnit].value; } /** @@ -146,9 +159,9 @@ export function convertDistance(inputValue: number, inputUnit: DISTANCE_UNIT_KEY * @param {string} outputUnit The unit of the output * @returns {number} The output */ -export function convertSpeed(inputValue: number, inputUnit: SPEED_UNIT_KEYS, - outputUnit: SPEED_UNIT_KEYS): number { - return (inputValue * SPEED_UNITS[inputUnit].value) / SPEED_UNITS[outputUnit].value; +export function convertSpeed(inputValue: number, inputUnit: SpeedUnits, + outputUnit: SpeedUnits): number { + return (inputValue * SpeedUnitData[inputUnit].value) / SpeedUnitData[outputUnit].value; } /** @@ -158,9 +171,9 @@ export function convertSpeed(inputValue: number, inputUnit: SPEED_UNIT_KEYS, * @param {string} outputUnit The unit of the output * @returns {number} The output */ -export function convertPace(inputValue: number, inputUnit: PACE_UNIT_KEYS, - outputUnit: PACE_UNIT_KEYS): number { - return (inputValue * PACE_UNITS[inputUnit].value) / PACE_UNITS[outputUnit].value; +export function convertPace(inputValue: number, inputUnit: PaceUnits, + outputUnit: PaceUnits): number { + return (inputValue * PaceUnitData[inputUnit].value) / PaceUnitData[outputUnit].value; } /** @@ -170,59 +183,60 @@ export function convertPace(inputValue: number, inputUnit: PACE_UNIT_KEYS, * @param {string} outputUnit The unit of the output * @returns {number} The output */ -export function convertSpeedPace(inputValue: number, inputUnit: SPEED_UNIT_KEYS | PACE_UNIT_KEYS, - outputUnit: SPEED_UNIT_KEYS | PACE_UNIT_KEYS): number { +export function convertSpeedPace(inputValue: number, inputUnit: SpeedUnits | PaceUnits, + outputUnit: SpeedUnits | PaceUnits): number { // Calculate input speed let speed; - if (inputUnit in PACE_UNITS) { - speed = 1 / (inputValue * PACE_UNITS[inputUnit as PACE_UNIT_KEYS].value); + if (inputUnit in PaceUnitData) { + speed = 1 / (inputValue * PaceUnitData[inputUnit as PaceUnits].value); } else { - speed = inputValue * SPEED_UNITS[inputUnit as SPEED_UNIT_KEYS].value; + speed = inputValue * SpeedUnitData[inputUnit as SpeedUnits].value; } // Calculate output - if (outputUnit in PACE_UNITS) { - return (1 / speed) / PACE_UNITS[outputUnit as PACE_UNIT_KEYS].value; + if (outputUnit in PaceUnitData) { + return (1 / speed) / PaceUnitData[outputUnit as PaceUnits].value; } - return speed / SPEED_UNITS[outputUnit as SPEED_UNIT_KEYS].value; + return speed / SpeedUnitData[outputUnit as SpeedUnits].value; } /** * Detect the user's default unit system - * @returns {string} The default unit system + * @returns {UnitSystems} The default unit system */ -export function detectDefaultUnitSystem(): string { +export function detectDefaultUnitSystem(): UnitSystems { // eslint-disable-next-line @typescript-eslint/no-explicit-any const language = (navigator.language || (navigator as any).userLanguage).toLowerCase(); if (language.endsWith('-us') || language.endsWith('-mm')) { - return 'imperial'; + return UnitSystems.Imperial; } - return 'metric'; + return UnitSystems.Metric; } /** * Get the default distance unit in a unit system - * @param {string} unitSystem The unit system - * @returns {string} The default distance unit + * @param {UnitSystems} unitSystem The unit system + * @returns {DistanceUnits} The default distance unit */ -export function getDefaultDistanceUnit(unitSystem: string): string { - return unitSystem === 'metric' ? 'kilometers' : 'miles'; +export function getDefaultDistanceUnit(unitSystem: UnitSystems): DistanceUnits { + return unitSystem === UnitSystems.Metric ? DistanceUnits.Kilometers : DistanceUnits.Miles; } /** * Get the default speed unit in a unit system - * @param {string} unitSystem The unit system - * @returns {string} The default speed unit + * @param {UnitSystems} unitSystem The unit system + * @returns {SpeedUnits} The default speed unit */ -export function getDefaultSpeedUnit(unitSystem: string): string { - return unitSystem === 'metric' ? 'kilometers_per_hour' : 'miles_per_hour'; +export function getDefaultSpeedUnit(unitSystem: UnitSystems): SpeedUnits { + return unitSystem === UnitSystems.Metric ? SpeedUnits.KilometersPerHour + : SpeedUnits.MilesPerHour; } /** * Get the default pace unit in a unit system - * @param {string} unitSystem The unit system - * @returns {string} The default pace unit + * @param {UnitSystems} unitSystem The unit system + * @returns {PaceUnits} The default pace unit */ -export function getDefaultPaceUnit(unitSystem: string): string { - return unitSystem === 'metric' ? 'seconds_per_kilometer' : 'seconds_per_mile'; +export function getDefaultPaceUnit(unitSystem: UnitSystems): PaceUnits { + return unitSystem === UnitSystems.Metric ? PaceUnits.TimePerKilometer : PaceUnits.TimePerMile; } diff --git a/src/views/UnitCalculator.vue b/src/views/UnitCalculator.vue @@ -38,7 +38,7 @@ import { computed, ref } from 'vue'; import { formatDuration, formatNumber } from '@/utils/format'; -import { DISTANCE_UNITS, TIME_UNITS, SPEED_UNITS, PACE_UNITS, convertDistance, convertTime, +import { DistanceUnitData, TimeUnitData, SpeedUnitData, PaceUnitData, convertDistance, convertTime, convertSpeedPace } from '@/utils/units'; import DecimalInput from '@/components/DecimalInput.vue'; @@ -90,11 +90,11 @@ const input = computed({ const units = computed(() => { switch (category.value) { case 'distance': { - return DISTANCE_UNITS; + return DistanceUnitData; } case 'time': { return { - ...TIME_UNITS, + ...TimeUnitData, 'hh:mm:ss': { name: 'hh:mm:ss', symbol: '', @@ -103,7 +103,7 @@ const units = computed(() => { }; } case 'speed_and_pace': { - return { ...PACE_UNITS, ...SPEED_UNITS }; + return { ...PaceUnitData, ...SpeedUnitData }; } default: { return {}; @@ -144,10 +144,10 @@ const outputValue = computed(() => { * @returns {String} The type ('decimal' or 'time') */ function getUnitType(unit) { - if (unit in DISTANCE_UNITS) { + if (unit in DistanceUnitData) { return 'decimal'; } - if (unit in TIME_UNITS) { + if (unit in TimeUnitData) { return 'decimal'; } if (unit === 'hh:mm:ss') {