<!-- Tweened number display (easeOutCubic) for live-updating stats -->
<template>
  <span>{{ formatted }}</span>
</template>

<script setup lang="ts">
import { ref, computed, watch, onUnmounted } from 'vue';

const props = withDefaults(
  defineProps<{ value: number; decimals?: number; duration?: number }>(),
  { decimals: 0, duration: 600 },
);

const display = ref(props.value);
let raf = 0;
let startVal = props.value;
let startTime = 0;
let target = props.value;

function animate(to: number): void {
  cancelAnimationFrame(raf);
  startVal = display.value;
  target = to;
  startTime = performance.now();
  const step = (now: number) => {
    const t = Math.min(1, (now - startTime) / props.duration);
    const eased = 1 - Math.pow(1 - t, 3); // easeOutCubic
    display.value = startVal + (target - startVal) * eased;
    if (t < 1) raf = requestAnimationFrame(step);
    else display.value = target;
  };
  raf = requestAnimationFrame(step);
}

watch(
  () => props.value,
  (v) => {
    if (Number.isFinite(v)) animate(v);
  },
);

const formatted = computed(() =>
  display.value.toLocaleString('fr-FR', {
    minimumFractionDigits: props.decimals,
    maximumFractionDigits: props.decimals,
  }),
);

onUnmounted(() => cancelAnimationFrame(raf));
</script>
