import { cloneDeep, isEqual } from 'lodash' import { computed, ComputedGetter, ComputedRef, readonly, Ref, ref, watch, watchEffect } from 'vue' export function computedIter<T>(fn: ComputedGetter<Iterable<T>>): ComputedRef<T[]> { return computed(() => Array.from(fn())) } export const useId = (() => { let _id = 0 return function useId(prefix = 'component') { return readonly(ref(`${prefix}-${_id++}`)) } })() export function useAsyncFunction<F extends (...args: never[]) => ReturnType<F>>( fn: F, ): { isLoading: Ref<boolean>; fn: (...args: Parameters<F>) => ReturnType<F> } { const isLoading = ref(false) function wrapper(...args: Parameters<F>): ReturnType<F> { isLoading.value = true try { return fn(...args) } finally { isLoading.value = false } } return { fn: wrapper, isLoading } } export function useUpdatableState<T>( externalStateRef: Ref<T>, onUpdate: (value: T) => void, clone: (value: T) => T = cloneDeep, ): Ref<T> { const localRef = ref() watchEffect(() => { localRef.value = clone(externalStateRef.value) }) watch( localRef, (newValue) => { if (!isEqual(newValue, externalStateRef.value)) { onUpdate(newValue) } }, { deep: true }, ) return localRef }