mirror of
https://github.com/soconnor0919/beenvoice.git
synced 2026-05-08 09:38:55 -04:00
0e46fdafb2
- Implemented `AdministrationContent` component for managing account roles. - Created `AdministrationPage` to serve as the main entry point for administration tasks. - Added PDF preview functionality with `PdfPreviewFrame` component for invoice generation. - Introduced `InputColor` component for advanced color selection with various formats. - Established color conversion utilities in `color-converter.ts` for handling color formats. - Defined appearance-related schemas and types in `appearance.ts` for consistent theme management.
135 lines
3.3 KiB
TypeScript
135 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
interface UseCountUpOptions {
|
|
/** The target number to count up to */
|
|
end: number;
|
|
/** The starting number (default: 0) */
|
|
start?: number;
|
|
/** Duration of the animation in milliseconds (default: 1000) */
|
|
duration?: number;
|
|
/** Delay before starting the animation in milliseconds (default: 0) */
|
|
delay?: number;
|
|
/** Custom easing function */
|
|
easing?: (t: number) => number;
|
|
/** Whether to format as currency */
|
|
currency?: boolean;
|
|
/** Number of decimal places (default: 0) */
|
|
decimals?: number;
|
|
/** Whether to use number separators (default: true) */
|
|
useGrouping?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Hook for animating numbers with a counting up effect
|
|
*/
|
|
export function useCountUp({
|
|
end,
|
|
start = 0,
|
|
duration = 1000,
|
|
delay = 0,
|
|
easing = (t: number) => t * t * (3 - 2 * t), // smooth step
|
|
currency = false,
|
|
decimals = 0,
|
|
useGrouping = true,
|
|
}: UseCountUpOptions) {
|
|
const [count, setCount] = useState(start);
|
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// Reset when end value changes
|
|
// eslint-disable-next-line react-hooks/set-state-in-effect -- Restart the animation from the configured start value when inputs change.
|
|
setCount(start);
|
|
setIsAnimating(false);
|
|
|
|
const startAnimation = () => {
|
|
setIsAnimating(true);
|
|
const startTime = Date.now();
|
|
const range = end - start;
|
|
|
|
const updateCount = () => {
|
|
const elapsed = Date.now() - startTime;
|
|
const progress = Math.min(elapsed / duration, 1);
|
|
|
|
if (progress < 1) {
|
|
const easedProgress = easing(progress);
|
|
const currentCount = start + range * easedProgress;
|
|
setCount(currentCount);
|
|
requestAnimationFrame(updateCount);
|
|
} else {
|
|
setCount(end);
|
|
setIsAnimating(false);
|
|
}
|
|
};
|
|
|
|
requestAnimationFrame(updateCount);
|
|
};
|
|
|
|
const timeoutId = setTimeout(startAnimation, delay);
|
|
return () => clearTimeout(timeoutId);
|
|
}, [end, start, duration, delay, easing]);
|
|
|
|
// Format the number for display
|
|
const formatNumber = (num: number): string => {
|
|
const options: Intl.NumberFormatOptions = {
|
|
minimumFractionDigits: decimals,
|
|
maximumFractionDigits: decimals,
|
|
useGrouping,
|
|
};
|
|
|
|
if (currency) {
|
|
return new Intl.NumberFormat("en-US", {
|
|
style: "currency",
|
|
currency: "USD",
|
|
...options,
|
|
}).format(num);
|
|
}
|
|
|
|
return new Intl.NumberFormat("en-US", options).format(num);
|
|
};
|
|
|
|
return {
|
|
/** The current animated value */
|
|
count,
|
|
/** Formatted display value */
|
|
displayValue: formatNumber(count),
|
|
/** Whether the animation is currently running */
|
|
isAnimating,
|
|
/** Reset the animation */
|
|
reset: () => {
|
|
setCount(start);
|
|
setIsAnimating(false);
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Preset for currency counting animation
|
|
*/
|
|
export function useCurrencyCountUp(
|
|
end: number,
|
|
options?: Omit<UseCountUpOptions, "currency" | "decimals" | "end">,
|
|
) {
|
|
return useCountUp({
|
|
...options,
|
|
end,
|
|
currency: true,
|
|
decimals: 2,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Preset for integer counting animation
|
|
*/
|
|
export function useIntegerCountUp(
|
|
end: number,
|
|
options?: Omit<UseCountUpOptions, "decimals" | "end">,
|
|
) {
|
|
return useCountUp({
|
|
...options,
|
|
end,
|
|
decimals: 0,
|
|
});
|
|
}
|