running-tools

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

commit 60bce6002bc55339e17e058da596529bd614a49c
parent 6d05ae8af95954bd2ec4af0fd7c5966e87a0997e
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Sat, 21 Jun 2025 18:04:18 -0700

Convert composables to TypeScript

Diffstat:
Msrc/components/PaceInput.vue | 2+-
Msrc/components/RaceOptions.vue | 2+-
Msrc/components/SplitOutputTable.vue | 2+-
Msrc/components/TargetEditor.vue | 2+-
Msrc/components/TargetSetSelector.vue | 2+-
Dsrc/composables/useObjectModel.js | 35-----------------------------------
Asrc/composables/useObjectModel.ts | 38++++++++++++++++++++++++++++++++++++++
Dsrc/composables/useStorage.js | 30------------------------------
Asrc/composables/useStorage.ts | 32++++++++++++++++++++++++++++++++
9 files changed, 75 insertions(+), 70 deletions(-)

diff --git a/src/components/PaceInput.vue b/src/components/PaceInput.vue @@ -48,7 +48,7 @@ const props = defineProps({ // Generate internal ref tied to modelValue prop const emit = defineEmits(['update:modelValue']); -const model = useObjectModel(props, emit, 'modelValue'); +const model = useObjectModel(() => props.modelValue, emit, 'modelValue'); </script> <style scoped> diff --git a/src/components/RaceOptions.vue b/src/components/RaceOptions.vue @@ -36,5 +36,5 @@ const props = defineProps({ // Generate internal ref tied to modelValue prop const emit = defineEmits(['update:modelValue']); -const model = useObjectModel(props, emit, 'modelValue'); +const model = useObjectModel(() => props.modelValue, emit, 'modelValue'); </script> diff --git a/src/components/SplitOutputTable.vue b/src/components/SplitOutputTable.vue @@ -75,7 +75,7 @@ const props = defineProps({ // Generate internal ref tied to modelValue prop const emit = defineEmits(['update:modelValue']); -const model = useObjectModel(props, emit, 'modelValue'); +const model = useObjectModel(() => props.modelValue, emit, 'modelValue'); /** * The target table results diff --git a/src/components/TargetEditor.vue b/src/components/TargetEditor.vue @@ -145,7 +145,7 @@ const props = defineProps({ const emit = defineEmits(['close', 'revert', 'update:modelValue']); // Generate internal ref tied to modelValue prop -const model = useObjectModel(props, emit, 'modelValue'); +const model = useObjectModel(() => props.modelValue, emit, 'modelValue'); /** * Add a new distance based target diff --git a/src/components/TargetSetSelector.vue b/src/components/TargetSetSelector.vue @@ -74,7 +74,7 @@ const props = defineProps({ // Generate internal ref tied to modelValue prop const emit = defineEmits(['update:targetSets']); -const targetSets = useObjectModel(props, emit, 'targetSets'); +const targetSets = useObjectModel(() => props.targetSets, emit, 'targetSets'); /** * The dialog element diff --git a/src/composables/useObjectModel.js b/src/composables/useObjectModel.js @@ -1,35 +0,0 @@ -import { ref, watch } from 'vue'; - -import { deepCopy, deepEqual } from '@/utils/misc'; - -/* - * Generate an internal ref that implements support for v-model with objects - * @param {Object} props The props object - * @param {Object} emit The emit object - * @param {String} name The name of the v-model prop - * @returns {Object} The internal ref - */ -export default function defineObjectModel(props, emit, name) { - /** - * The internal value - */ - const internalValue = ref(deepCopy(props[name])); - - /** - * Update the internal value when the component value changes - */ - watch(() => props[name], (newValue) => { - if (!deepEqual(internalValue.value, newValue)) { - internalValue.value = deepCopy(newValue); - } - }, { deep: true }); - - /** - * Update the component value when the internal value changes - */ - watch(internalValue, (newValue) => { - emit(`update:${name}`, deepCopy(newValue)); - }, { deep: true }); - - return internalValue; -} diff --git a/src/composables/useObjectModel.ts b/src/composables/useObjectModel.ts @@ -0,0 +1,38 @@ +import { ref, watch } from 'vue'; +import type { Ref } from 'vue'; + +import { deepCopy, deepEqual } from '@/utils/misc'; + +/* + * Generate an internal ref that implements support for v-model with objects + * @param {Function} prop A function returning the prop + * @param {Function} emit The emit function + * @param {string} name The name of the v-model prop + * @returns {Ref<object>} The internal ref + */ +export default function defineObjectModel(prop: () => Ref<object>, + emit: (x: string, y: object) => void, + name: string): Ref<object> { + /** + * The internal value + */ + const internalValue = ref(deepCopy(prop())); + + /** + * Update the internal value when the component value changes + */ + watch(prop, (newValue: object) => { + if (!deepEqual(internalValue.value, newValue)) { + internalValue.value = deepCopy(newValue); + } + }, { deep: true }); + + /** + * Update the component value when the internal value changes + */ + watch(internalValue, (newValue: object) => { + emit(`update:${name}`, deepCopy(newValue)); + }, { deep: true }); + + return internalValue; +} diff --git a/src/composables/useStorage.js b/src/composables/useStorage.js @@ -1,30 +0,0 @@ -import { ref, onActivated, watchEffect } from 'vue'; - -import * as storage from '@/utils/storage'; - -/* - * Create a reactive value that is synced with a localStorage item - * @param {String} key The localStorage item's key - * @defaultValue {Object} defaultValue The default value - */ -export default function useStorage(key, defaultValue) { - const clonedDefault = JSON.parse(JSON.stringify(defaultValue)); - const value = ref(clonedDefault); - - // (Re)load value from localStorage - function updateValue() { - let parsedValue = storage.get(key); - if (parsedValue !== null) value.value = parsedValue; - } - updateValue(); - onActivated(updateValue); - - // Save value to localStorage when modified - watchEffect(() => { - if (typeof localStorage !== 'undefined') { - storage.set(key, value.value); - } - }) - - return value -} diff --git a/src/composables/useStorage.ts b/src/composables/useStorage.ts @@ -0,0 +1,32 @@ +import { ref, onActivated, watchEffect } from 'vue'; +import type { Ref } from 'vue'; + +import * as storage from '@/utils/storage'; + +/* + * Create a reactive value that is synced with a localStorage item + * @param {string} key The localStorage item's key + * @param {object} defaultValue The default value + * @returns {Ref<object>} The synchronized ref + */ +export default function useStorage(key: string, defaultValue: object): Ref<object> { + const clonedDefault = JSON.parse(JSON.stringify(defaultValue)); + const value = ref(clonedDefault); + + // (Re)load value from localStorage + function updateValue() { + const parsedValue = storage.get(key); + if (parsedValue !== null) value.value = parsedValue; + } + updateValue(); + onActivated(updateValue); + + // Save value to localStorage when modified + watchEffect(() => { + if (typeof localStorage !== 'undefined') { + storage.set(key, value.value); + } + }) + + return value +}