From 36319dc5412ec68d5951eea33ccc2e913b4808d3 Mon Sep 17 00:00:00 2001 From: Pontus Abrahamsson Date: Wed, 20 Dec 2023 17:21:04 +0100 Subject: [PATCH] Animate last month --- .../src/actions/share-file-action.ts | 2 - .../dashboard/src/components/charts/chart.tsx | 15 +- apps/dashboard/src/components/counter.tsx | 104 ++++++++ apps/dashboard/src/styles/globals.css | 231 +++++++++++++++++- 4 files changed, 343 insertions(+), 9 deletions(-) create mode 100644 apps/dashboard/src/components/counter.tsx diff --git a/apps/dashboard/src/actions/share-file-action.ts b/apps/dashboard/src/actions/share-file-action.ts index 905044cbec..f49e8ca997 100644 --- a/apps/dashboard/src/actions/share-file-action.ts +++ b/apps/dashboard/src/actions/share-file-action.ts @@ -19,7 +19,5 @@ export const shareFileAction = action(shareFileSchema, async (value) => { }, }); - console.log(response); - return response?.data?.signedUrl; }); diff --git a/apps/dashboard/src/components/charts/chart.tsx b/apps/dashboard/src/components/charts/chart.tsx index e53256b69f..3ae78782d4 100644 --- a/apps/dashboard/src/components/charts/chart.tsx +++ b/apps/dashboard/src/components/charts/chart.tsx @@ -1,3 +1,4 @@ +import { Counter } from "@/components/counter"; import { formatAmount } from "@/utils/format"; import { getMetrics } from "@midday/supabase/cached-queries"; import { cn } from "@midday/ui/utils"; @@ -11,16 +12,18 @@ export async function Chart({ value, defaultValue, disabled }) { ? chartData : await getMetrics({ ...defaultValue, ...value, type }); + const lastPeriodAmount = data.result[data.result.length - 1]?.current?.value; + return (

- {formatAmount({ - amount: data.summary.currentTotal || 0, - currency: data.summary.currency, - maximumFractionDigits: 0, - minimumFractionDigits: 0, - })} +

vs{" "} diff --git a/apps/dashboard/src/components/counter.tsx b/apps/dashboard/src/components/counter.tsx new file mode 100644 index 0000000000..00c644e395 --- /dev/null +++ b/apps/dashboard/src/components/counter.tsx @@ -0,0 +1,104 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; + +const CONFIG = { + min: 0, + max: 20000, +}; + +const Character = ({ className, key, value }) => { + return ( + + + 9 + {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((val, index) => { + return {val}; + })} + 0 + + + ); +}; + +const InnerCounter = ({ currency, pad, value, locale }) => { + const padCount = pad + ? CONFIG.max.toFixed(2).toString().length - value.toString().length + : 0; + + const paddedValue = value + .toString() + .padStart(value.toString().length + padCount, "1"); + + let i = 0; + const renderValue = new Intl.NumberFormat(locale, { + style: "currency", + currency, + }) + .format(paddedValue) + .split("") + .map((character, index) => { + if (!Number.isNaN(parseInt(character, 10)) && i < padCount) { + i++; + return "0"; + } + return character; + }) + .join(""); + + return ( +

+
+

+ {renderValue} + +

+
+
+ ); +}; + +export function Counter({ + value: initalValue, + currency, + locale, + lastPeriodAmount = 0, +}) { + const hasRunned = useRef(false); + const [value, setValue] = useState(initalValue - lastPeriodAmount); + + useEffect(() => { + if (!hasRunned.current) { + setValue((prev) => prev + lastPeriodAmount); + hasRunned.current = true; + } + }, [lastPeriodAmount, hasRunned]); + + return ( + <> + + + + ); +} diff --git a/apps/dashboard/src/styles/globals.css b/apps/dashboard/src/styles/globals.css index 1dd962b223..5e5919d719 100644 --- a/apps/dashboard/src/styles/globals.css +++ b/apps/dashboard/src/styles/globals.css @@ -90,4 +90,233 @@ body { 25% { transform: translateX(0.5rem); } 75% { transform: translateX(-0.5rem); } 100% { transform: translateX(0rem); } -} \ No newline at end of file +} + +:root { + --transition: 1; + --line: hsl(0 0% 95% / 0.25); + --basic: ease; + --back: + linear( 0, -0.0077 5.2%, -0.0452 16.98%, -0.0493 22.35%, -0.0418 25.57%, + -0.0258 28.57%, -0.0007 31.42%, 0.0335 34.15%, 0.1242 39.03%, 0.2505 43.65%, + 0.3844 47.35%, 0.656 53.68%, 0.81 58.37%, 0.9282 63.52%, 0.9719 66.23%, + 1.0055 69.04%, 1.0255 71.4%, 1.0396 73.87%, 1.0477 76.48%, 1.05 79.27%, + 1.0419 84.36%, 1.0059 95.49%, 1 ); + --expo: + linear( 0, 0.0053 17.18%, 0.0195 26.59%, 0.0326 30.31%, 0.0506 33.48%, + 0.0744 36.25%, 0.1046 38.71%, 0.1798 42.62%, 0.2846 45.93%, 0.3991 48.37%, + 0.6358 52.29%, 0.765 55.45%, 0.8622 59.3%, 0.8986 61.51%, 0.9279 63.97%, + 0.9481 66.34%, 0.9641 69.01%, 0.9856 75.57%, 0.9957 84.37%, 1 ); + --sine: + linear( 0, 0.007 5.35%, 0.0282 10.75%, 0.0638 16.26%, 0.1144 21.96%, + 0.1833 28.16%, 0.2717 34.9%, 0.6868 62.19%, 0.775 68.54%, 0.8457 74.3%, + 0.9141 81.07%, 0.9621 87.52%, 0.9905 93.8%, 1 ); + --power: + linear( 0, 0.0012 14.95%, 0.0089 22.36%, 0.0297 28.43%, 0.0668 33.43%, + 0.0979 36.08%, 0.1363 38.55%, 0.2373 43.07%, 0.3675 47.01%, 0.5984 52.15%, + 0.7121 55.23%, 0.8192 59.21%, 0.898 63.62%, 0.9297 66.23%, 0.9546 69.06%, + 0.9733 72.17%, 0.9864 75.67%, 0.9982 83.73%, 1 ); + --circ: + linear( -0, 0.0033 5.75%, 0.0132 11.43%, 0.0296 16.95%, 0.0522 22.25%, + 0.0808 27.25%, 0.1149 31.89%, 0.1542 36.11%, 0.1981 39.85%, 0.2779 44.79%, + 0.3654 48.15%, 0.4422 49.66%, 0.5807 50.66%, 0.6769 53.24%, 0.7253 55.37%, + 0.7714 58.01%, 0.8142 61.11%, 0.8536 64.65%, 0.9158 72.23%, 0.9619 80.87%, + 0.9904 90.25%, 1 ); + --bounce: + linear( 0, 0.0039, 0.0157, 0.0352, 0.0625 9.09%, + 0.1407, 0.25, 0.3908, 0.5625, 0.7654, + 1, 0.8907, 0.8125 45.45%, 0.7852, 0.7657, + 0.7539, 0.75, 0.7539, 0.7657, 0.7852, + 0.8125 63.64%, 0.8905, 1 72.73%, 0.9727, 0.9532, + 0.9414, 0.9375, 0.9414, 0.9531, 0.9726, + 1, 0.9883, 0.9844, 0.9883, 1 ); + --elastic: + linear( 0, 0.0009 8.51%, -0.0047 19.22%, 0.0016 22.39%, 0.023 27.81%, + 0.0237 30.08%, 0.0144 31.81%, -0.0051 33.48%, -0.1116 39.25%, -0.1181 40.59%, + -0.1058 41.79%, -0.0455, 0.0701 45.34%, 0.9702 55.19%, 1.0696 56.97%, + 1.0987 57.88%, 1.1146 58.82%, 1.1181 59.83%, 1.1092 60.95%, 1.0057 66.48%, + 0.986 68.14%, 0.9765 69.84%, 0.9769 72.16%, 0.9984 77.61%, 1.0047 80.79%, + 0.9991 91.48%, 1 ); + --ease: var(--back); +} + +.counter h2 { + transform-style: flat; +} + +.counter { + display: grid; + place-items: center; +} + +.counter fieldset .sr-only { + position: absolute; + font-size: var(--font-size); + line-height: var(--line-height); + z-index: 2; + font-variant: tabular-nums; + color: transparent; +} + +.counter fieldset { + --mask-size: 0.25; + /* --font-size: clamp(20px, 4vw + 1rem, 8rem); */ + --font-size: 35px; + --line-height: calc(var(--font-size) * 1.5); + padding: 0; + margin: 0; + border: 0; +} + +.counter legend { + color: hsl(20 80% 50%); + border: 0; + font-size: calc(var(--font-size) * 0.25); +} + +.character { + display: grid; + height: 1lh; + line-height: var(--line-height); + font-variant: tabular-nums; + font-size: var(--font-size); + overflow: hidden; + mask: linear-gradient(transparent, white calc(1lh * var(--mask-size)) calc(100% - (1lh * var(--mask-size))), transparent); + transform-style: flat; +} + +.character__track span { + height: 1lh; + font-weight: 120; + transform-style: flat; +} + +.character, +.character__track span { + /* background: linear-gradient(hsl(0 0% 98%) 50%, hsl(0 0% 45%)); */ + background-color: white; + background-attachment: fixed; + background-clip: text; + color: transparent; + transform-style: flat; +} + +.character { + font-weight: 20; +} + +.character__track { + display: grid; + translate: 0 calc((var(--v) + 1) * (var(--line-height) * -1)); + transition: translate calc(var(--transition) * 1s) var(--ease); +} + +/* .character:first-of-type { + margin-right: 0.2ch; + opacity: 0.75; + font-size: calc(var(--font-size) * 0.8); +} */ + +.character:first-of-type { + /* margin-right: 0.2ch; */ + /* opacity: 0.75; */ + /* font-size: calc(var(--font-size) * 0.8); */ +} + +.characters { + display: flex; + gap: 2px; + transform-style: flat; +} + +.fraction { + font-size: calc(var(--font-size) * 0.75); + opacity: 0.75; + font-weight: 40; + height: var(--line-height); +} + +.fraction .character__track span { + display: flex; + flex-direction: column; + align-items: end; + padding: calc((var(--line-height) - var(--font-size)) * 0.2) 0; +} + +.counter:last-of-type { + position: absolute; + opacity: 0; + pointer-events: none; +} + +.counter:last-of-type [data-value] { + position: relative; +} + +.counter:last-of-type [data-value]::after { + content: attr(data-value); + position: absolute; + bottom: 0%; + left: 0%; + font-family: "Geist Sans", sans-serif; + font-weight: 120; + + background: linear-gradient(hsl(0 0% 98%) 50%, hsl(0 0% 45%)); + background-attachment: fixed; + background-clip: text; + color: transparent; +} + +.counter:last-of-type .fraction[data-value]::after { + display: flex; + flex-direction: column; + align-items: end; + padding: calc((var(--line-height) - var(--font-size)) * 0.2) 0; + height: var(--line-height); +} + +.dg.ac { + z-index: 9999 !important; + transform: translate3d(0, 0, 100vmin); +} + +.counter:first-of-type { + transform: translate3d(0, 0, calc(var(--depth) * 1)); +} + +.counter:last-of-type { + transform: translate3d(0, 0, calc(var(--depth) * -1)); +} + +.counter { + transition: transform 0.5s 2s; +} + +.counter:last-of-type { + transition: transform 0.5s 2s ease, opacity 0.5s 2s steps(1, end), background 0.5s 1s, border-color 0.5s 1s; +} + +.counter:last-of-type .character { + mask: unset; + overflow: visible; +} + +.counter:last-of-type .character__track span { + opacity: 0; +} + +.counter:last-of-type .character--symbol, +.counter:last-of-type .character__track span { + transition: opacity 0.5s; +} +.counter:last-of-type [data-value]::after { + transition: opacity 0.5s; +} +.counter:last-of-type legend { + transition: color 0.5s 1s; +} + + + + +