import { cloneDeep, isEqual } from 'lodash' import { computed, ComputedGetter, ComputedRef, readonly, Ref, ref, watch, watchEffect } from 'vue' import { formatISO, parseISO } from 'date-fns' 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[]) => Promise<unknown>>(fn: F) { const isProcessing = ref(false) async function wrapper(...args: Parameters<F>): Promise<Awaited<ReturnType<F>>> { isProcessing.value = true try { return (await fn(...args)) as Awaited<ReturnType<F>> } finally { isProcessing.value = false } } return { fn: wrapper, isProcessing } } 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 } export function useFormattedISODate(date: Ref<Date>) { return computed({ get() { return formatISO(date.value, { representation: 'date' }) }, set(dateValue: string) { date.value = parseISO(dateValue) }, }) }