Introduces an exportOrdersToCSV function in lib/api-client.ts to allow exporting orders by status as a CSV file. Updates various UI components to use the '•' (bullet) symbol instead of '·' (middle dot) and replaces some emoji/unicode characters for improved consistency and compatibility. Also normalizes the 'use client' directive to include a BOM in many files.
72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useRef } from "react";
|
|
|
|
export interface AnimatedCounterProps {
|
|
value: number;
|
|
duration?: number;
|
|
prefix?: string;
|
|
suffix?: string;
|
|
formatter?: (value: number) => string;
|
|
}
|
|
|
|
export function AnimatedCounter({
|
|
value,
|
|
duration = 2000,
|
|
prefix = "",
|
|
suffix = "",
|
|
formatter
|
|
}: AnimatedCounterProps) {
|
|
const [displayValue, setDisplayValue] = useState(0);
|
|
const startTimeRef = useRef<number | null>(null);
|
|
const frameRef = useRef<number | null>(null);
|
|
|
|
// Easing function for smoother animation
|
|
const easeOutQuad = (t: number): number => t * (2 - t);
|
|
|
|
useEffect(() => {
|
|
// Reset start time on new value
|
|
startTimeRef.current = null;
|
|
|
|
// Cancel any ongoing animation
|
|
if (frameRef.current) {
|
|
cancelAnimationFrame(frameRef.current);
|
|
}
|
|
|
|
// Animation function
|
|
const animate = (timestamp: number) => {
|
|
if (!startTimeRef.current) {
|
|
startTimeRef.current = timestamp;
|
|
}
|
|
|
|
const elapsed = timestamp - startTimeRef.current;
|
|
const progress = Math.min(elapsed / duration, 1);
|
|
const easedProgress = easeOutQuad(progress);
|
|
|
|
const nextValue = Math.floor(easedProgress * value);
|
|
setDisplayValue(nextValue);
|
|
|
|
if (progress < 1) {
|
|
frameRef.current = requestAnimationFrame(animate);
|
|
} else {
|
|
setDisplayValue(value);
|
|
}
|
|
};
|
|
|
|
// Start the animation
|
|
frameRef.current = requestAnimationFrame(animate);
|
|
|
|
return () => {
|
|
if (frameRef.current) {
|
|
cancelAnimationFrame(frameRef.current);
|
|
}
|
|
};
|
|
}, [value, duration]);
|
|
|
|
const formattedValue = formatter
|
|
? formatter(displayValue)
|
|
: `${prefix}${displayValue.toLocaleString()}${suffix}`;
|
|
|
|
return <span>{formattedValue}</span>;
|
|
}
|