diff --git a/app/components/AnimatedCounter.tsx b/app/components/AnimatedCounter.tsx new file mode 100644 index 0000000..56b8edf --- /dev/null +++ b/app/components/AnimatedCounter.tsx @@ -0,0 +1,28 @@ +"use client"; + +import { AnimatePresence, motion } from "framer-motion"; + +interface Props { + value: number; +} + +export const AnimatedCounter = ({ value }: Props) => { + const digits = String(value).split(""); + + return ( + + {digits.map((digit, index) => ( + + {digit} + + ))} + + ); +}; diff --git a/package.json b/package.json index 9d887f1..50cf455 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "d3": "^7.8.5", "date-fns": "^3.6.0", "drizzle-orm": "^0.33.0", + "framer-motion": "12.0.0-alpha.1", "lucide-react": "^0.381.0", "next": "^15.0.3", "react": "19.0.0-rc.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 948fc6f..f5764d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,9 @@ importers: drizzle-orm: specifier: ^0.33.0 version: 0.33.0(@neondatabase/serverless@0.9.4)(@types/pg@8.11.6)(@types/react@18.3.3)(react@19.0.0-rc.1) + framer-motion: + specifier: 12.0.0-alpha.1 + version: 12.0.0-alpha.1(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1) lucide-react: specifier: ^0.381.0 version: 0.381.0(react@19.0.0-rc.1) @@ -3313,6 +3316,20 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + framer-motion@12.0.0-alpha.1: + resolution: {integrity: sha512-WpMrDfk6I5Q4T/7+LEjQOVbAD5Yb/cGbbV+LLllFEg+dHi8XZ7QecJ9aYS9bn12cWuF7gGy+uqskyAkGTWHs3w==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -4922,9 +4939,6 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -5984,7 +5998,7 @@ snapshots: '@emnapi/runtime@1.3.1': dependencies: - tslib: 2.6.2 + tslib: 2.8.1 optional: true '@esbuild-kit/core-utils@3.3.2': @@ -7034,7 +7048,7 @@ snapshots: '@swc/helpers@0.5.13': dependencies: - tslib: 2.6.2 + tslib: 2.8.1 '@tailwindcss/forms@0.5.9(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.5.4)))': dependencies: @@ -7460,7 +7474,7 @@ snapshots: aria-hidden@1.2.4: dependencies: - tslib: 2.6.2 + tslib: 2.8.1 aria-query@5.1.3: dependencies: @@ -8391,7 +8405,7 @@ snapshots: debug: 4.3.7 enhanced-resolve: 5.15.0 eslint: 9.0.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.0.0))(eslint@9.0.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.0.0) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.0.0) fast-glob: 3.3.2 get-tsconfig: 4.7.2 @@ -8403,7 +8417,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.0.0))(eslint@9.0.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.0.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -8414,7 +8428,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.0.0))(eslint@9.0.0): + eslint-module-utils@2.8.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.0.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -8436,7 +8450,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.0.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.0.0))(eslint@9.0.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.0.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.0.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -8666,6 +8680,13 @@ snapshots: fraction.js@4.3.7: {} + framer-motion@12.0.0-alpha.1(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1): + dependencies: + tslib: 2.8.1 + optionalDependencies: + react: 19.0.0-rc.1 + react-dom: 19.0.0-rc.1(react@19.0.0-rc.1) + fs.realpath@1.0.0: {} fsevents@2.3.2: @@ -10451,8 +10472,6 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tslib@2.6.2: {} - tslib@2.8.1: {} type-check@0.4.0: