BatchCalculator.vue (7190B)
1 <template> 2 <div class="calculator"> 3 <h2>Batch Input</h2> 4 <div class="input"> 5 <pace-input v-model="batchOptions.input" aria-label="Input"/> 6 </div> 7 8 <h2>Batch Options</h2> 9 <div class="input"> 10 <div> 11 Increment: 12 <time-input v-model="batchOptions.increment" label="Duration increment" :show-hours="false"/> 13 × 14 <integer-input v-model="batchOptions.rows" min="1" aria-label="Number of rows"/> 15 </div> 16 <div> 17 Calculator: 18 <select aria-label="Calculator" v-model="batchOptions.calculator"> 19 <option :value="calculators.Calculators.Pace">Pace Calculator</option> 20 <option :value="calculators.Calculators.Race">Race Calculator</option> 21 <option :value="calculators.Calculators.Workout">Workout Calculator</option> 22 </select> 23 </div> 24 </div> 25 26 <details> 27 <summary> 28 <h2>Advanced Options</h2> 29 </summary> 30 <advanced-options-input v-model:batch-options="batchOptions" 31 v-model:globalOptions="globalOptions" v-model:options="calcOptions" 32 v-model:targetSets="targetSets" :type="batchOptions.calculator"/> 33 </details> 34 35 <h2>Batch Results</h2> 36 <double-output-table class="output" :calculate-result="calculateResult" 37 :input-distance="inputDistance" :input-times="inputTimes" :label="batchColumnLabel" 38 :targets="targetSets[calcOptions.selectedTargetSet] ? 39 targetSets[calcOptions.selectedTargetSet].targets : []"/> 40 </div> 41 </template> 42 43 <script setup lang="ts"> 44 import { computed } from 'vue'; 45 46 import * as calculators from '@/core/calculators'; 47 import type { BatchOptions, GlobalOptions, PaceOptions, RaceOptions, TargetResult, 48 WorkoutOptions } from '@/core/calculators'; 49 import * as targetUtils from '@/core/targets'; 50 import { formatDistance } from '@/core/units'; 51 import type { Distance, DistanceTime } from '@/core/units'; 52 53 import AdvancedOptionsInput from '@/components/AdvancedOptionsInput.vue'; 54 import DoubleOutputTable from '@/components/DoubleOutputTable.vue'; 55 import IntegerInput from '@/components/IntegerInput.vue'; 56 import PaceInput from '@/components/PaceInput.vue'; 57 import TimeInput from '@/components/TimeInput.vue'; 58 59 import useStorage from '@/composables/useStorage'; 60 61 /* 62 * The global options 63 */ 64 const globalOptions = useStorage<GlobalOptions>('global-options', calculators.defaultGlobalOptions); 65 66 /* 67 * The batch calculator options 68 */ 69 const batchOptions = useStorage<BatchOptions>('batch-calculator-options', 70 calculators.defaultBatchOptions); 71 72 /* 73 * The options for each calculator 74 */ 75 const paceOptions = useStorage<PaceOptions>('pace-calculator-options', 76 calculators.defaultPaceOptions); 77 const raceOptions = useStorage<RaceOptions>('race-calculator-options', 78 calculators.defaultRaceOptions); 79 const workoutOptions = useStorage<WorkoutOptions>('workout-calculator-options', 80 calculators.defaultWorkoutOptions); 81 82 /* 83 * The target sets for each calculator 84 */ 85 const paceTargetSets = useStorage<targetUtils.StandardTargetSets>('pace-calculator-target-sets', 86 targetUtils.defaultPaceTargetSets); 87 const raceTargetSets = useStorage<targetUtils.StandardTargetSets>('race-calculator-target-sets', 88 targetUtils.defaultRaceTargetSets); 89 const workoutTargetSets = useStorage<targetUtils.WorkoutTargetSets>( 90 'workout-calculator-target-sets', targetUtils.defaultWorkoutTargetSets); 91 92 /* 93 * The input distance 94 */ 95 const inputDistance = computed<Distance>(() => ({ 96 distanceValue: batchOptions.value.input.distanceValue, 97 distanceUnit: batchOptions.value.input.distanceUnit, 98 })); 99 100 /* 101 * The set of input times 102 */ 103 const inputTimes = computed<Array<number>>(() => { 104 const results = []; 105 for (let i = 0; i < batchOptions.value.rows; i++) { 106 results.push(batchOptions.value.input.time + batchOptions.value.increment * i); 107 } 108 return results; 109 }); 110 111 /* 112 * The target sets for the current calculator 113 */ 114 const targetSets = computed<targetUtils.TargetSets>({ 115 get: () => { 116 switch (batchOptions.value.calculator) { 117 case (calculators.Calculators.Pace): { 118 return paceTargetSets.value; 119 } 120 case (calculators.Calculators.Race): { 121 return raceTargetSets.value; 122 } 123 default: 124 case (calculators.Calculators.Workout): { 125 return workoutTargetSets.value; 126 } 127 } 128 }, 129 set: (newValue: targetUtils.TargetSets) => { 130 switch (batchOptions.value.calculator) { 131 case (calculators.Calculators.Pace): { 132 paceTargetSets.value = newValue as targetUtils.StandardTargetSets; 133 break; 134 } 135 case (calculators.Calculators.Race): { 136 raceTargetSets.value = newValue as targetUtils.StandardTargetSets; 137 break; 138 } 139 default: 140 case (calculators.Calculators.Workout): { 141 workoutTargetSets.value = newValue as targetUtils.WorkoutTargetSets; 142 break; 143 } 144 } 145 }, 146 }); 147 148 /* 149 * The options for the current calculator 150 */ 151 const calcOptions = computed<PaceOptions | RaceOptions | WorkoutOptions>({ 152 get: () => { 153 switch (batchOptions.value.calculator) { 154 case (calculators.Calculators.Pace): { 155 return paceOptions.value; 156 } 157 case (calculators.Calculators.Race): { 158 return raceOptions.value; 159 } 160 default: 161 case (calculators.Calculators.Workout): { 162 return workoutOptions.value; 163 } 164 } 165 }, 166 set: (newValue: PaceOptions | RaceOptions | WorkoutOptions) => { 167 switch(batchOptions.value.calculator) { 168 case (calculators.Calculators.Pace): { 169 paceOptions.value = newValue as PaceOptions; 170 break; 171 } 172 case (calculators.Calculators.Race): { 173 raceOptions.value = newValue as RaceOptions; 174 break; 175 } 176 default: 177 case (calculators.Calculators.Workout): { 178 workoutOptions.value = newValue as WorkoutOptions; 179 break; 180 } 181 } 182 }, 183 }); 184 185 /* 186 * The appropriate calculate_results function for the current calculator 187 */ 188 const calculateResult = computed<(x: DistanceTime, y: targetUtils.Target) => TargetResult>(() => { 189 switch(batchOptions.value.calculator) { 190 case (calculators.Calculators.Pace): { 191 return (x,y) => calculators.calculatePaceResults(x, y, globalOptions.value.defaultUnitSystem, 192 false); 193 } 194 case (calculators.Calculators.Race): { 195 return (x,y) => calculators.calculateRaceResults(x, y, 196 globalOptions.value.racePredictionOptions, globalOptions.value.defaultUnitSystem, false); 197 } 198 default: 199 case (calculators.Calculators.Workout): { 200 return (x,y) => calculators.calculateWorkoutResults(x, y as targetUtils.WorkoutTarget, 201 globalOptions.value.racePredictionOptions, workoutOptions.value.customTargetNames, false); 202 } 203 } 204 }); 205 206 /* 207 * The label to render for the batch column 208 */ 209 const batchColumnLabel = computed<string>(() => { 210 if (batchOptions.value.calculator == calculators.Calculators.Workout && 211 (calcOptions.value as WorkoutOptions).customTargetNames && batchOptions.value.label) { 212 return batchOptions.value.label; 213 } else { 214 return formatDistance(batchOptions.value.input, false); 215 } 216 }); 217 </script> 218 219 <style scoped> 220 @import '@/assets/target-calculator.css'; 221 </style>