aboutsummaryrefslogtreecommitdiff
path: root/src/components/metrics/MetricCard.tsx
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-01-24 13:09:50 +0000
committerFuwn <[email protected]>2026-01-24 13:09:50 +0000
commit396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch)
treeb9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /src/components/metrics/MetricCard.tsx
downloadumami-main.tar.xz
umami-main.zip
Initial commitHEADmain
Created from https://vercel.com/new
Diffstat (limited to 'src/components/metrics/MetricCard.tsx')
-rw-r--r--src/components/metrics/MetricCard.tsx56
1 files changed, 56 insertions, 0 deletions
diff --git a/src/components/metrics/MetricCard.tsx b/src/components/metrics/MetricCard.tsx
new file mode 100644
index 0000000..d15bcf1
--- /dev/null
+++ b/src/components/metrics/MetricCard.tsx
@@ -0,0 +1,56 @@
+import { useSpring } from '@react-spring/web';
+import { Column, Text } from '@umami/react-zen';
+import { AnimatedDiv } from '@/components/common/AnimatedDiv';
+import { ChangeLabel } from '@/components/metrics/ChangeLabel';
+import { formatNumber } from '@/lib/format';
+
+export interface MetricCardProps {
+ value: number;
+ previousValue?: number;
+ change?: number;
+ label?: string;
+ reverseColors?: boolean;
+ formatValue?: (n: any) => string;
+ showLabel?: boolean;
+ showChange?: boolean;
+}
+
+export const MetricCard = ({
+ value = 0,
+ change = 0,
+ label,
+ reverseColors = false,
+ formatValue = formatNumber,
+ showLabel = true,
+ showChange = false,
+}: MetricCardProps) => {
+ const diff = value - change;
+ const pct = ((value - diff) / diff) * 100;
+ const props = useSpring({ x: Number(value) || 0, from: { x: 0 } });
+ const changeProps = useSpring({ x: Number(pct) || 0, from: { x: 0 } });
+
+ return (
+ <Column
+ justifyContent="center"
+ paddingX="6"
+ paddingY="4"
+ borderRadius="3"
+ backgroundColor
+ border
+ >
+ {showLabel && (
+ <Text weight="bold" wrap="nowrap">
+ {label}
+ </Text>
+ )}
+ <Text size="8" weight="bold" wrap="nowrap">
+ <AnimatedDiv title={value?.toString()}>{props?.x?.to(x => formatValue(x))}</AnimatedDiv>
+ </Text>
+ {showChange && (
+ <ChangeLabel value={change} title={formatValue(change)} reverseColors={reverseColors}>
+ <AnimatedDiv>{changeProps?.x?.to(x => `${Math.abs(~~x)}%`)}</AnimatedDiv>
+ </ChangeLabel>
+ )}
+ </Column>
+ );
+};