import { shallowRef, toValue } from 'vue'; import type { MaybeRefOrGetter, Ref } from 'vue'; import { timestamp } from '@robonen/stdlib'; import type { ResumableActions } from '@/types'; import { useRafFn } from '@/composables/animation/useRafFn'; import { useIntervalFn } from '@/composables/animation/useIntervalFn'; export interface UseTimestampOptions { /** * Expose pause/resume controls alongside the timestamp * * @default false */ controls?: Controls; /** * Offset added to the timestamp in milliseconds. Accepts a reactive value * (ref or getter); the timestamp recomputes with the latest offset on the * next update. * * @default 0 */ offset?: MaybeRefOrGetter; /** * Start updating immediately * * @default true */ immediate?: boolean; /** * Update strategy. `'requestAnimationFrame'` updates every frame; a number * updates on a fixed interval (ms). * * @default 'requestAnimationFrame' */ interval?: 'requestAnimationFrame' | number; /** * Callback invoked on every update with the current timestamp */ callback?: (timestamp: number) => void; } /** * Pause/resume controls returned when `controls: true`. */ export interface UseTimestampControls extends ResumableActions { /** * The reactive timestamp */ timestamp: Ref; /** * Whether the updater (RAF loop or interval) is currently active */ isActive: Readonly>; } export type UseTimestampReturn = Controls extends true ? UseTimestampControls : Ref; /** * @name useTimestamp * @category Animation * @description Reactive current timestamp, updated via `requestAnimationFrame` * or a fixed interval. * * @param {UseTimestampOptions} [options={}] Options * @returns {Ref | UseTimestampControls} The timestamp, or controls when `controls: true` * * @example * const now = useTimestamp(); * * @example * const { timestamp, pause, resume, isActive } = useTimestamp({ controls: true, interval: 1000 }); * * @example * // Reactive offset * const offset = ref(0); * const now = useTimestamp({ offset }); * * @since 0.0.15 */ export function useTimestamp(options?: UseTimestampOptions): Ref; export function useTimestamp(options: UseTimestampOptions): UseTimestampControls; export function useTimestamp( options: UseTimestampOptions = {}, ): Ref | UseTimestampControls { const { controls = false, offset = 0, immediate = true, interval = 'requestAnimationFrame', callback, } = options; const ts = shallowRef(timestamp() + toValue(offset)); const update = callback ? () => { ts.value = timestamp() + toValue(offset); callback(ts.value); } : () => { ts.value = timestamp() + toValue(offset); }; const resumableControls = interval === 'requestAnimationFrame' ? useRafFn(update, { immediate }) : useIntervalFn(update, interval, { immediate }); if (controls) { return { timestamp: ts, ...resumableControls, }; } return ts; }