running-tools

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

commit ffb11a11570eded74d17db9f12fe8e062c2e52a1
parent 3a6ab29f053d00197a7aca4f06ca2acbb34c2f08
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Sat, 17 Feb 2024 12:52:41 -0800

Implement default-unit-system setting

Diffstat:
Msrc/assets/target-calculator.css | 16++--------------
Msrc/components/SimpleTargetTable.vue | 12++++++++++--
Msrc/components/TargetEditor.vue | 10+++++++++-
Msrc/components/TargetSetSelector.vue | 21++++++++++++++++++++-
Msrc/utils/units.js | 27+++++++++++++++------------
Msrc/views/PaceCalculator.vue | 42+++++++++++++++++++++++++++++++++---------
Msrc/views/RaceCalculator.vue | 53++++++++++++++++++++++++++++++++++-------------------
Msrc/views/SplitCalculator.vue | 51+++++++++++++++++++++++++++++++++------------------
Mtests/unit/components/TargetEditor.spec.js | 1+
Mtests/unit/views/PaceCalculator.spec.js | 4++--
Mtests/unit/views/RaceCalculator.spec.js | 4++--
11 files changed, 161 insertions(+), 80 deletions(-)

diff --git a/src/assets/target-calculator.css b/src/assets/target-calculator.css @@ -22,26 +22,14 @@ h2 { margin-left: 5px; } -/* target set */ -.target-set { +/* extra settings */ +.target-set, .default-units { margin-bottom: 5px; } .target-set button { margin-left: 3px; } -/* target set editor dialog */ -.target-set-editor-dialog { - width: min(100% - 2em, 400px); - max-height: min(100% - 2em, 815px); - margin-top: 100px; -} -@media only screen and (max-height: 800px) { - .target-set-editor-dialog { - margin-top: 1em; - } -} - /* calculator output */ .output { min-width: 300px; diff --git a/src/components/SimpleTargetTable.vue b/src/components/SimpleTargetTable.vue @@ -24,7 +24,7 @@ <td v-if="showPace"> {{ formatDuration(getPace(item), 3, 0, true) }} - / {{ distanceUnits[getDefaultDistanceUnit()].symbol }} + / {{ distanceUnits[getDefaultDistanceUnit(defaultUnitSystem)].symbol }} </td> </tr> @@ -69,6 +69,14 @@ export default { type: Boolean, default: false, }, + + /** + * The unit system to use when showing result paces + */ + defaultUnitSystem: { + type: String, + default: 'metric', + }, }, data() { @@ -122,7 +130,7 @@ export default { */ getPace(result) { return result.time / unitUtils.convertDistance(result.distanceValue, result.distanceUnit, - unitUtils.getDefaultDistanceUnit()); + unitUtils.getDefaultDistanceUnit(this.defaultUnitSystem)); }, }, }; diff --git a/src/components/TargetEditor.vue b/src/components/TargetEditor.vue @@ -109,6 +109,14 @@ export default { type: Boolean, default: false, }, + + /** + * The unit system to use when creating distance targets + */ + defaultUnitSystem: { + type: String, + default: 'metric', + }, }, data() { @@ -173,7 +181,7 @@ export default { this.internalValue.targets.push({ result: 'time', distanceValue: 1, - distanceUnit: unitUtils.getDefaultDistanceUnit(), + distanceUnit: unitUtils.getDefaultDistanceUnit(this.defaultUnitSystem), }); }, diff --git a/src/components/TargetSetSelector.vue b/src/components/TargetSetSelector.vue @@ -13,7 +13,7 @@ <dialog ref="dialog" class="target-set-editor-dialog" aria-label="Edit target set"> <target-editor @close="$refs.dialog.close()" v-model="targetSets[internalValue]" - @revert="revertTargetSet" + @revert="revertTargetSet" :default-unit-system="defaultUnitSystem" :isCustomSet="!internalValue.startsWith('_')"/> </dialog> </span> @@ -43,6 +43,14 @@ export default { type: String, default: '_new', }, + + /** + * The unit system to use when creating distance targets + */ + defaultUnitSystem: { + type: String, + default: 'metric', + }, }, data() { @@ -137,4 +145,15 @@ export default { .target-set-selector .icon { margin-left: 0.3em; } + +.target-set-editor-dialog { + width: min(100% - 2em, 400px); + max-height: min(100% - 2em, 815px); + margin-top: 100px; +} +@media only screen and (max-height: 800px) { + .target-set-editor-dialog { + margin-top: 1em; + } +} </style> diff --git a/src/utils/units.js b/src/utils/units.js @@ -160,10 +160,10 @@ function convertSpeedPace(inputValue, inputUnit, outputUnit) { } /** - * Get the default unit system + * Detect the user's default unit system * @returns {String} The default unit system */ -function getDefaultUnitSystem() { +function detectDefaultUnitSystem() { const language = (navigator.language || navigator.userLanguage).toLowerCase(); if (language.endsWith('-us') || language.endsWith('-mm')) { return 'imperial'; @@ -172,27 +172,30 @@ function getDefaultUnitSystem() { } /** - * Get the default distance unit + * Get the default distance unit in a unit system + * @param {String} unitSystem The unit system * @returns {String} The default distance unit */ -function getDefaultDistanceUnit() { - return getDefaultUnitSystem() === 'metric' ? 'kilometers' : 'miles'; +function getDefaultDistanceUnit(unitSystem) { + return unitSystem === 'metric' ? 'kilometers' : 'miles'; } /** - * Get the default speed unit + * Get the default speed unit in a unit system + * @param {String} unitSystem The unit system * @returns {String} The default speed unit */ -function getDefaultSpeedUnit() { - return getDefaultUnitSystem() === 'metric' ? 'kilometers_per_hour' : 'miles_per_hour'; +function getDefaultSpeedUnit(unitSystem) { + return unitSystem === 'metric' ? 'kilometers_per_hour' : 'miles_per_hour'; } /** - * Get the default pace unit + * Get the default pace unit in a unit system + * @param {String} unitSystem The unit system * @returns {String} The default pace unit */ -function getDefaultPaceUnit() { - return getDefaultUnitSystem() === 'metric' ? 'seconds_per_kilometer' : 'seconds_per_mile'; +function getDefaultPaceUnit(unitSystem) { + return unitSystem === 'metric' ? 'seconds_per_kilometer' : 'seconds_per_mile'; } export default { @@ -207,7 +210,7 @@ export default { convertPace, convertSpeedPace, - getDefaultUnitSystem, + detectDefaultUnitSystem, getDefaultDistanceUnit, getDefaultSpeedUnit, getDefaultPaceUnit, diff --git a/src/views/PaceCalculator.vue b/src/views/PaceCalculator.vue @@ -19,9 +19,17 @@ </div> <h2>Equivalent Paces</h2> + <div class="default-units"> + Default units: + <select v-model="defaultUnitSystem" aria-label="Default units"> + <option value="imperial">Miles</option> + <option value="metric">Kilometers</option> + </select> + </div> <div class="target-set"> Target Set: - <target-set-selector v-model="selectedTargetSet" @targets-updated="reloadTargets"/> + <target-set-selector v-model="selectedTargetSet" @targets-updated="reloadTargets" + :default-unit-system="defaultUnitSystem"/> </div> <simple-target-table class="output" :calculate-result="calculatePace" @@ -68,6 +76,13 @@ export default { inputTime: storage.get('pace-calculator-input-time', 20 * 60), /** + * The default unit system + * + * Loaded in activate() method + */ + defaultUnitSystem: null, + + /** * The names of the distance units */ distanceUnits: unitUtils.DISTANCE_UNITS, @@ -79,13 +94,10 @@ export default { /** * The target sets + * + * Loaded in activate() method */ - targetSets: storage.get('target-sets', targetUtils.defaultTargetSets), - - /** - * Whether the target set is being edited - */ - editingTargetSets: false, + targetSets: {}, }; }, @@ -112,6 +124,13 @@ export default { }, /** + * Save default unit system + */ + defaultUnitSystem(newValue) { + storage.set('default-unit-system', newValue); + }, + + /** * Save the current selected target set */ selectedTargetSet(newValue) { @@ -166,11 +185,12 @@ export default { let distance = paceUtils.getDistance(this.pace, target.time); // Convert output distance into default distance unit - distance = unitUtils.convertDistance(distance, 'meters', unitUtils.getDefaultDistanceUnit()); + distance = unitUtils.convertDistance(distance, 'meters', + unitUtils.getDefaultDistanceUnit(this.defaultUnitSystem)); // Update result result.distanceValue = distance; - result.distanceUnit = unitUtils.getDefaultDistanceUnit(); + result.distanceUnit = unitUtils.getDefaultDistanceUnit(this.defaultUnitSystem); } // Return result @@ -178,8 +198,12 @@ export default { }, }, + /** + * (Re)load settings used in multiple calculators + */ activated() { this.targetSets = storage.get('target-sets', targetUtils.defaultTargetSets); + this.defaultUnitSystem = storage.get('default-unit-system', unitUtils.detectDefaultUnitSystem()); }, }; </script> diff --git a/src/views/RaceCalculator.vue b/src/views/RaceCalculator.vue @@ -24,6 +24,13 @@ </button> </h2> <div class="advanced-options" v-show="showAdvancedOptions"> + <div class="default-units"> + Default units: + <select v-model="defaultUnitSystem" aria-label="Default units"> + <option value="imperial">Miles</option> + <option value="metric">Kilometers</option> + </select> + </div> <div> Prediction Model: <select v-model="model" aria-label="Prediction model"> @@ -55,10 +62,11 @@ <h2>Equivalent Race Results</h2> <div class="target-set"> Target Set: - <target-set-selector v-model="selectedTargetSet" @targets-updated="reloadTargets"/> + <target-set-selector v-model="selectedTargetSet" @targets-updated="reloadTargets" + :default-unit-system="defaultUnitSystem"/> </div> - <simple-target-table class="output" :calculate-result="predictResult" + <simple-target-table class="output" :calculate-result="predictResult" :default-unit-system="defaultUnitSystem" :targets="targetSets[selectedTargetSet] ? targetSets[selectedTargetSet].targets : []" show-pace/> </div> </template> @@ -103,6 +111,13 @@ export default { inputTime: storage.get('race-calculator-input-time', 20 * 60), /** + * The default unit system + * + * Loaded in activate() method + */ + defaultUnitSystem: null, + + /** * The race prediction model */ model: storage.get('race-calculator-model', 'AverageModel'), @@ -134,13 +149,10 @@ export default { /** * The target sets + * + * Loaded in activate() method */ - targetSets: storage.get('target-sets', targetUtils.defaultTargetSets), - - /** - * Whether the target set is being edited - */ - editingTargetSets: false, + targetSets: {}, }; }, @@ -222,11 +234,12 @@ export default { } // Convert output distance into default distance unit - distance = unitUtils.convertDistance(distance, 'meters', unitUtils.getDefaultDistanceUnit()); + distance = unitUtils.convertDistance(distance, 'meters', + unitUtils.getDefaultDistanceUnit(this.defaultUnitSystem)); // Update result result.distanceValue = distance; - result.distanceUnit = unitUtils.getDefaultDistanceUnit(); + result.distanceUnit = unitUtils.getDefaultDistanceUnit(this.defaultUnitSystem); } // Return result @@ -298,6 +311,13 @@ export default { }, /** + * Save default unit system + */ + defaultUnitSystem(newValue) { + storage.set('default-unit-system', newValue); + }, + + /** * Save prediction model */ model(newValue) { @@ -324,19 +344,14 @@ export default { selectedTargetSet(newValue) { storage.set('pace-calculator-target-set', newValue); }, - - /** - * Refresh the target sets - */ - editingTargetSets(newValue) { - if (!newValue) { - this.targetSets = storage.get('target-sets', targetUtils.defaultTargetSets); - } - } }, + /** + * (Re)load settings used in multiple calculators + */ activated() { this.targetSets = storage.get('target-sets', targetUtils.defaultTargetSets); + this.defaultUnitSystem = storage.get('default-unit-system', unitUtils.detectDefaultUnitSystem()); }, }; </script> diff --git a/src/views/SplitCalculator.vue b/src/views/SplitCalculator.vue @@ -1,8 +1,17 @@ <template> <div class="calculator"> + <div class="default-units"> + Default units: + <select v-model="defaultUnitSystem" aria-label="Default units"> + <option value="imperial">Miles</option> + <option value="metric">Kilometers</option> + </select> + </div> + <div class="target-set"> Target Set: - <target-set-selector v-model="selectedTargetSet" @targets-updated="reloadTargets"/> + <target-set-selector v-model="selectedTargetSet" @targets-updated="reloadTargets" + :default-unit-system="defaultUnitSystem"/> </div> <div class="output"> @@ -40,7 +49,7 @@ <td> {{ formatDuration(item.pace, 3, 0, true) }} - / {{ distanceUnits[getDefaultDistanceUnit()].symbol }} + / {{ distanceUnits[getDefaultDistanceUnit(defaultUnitSystem)].symbol }} </td> </tr> @@ -75,6 +84,13 @@ export default { data() { return { /** + * The default unit system + * + * Loaded in activate() method + */ + defaultUnitSystem: null, + + /** * The distance units */ distanceUnits: unitUtils.DISTANCE_UNITS, @@ -101,32 +117,27 @@ export default { /** * The default output targets + * + * Loaded in activate() method */ - targetSets: storage.get('target-sets', targetUtils.defaultTargetSets), - - /** - * Whether the target set is being edited - */ - editingTargetSets: false, + targetSets: {}, }; }, watch: { /** - * Save the current selected target set + * Save default unit system */ - selectedTargetSet(newValue) { - storage.set('split-calculator-target-set', newValue); + defaultUnitSystem(newValue) { + storage.set('default-unit-system', newValue); }, /** - * Refresh the target sets + * Save the current selected target set */ - editingTargetSets(newValue) { - if (!newValue) { - this.targetSets = storage.get('target-sets', targetUtils.defaultTargetSets); - } - } + selectedTargetSet(newValue) { + storage.set('split-calculator-target-set', newValue); + }, }, computed: { @@ -157,7 +168,7 @@ export default { // Calculate pace const pace = splitTime / unitUtils.convertDistance(splitDistance, 'meters', - unitUtils.getDefaultDistanceUnit()); + unitUtils.getDefaultDistanceUnit(this.defaultUnitSystem)); // Add row to results array results.push({ @@ -184,8 +195,12 @@ export default { }, }, + /** + * (Re)load settings used in multiple calculators + */ activated() { this.targetSets = storage.get('target-sets', targetUtils.defaultTargetSets); + this.defaultUnitSystem = storage.get('default-unit-system', unitUtils.detectDefaultUnitSystem()); }, }; </script> diff --git a/tests/unit/components/TargetEditor.spec.js b/tests/unit/components/TargetEditor.spec.js @@ -15,6 +15,7 @@ test('addDistanceTarget method should correctly add distance target', async () = { time: 0, result: 'distance' }, ], }, + defaultUnitSystem: 'imperial' }, }); diff --git a/tests/unit/views/PaceCalculator.spec.js b/tests/unit/views/PaceCalculator.spec.js @@ -51,8 +51,8 @@ test('should correctly calculate distances', async () => { // Assert result is correct expect(result).to.deep.equal({ - distanceValue: unitUtils.convertDistance(2, 'miles', unitUtils.getDefaultDistanceUnit()), - distanceUnit: unitUtils.getDefaultDistanceUnit(), + distanceValue: unitUtils.convertDistance(2, 'miles', unitUtils.getDefaultDistanceUnit(unitUtils.detectDefaultUnitSystem())), + distanceUnit: unitUtils.getDefaultDistanceUnit(unitUtils.detectDefaultUnitSystem()), time: 200, result: 'distance', }); diff --git a/tests/unit/views/RaceCalculator.spec.js b/tests/unit/views/RaceCalculator.spec.js @@ -55,8 +55,8 @@ test('should correctly predict race distances', async () => { const prediction = raceUtils.AverageModel.predictDistance(1200, 5000, 2460); expect(result).to.deep.equal({ distanceValue: unitUtils.convertDistance(prediction, 'meters', - unitUtils.getDefaultDistanceUnit()), - distanceUnit: unitUtils.getDefaultDistanceUnit(), + unitUtils.getDefaultDistanceUnit(unitUtils.detectDefaultUnitSystem())), + distanceUnit: unitUtils.getDefaultDistanceUnit(unitUtils.detectDefaultUnitSystem()), time: 2460, result: 'distance', });