Replace react-spring with motion, fix breaking changes in react-window/date-fns-tz/pure-rand.
This commit is contained in:
1 parent
df29c16c00
commit
cc829c8f6f
9 files changed
+132
-110
No files matched your search
+1
-1
@@ -59,7 +59,6 @@
|
||||
"@prisma/adapter-pg": "^7.7.0",
|
||||
"@prisma/client": "^7.7.0",
|
||||
"@prisma/extension-read-replicas": "^0.5.0",
|
||||
"@react-spring/web": "^10.0.3",
|
||||
"@svgr/cli": "^8.1.0",
|
||||
"@tanstack/react-query": "^5.99.0",
|
||||
"@umami/react-zen": "^0.245.0",
|
||||
@@ -89,6 +88,7 @@
|
||||
"kafkajs": "^2.1.0",
|
||||
"lucide-react": "^1.8.0",
|
||||
"maxmind": "^5.0.6",
|
||||
"motion": "^12.38.0",
|
||||
"next": "16.2.4",
|
||||
"next-intl": "4.9.1",
|
||||
"node-fetch": "^3.2.8",
|
||||
|
||||
Generated
+60
-62
@@ -32,9 +32,6 @@ importers:
|
||||
'@prisma/extension-read-replicas':
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0(@prisma/client@7.7.0(prisma@7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@6.0.3))(typescript@6.0.3))
|
||||
'@react-spring/web':
|
||||
specifier: ^10.0.3
|
||||
version: 10.0.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
'@svgr/cli':
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.0(typescript@6.0.3)
|
||||
@@ -122,6 +119,9 @@ importers:
|
||||
maxmind:
|
||||
specifier: ^5.0.6
|
||||
version: 5.0.6
|
||||
motion:
|
||||
specifier: ^12.38.0
|
||||
version: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
next:
|
||||
specifier: 16.2.4
|
||||
version: 16.2.4(@babel/core@7.29.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
@@ -2182,54 +2182,27 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
||||
react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
|
||||
|
||||
'@react-spring/animated@10.0.3':
|
||||
resolution: {integrity: sha512-7MrxADV3vaUADn2V9iYhaIL6iOWRx9nCJjYrsk2AHD2kwPr6fg7Pt0v+deX5RnCDmCKNnD6W5fasiyM8D+wzJQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-spring/animated@9.7.5':
|
||||
resolution: {integrity: sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
||||
'@react-spring/core@10.0.3':
|
||||
resolution: {integrity: sha512-D4DwNO68oohDf/0HG2G0Uragzb9IA1oXblxrd6MZAcBcUQG2EHUWXewjdECMPLNmQvlYVyyBRH6gPxXM5DX7DQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-spring/core@9.7.5':
|
||||
resolution: {integrity: sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
||||
'@react-spring/rafz@10.0.3':
|
||||
resolution: {integrity: sha512-Ri2/xqt8OnQ2iFKkxKMSF4Nqv0LSWnxXT4jXFzBDsHgeeH/cHxTLupAWUwmV9hAGgmEhBmh5aONtj3J6R/18wg==}
|
||||
|
||||
'@react-spring/rafz@9.7.5':
|
||||
resolution: {integrity: sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==}
|
||||
|
||||
'@react-spring/shared@10.0.3':
|
||||
resolution: {integrity: sha512-geCal66nrkaQzUVhPkGomylo+Jpd5VPK8tPMEDevQEfNSWAQP15swHm+MCRG4wVQrQlTi9lOzKzpRoTL3CA84Q==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-spring/shared@9.7.5':
|
||||
resolution: {integrity: sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
||||
'@react-spring/types@10.0.3':
|
||||
resolution: {integrity: sha512-H5Ixkd2OuSIgHtxuHLTt7aJYfhMXKXT/rK32HPD/kSrOB6q6ooeiWAXkBy7L8F3ZxdkBb9ini9zP9UwnEFzWgQ==}
|
||||
|
||||
'@react-spring/types@9.7.5':
|
||||
resolution: {integrity: sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==}
|
||||
|
||||
'@react-spring/web@10.0.3':
|
||||
resolution: {integrity: sha512-ndU+kWY81rHsT7gTFtCJ6mrVhaJ6grFmgTnENipzmKqot4HGf5smPNK+cZZJqoGeDsj9ZsiWPW4geT/NyD484A==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-spring/web@9.7.5':
|
||||
resolution: {integrity: sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==}
|
||||
peerDependencies:
|
||||
@@ -3987,6 +3960,20 @@ packages:
|
||||
fraction.js@5.3.4:
|
||||
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
|
||||
|
||||
framer-motion@12.38.0:
|
||||
resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==}
|
||||
peerDependencies:
|
||||
'@emotion/is-prop-valid': '*'
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/is-prop-valid':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
fs-extra@8.1.0:
|
||||
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
|
||||
engines: {node: '>=6 <7 || >=8'}
|
||||
@@ -4913,6 +4900,26 @@ packages:
|
||||
resolution: {integrity: sha512-7e87vk0DdWT647wjcfEtWeMtjm+zVGqNohN/aeIymbUfjHQ2T4Sx5kM+1irVDBSloNC3CkGKxswdMoo8yhqTDg==}
|
||||
engines: {node: '>=10', npm: '>=6'}
|
||||
|
||||
motion-dom@12.38.0:
|
||||
resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==}
|
||||
|
||||
motion-utils@12.36.0:
|
||||
resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==}
|
||||
|
||||
motion@12.38.0:
|
||||
resolution: {integrity: sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==}
|
||||
peerDependencies:
|
||||
'@emotion/is-prop-valid': '*'
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/is-prop-valid':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@@ -8566,25 +8573,12 @@ snapshots:
|
||||
react: 19.2.5
|
||||
react-dom: 19.2.5(react@19.2.5)
|
||||
|
||||
'@react-spring/animated@10.0.3(react@19.2.5)':
|
||||
dependencies:
|
||||
'@react-spring/shared': 10.0.3(react@19.2.5)
|
||||
'@react-spring/types': 10.0.3
|
||||
react: 19.2.5
|
||||
|
||||
'@react-spring/animated@9.7.5(react@19.2.5)':
|
||||
dependencies:
|
||||
'@react-spring/shared': 9.7.5(react@19.2.5)
|
||||
'@react-spring/types': 9.7.5
|
||||
react: 19.2.5
|
||||
|
||||
'@react-spring/core@10.0.3(react@19.2.5)':
|
||||
dependencies:
|
||||
'@react-spring/animated': 10.0.3(react@19.2.5)
|
||||
'@react-spring/shared': 10.0.3(react@19.2.5)
|
||||
'@react-spring/types': 10.0.3
|
||||
react: 19.2.5
|
||||
|
||||
'@react-spring/core@9.7.5(react@19.2.5)':
|
||||
dependencies:
|
||||
'@react-spring/animated': 9.7.5(react@19.2.5)
|
||||
@@ -8592,35 +8586,16 @@ snapshots:
|
||||
'@react-spring/types': 9.7.5
|
||||
react: 19.2.5
|
||||
|
||||
'@react-spring/rafz@10.0.3': {}
|
||||
|
||||
'@react-spring/rafz@9.7.5': {}
|
||||
|
||||
'@react-spring/shared@10.0.3(react@19.2.5)':
|
||||
dependencies:
|
||||
'@react-spring/rafz': 10.0.3
|
||||
'@react-spring/types': 10.0.3
|
||||
react: 19.2.5
|
||||
|
||||
'@react-spring/shared@9.7.5(react@19.2.5)':
|
||||
dependencies:
|
||||
'@react-spring/rafz': 9.7.5
|
||||
'@react-spring/types': 9.7.5
|
||||
react: 19.2.5
|
||||
|
||||
'@react-spring/types@10.0.3': {}
|
||||
|
||||
'@react-spring/types@9.7.5': {}
|
||||
|
||||
'@react-spring/web@10.0.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
||||
dependencies:
|
||||
'@react-spring/animated': 10.0.3(react@19.2.5)
|
||||
'@react-spring/core': 10.0.3(react@19.2.5)
|
||||
'@react-spring/shared': 10.0.3(react@19.2.5)
|
||||
'@react-spring/types': 10.0.3
|
||||
react: 19.2.5
|
||||
react-dom: 19.2.5(react@19.2.5)
|
||||
|
||||
'@react-spring/web@9.7.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
||||
dependencies:
|
||||
'@react-spring/animated': 9.7.5(react@19.2.5)
|
||||
@@ -10410,6 +10385,15 @@ snapshots:
|
||||
|
||||
fraction.js@5.3.4: {}
|
||||
|
||||
framer-motion@12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
|
||||
dependencies:
|
||||
motion-dom: 12.38.0
|
||||
motion-utils: 12.36.0
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
react: 19.2.5
|
||||
react-dom: 19.2.5(react@19.2.5)
|
||||
|
||||
fs-extra@8.1.0:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
@@ -11524,6 +11508,20 @@ snapshots:
|
||||
|
||||
mmdb-lib@3.0.2: {}
|
||||
|
||||
motion-dom@12.38.0:
|
||||
dependencies:
|
||||
motion-utils: 12.36.0
|
||||
|
||||
motion-utils@12.36.0: {}
|
||||
|
||||
motion@12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
|
||||
dependencies:
|
||||
framer-motion: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
react: 19.2.5
|
||||
react-dom: 19.2.5(react@19.2.5)
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
mysql2@3.15.3:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { type AnimatedComponent, animated } from '@react-spring/web';
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
export const AnimatedDiv: AnimatedComponent<any> = animated.div;
|
||||
export const AnimatedDiv = motion.div;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { formatInTimeZone, utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
|
||||
import { formatInTimeZone, fromZonedTime, toZonedTime } from 'date-fns-tz';
|
||||
import { TIMEZONE_CONFIG, TIMEZONE_LEGACY } from '@/lib/constants';
|
||||
import { getTimezone } from '@/lib/date';
|
||||
import { setItem } from '@/lib/storage';
|
||||
@@ -61,19 +61,19 @@ export function useTimezone() {
|
||||
};
|
||||
|
||||
const toUtc = (date: Date | string | number) => {
|
||||
return zonedTimeToUtc(date, timezone);
|
||||
return fromZonedTime(date, timezone);
|
||||
};
|
||||
|
||||
const fromUtc = (date: Date | string | number) => {
|
||||
return utcToZonedTime(date, timezone);
|
||||
return toZonedTime(date, timezone);
|
||||
};
|
||||
|
||||
const localToUtc = (date: Date | string | number) => {
|
||||
return zonedTimeToUtc(date, localTimeZone);
|
||||
return fromZonedTime(date, localTimeZone);
|
||||
};
|
||||
|
||||
const localFromUtc = (date: Date | string | number) => {
|
||||
return utcToZonedTime(date, localTimeZone);
|
||||
return toZonedTime(date, localTimeZone);
|
||||
};
|
||||
|
||||
const canonicalizeTimezone = (timezone: string): string => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { config, useSpring } from '@react-spring/web';
|
||||
import { Column, Grid, Row, Text } from '@umami/react-zen';
|
||||
import type { ReactNode } from 'react';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import { useSpring, useTransform } from 'motion/react';
|
||||
import { type ReactNode, useEffect } from 'react';
|
||||
import { List, type RowComponentProps } from 'react-window';
|
||||
import { AnimatedDiv } from '@/components/common/AnimatedDiv';
|
||||
import { Empty } from '@/components/common/Empty';
|
||||
import { useMessages, useMobile } from '@/components/hooks';
|
||||
@@ -62,7 +62,7 @@ export function ListTable({
|
||||
);
|
||||
};
|
||||
|
||||
const ListTableRow = ({ index, style }) => {
|
||||
const ListTableRow = ({ index, style }: RowComponentProps) => {
|
||||
return <div style={style}>{getRow(data[index], index)}</div>;
|
||||
};
|
||||
|
||||
@@ -82,14 +82,14 @@ export function ListTable({
|
||||
<Column gap="1">
|
||||
{data?.length === 0 && <Empty />}
|
||||
{virtualize && data.length > 0 ? (
|
||||
<FixedSizeList
|
||||
width="100%"
|
||||
height={itemCount * ITEM_SIZE}
|
||||
itemCount={data.length}
|
||||
itemSize={ITEM_SIZE}
|
||||
>
|
||||
{ListTableRow}
|
||||
</FixedSizeList>
|
||||
<List
|
||||
style={{ width: '100%', height: itemCount * ITEM_SIZE }}
|
||||
defaultHeight={itemCount * ITEM_SIZE}
|
||||
rowCount={data.length}
|
||||
rowHeight={ITEM_SIZE}
|
||||
rowComponent={ListTableRow}
|
||||
rowProps={{}}
|
||||
/>
|
||||
) : (
|
||||
data.map(getRow)
|
||||
)}
|
||||
@@ -108,12 +108,21 @@ const AnimatedRow = ({
|
||||
formatCount,
|
||||
isPhone,
|
||||
}) => {
|
||||
const props = useSpring({
|
||||
width: percent,
|
||||
y: !Number.isNaN(value) ? value : 0,
|
||||
from: { width: 0, y: 0 },
|
||||
config: animate ? config.default : { duration: 0 },
|
||||
});
|
||||
const y = !Number.isNaN(value) ? value : 0;
|
||||
const ySpring = useSpring(0, { stiffness: 170, damping: 26 });
|
||||
const widthSpring = useSpring(0, { stiffness: 170, damping: 26 });
|
||||
const yText = useTransform(ySpring, n => (formatCount ? formatCount(n) : formatLongNumber(n)));
|
||||
const widthText = useTransform(widthSpring, n => `${n?.toFixed?.(0)}%`);
|
||||
|
||||
useEffect(() => {
|
||||
if (animate) {
|
||||
ySpring.set(y);
|
||||
widthSpring.set(percent);
|
||||
} else {
|
||||
ySpring.jump(y);
|
||||
widthSpring.jump(percent);
|
||||
}
|
||||
}, [y, percent, animate, ySpring, widthSpring]);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
@@ -136,9 +145,7 @@ const AnimatedRow = ({
|
||||
>
|
||||
{change}
|
||||
<Text weight="bold">
|
||||
<AnimatedDiv title={props?.y as any}>
|
||||
{formatCount ? props.y?.to(formatCount) : props.y?.to(formatLongNumber)}
|
||||
</AnimatedDiv>
|
||||
<AnimatedDiv title={String(value)}>{yText}</AnimatedDiv>
|
||||
</Text>
|
||||
</Row>
|
||||
{showPercentage && (
|
||||
@@ -151,7 +158,7 @@ const AnimatedRow = ({
|
||||
color="muted"
|
||||
paddingLeft="3"
|
||||
>
|
||||
<AnimatedDiv>{props.width.to(n => `${n?.toFixed?.(0)}%`)}</AnimatedDiv>
|
||||
<AnimatedDiv>{widthText}</AnimatedDiv>
|
||||
</Row>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useSpring } from '@react-spring/web';
|
||||
import { Button, Column, Icon, Row, Text, Tooltip, TooltipTrigger } from '@umami/react-zen';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useSpring, useTransform } from 'motion/react';
|
||||
import { type ReactNode, useEffect } from 'react';
|
||||
import { AnimatedDiv } from '@/components/common/AnimatedDiv';
|
||||
import { Info } from '@/components/icons';
|
||||
import { ChangeLabel } from '@/components/metrics/ChangeLabel';
|
||||
@@ -30,8 +30,20 @@ export const MetricCard = ({
|
||||
}: MetricCardProps) => {
|
||||
const diff = value - change;
|
||||
const pct = diff !== 0 ? ((value - diff) / diff) * 100 : value !== 0 ? 100 : 0;
|
||||
const props = useSpring({ x: Number(value) || 0, from: { x: 0 } });
|
||||
const changeProps = useSpring({ x: Number(pct) || 0, from: { x: 0 } });
|
||||
const x = Number(value) || 0;
|
||||
const p = Number(pct) || 0;
|
||||
const xSpring = useSpring(0, { stiffness: 170, damping: 26 });
|
||||
const pctSpring = useSpring(0, { stiffness: 170, damping: 26 });
|
||||
const valueText = useTransform(xSpring, n => formatValue(n));
|
||||
const pctText = useTransform(pctSpring, n => `${Math.abs(~~n)}%`);
|
||||
|
||||
useEffect(() => {
|
||||
xSpring.set(x);
|
||||
}, [x, xSpring]);
|
||||
|
||||
useEffect(() => {
|
||||
pctSpring.set(p);
|
||||
}, [p, pctSpring]);
|
||||
|
||||
return (
|
||||
<Column
|
||||
@@ -61,11 +73,11 @@ export const MetricCard = ({
|
||||
</Row>
|
||||
)}
|
||||
<Text size="4xl" weight="bold" wrap="nowrap">
|
||||
<AnimatedDiv title={value?.toString()}>{props?.x?.to(x => formatValue(x))}</AnimatedDiv>
|
||||
<AnimatedDiv title={value?.toString()}>{valueText}</AnimatedDiv>
|
||||
</Text>
|
||||
{showChange && (
|
||||
<ChangeLabel value={change} title={formatValue(change)} reverseColors={reverseColors}>
|
||||
<AnimatedDiv>{changeProps?.x?.to(x => `${Math.abs(~~x)}%`)}</AnimatedDiv>
|
||||
<AnimatedDiv>{pctText}</AnimatedDiv>
|
||||
</ChangeLabel>
|
||||
)}
|
||||
</Column>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { config, useSpring } from '@react-spring/web';
|
||||
import { Column, Text } from '@umami/react-zen';
|
||||
import { useRef } from 'react';
|
||||
import { useSpring, useTransform } from 'motion/react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { AnimatedDiv } from '@/components/common/AnimatedDiv';
|
||||
import { Badge } from '@/components/common/Badge';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
@@ -45,10 +45,14 @@ export const PerformanceCard = ({
|
||||
const metricChanged = prevMetricRef.current !== metric;
|
||||
if (metricChanged) prevMetricRef.current = metric;
|
||||
|
||||
const spring = useSpring({
|
||||
value: !Number.isNaN(value) ? value : 0,
|
||||
config: metricChanged ? { duration: 0 } : config.default,
|
||||
});
|
||||
const target = !Number.isNaN(value) ? value : 0;
|
||||
const spring = useSpring(target, { stiffness: 170, damping: 26 });
|
||||
const display = useTransform(spring, n => formatValue(n));
|
||||
|
||||
useEffect(() => {
|
||||
if (metricChanged) spring.jump(target);
|
||||
else spring.set(target);
|
||||
}, [target, metricChanged, spring]);
|
||||
|
||||
return (
|
||||
<Column
|
||||
@@ -67,7 +71,7 @@ export const PerformanceCard = ({
|
||||
{label}
|
||||
</Text>
|
||||
<Text size="4xl" weight="bold" wrap="nowrap">
|
||||
<AnimatedDiv>{spring.value.to(n => formatValue(n))}</AnimatedDiv>
|
||||
<AnimatedDiv>{display}</AnimatedDiv>
|
||||
</Text>
|
||||
<Badge variant={RATING_VARIANTS[rating]}>
|
||||
{t(labels[rating === 'needs-improvement' ? 'needsImprovement' : rating])}
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ import {
|
||||
subWeeks,
|
||||
subYears,
|
||||
} from 'date-fns';
|
||||
import { utcToZonedTime } from 'date-fns-tz';
|
||||
import { toZonedTime } from 'date-fns-tz';
|
||||
import { getDateLocale } from '@/lib/lang';
|
||||
import type { DateRange } from '@/lib/types';
|
||||
|
||||
@@ -164,7 +164,7 @@ export function parseDateRange(
|
||||
}
|
||||
|
||||
const date = new Date();
|
||||
const now = timezone ? utcToZonedTime(date, timezone) : date;
|
||||
const now = timezone ? toZonedTime(date, timezone) : date;
|
||||
const dateLocale = getDateLocale(locale);
|
||||
const { num = 1, unit } = parseDateValue(value);
|
||||
|
||||
|
||||
+4
-3
@@ -1,10 +1,11 @@
|
||||
import prand from 'pure-rand';
|
||||
import { uniformInt } from 'pure-rand/distribution/uniformInt';
|
||||
import { xoroshiro128plus } from 'pure-rand/generator/xoroshiro128plus';
|
||||
|
||||
const seed = Date.now() ^ (Math.random() * 0x100000000);
|
||||
const rng = prand.xoroshiro128plus(seed);
|
||||
const rng = xoroshiro128plus(seed);
|
||||
|
||||
export function random(min: number, max: number) {
|
||||
return prand.unsafeUniformIntDistribution(min, max, rng);
|
||||
return uniformInt(rng, min, max);
|
||||
}
|
||||
|
||||
export function getRandomChars(
|
||||
|
||||
Reference in new issue
Block a user