- Add chart palette, motion, and z-index CSS vars to globals.css - Replace custom theme-provider with next-themes (dark default) - Extend tailwind.config.ts with heading fonts, spacing (row-compact, row-roomy, sidebar), chart colors, elevation shadows, glow shadows, transition timing, pill border-radius, z-index scale - Update tick-flash animations to match design token spec (480ms) - Add prefers-reduced-motion support for all animations - Create base design-system components: Surface, SurfaceElevated, Divider, DensityProvider/useDensity, Numeric (VND/percent/compact formatting), Signal (up/down/neutral pill) - Add dev-only /dev/tokens showcase route (404 in production) - Update theme-provider tests to match next-themes integration Co-Authored-By: Paperclip <noreply@paperclip.ing>
65 lines
1.6 KiB
TypeScript
65 lines
1.6 KiB
TypeScript
import { cn } from '@/lib/utils';
|
|
|
|
export interface NumericProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
/** The numeric value to display. */
|
|
value: number;
|
|
/** Format style. Default 'vnd'. */
|
|
format?: 'vnd' | 'percent' | 'decimal' | 'compact';
|
|
/** Number of fraction digits for percent/decimal. Default 1 for percent, 0 for vnd. */
|
|
fractionDigits?: number;
|
|
}
|
|
|
|
const vndFormatter = new Intl.NumberFormat('vi-VN', {
|
|
style: 'currency',
|
|
currency: 'VND',
|
|
maximumFractionDigits: 0,
|
|
});
|
|
|
|
const compactFormatter = new Intl.NumberFormat('vi-VN', {
|
|
notation: 'compact',
|
|
maximumFractionDigits: 1,
|
|
});
|
|
|
|
function formatValue(
|
|
value: number,
|
|
format: NumericProps['format'],
|
|
fractionDigits?: number,
|
|
): string {
|
|
switch (format) {
|
|
case 'percent':
|
|
return `${value >= 0 ? '+' : ''}${value.toFixed(fractionDigits ?? 1)}%`;
|
|
case 'decimal':
|
|
return new Intl.NumberFormat('vi-VN', {
|
|
minimumFractionDigits: fractionDigits ?? 0,
|
|
maximumFractionDigits: fractionDigits ?? 2,
|
|
}).format(value);
|
|
case 'compact':
|
|
return compactFormatter.format(value);
|
|
case 'vnd':
|
|
default:
|
|
return vndFormatter.format(value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Numeric display — right-aligned, tabular-nums, formatted for VND/percent.
|
|
* Automatically sets `data-numeric` for global tabular-nums styling.
|
|
*/
|
|
export function Numeric({
|
|
value,
|
|
format = 'vnd',
|
|
fractionDigits,
|
|
className,
|
|
...props
|
|
}: NumericProps) {
|
|
return (
|
|
<span
|
|
data-numeric
|
|
className={cn('text-right font-mono tabular-nums', className)}
|
|
{...props}
|
|
>
|
|
{formatValue(value, format, fractionDigits)}
|
|
</span>
|
|
);
|
|
}
|