pl-fe: @react-spring/web migrations, adapted from mastodon

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-01-04 14:28:24 +01:00
parent c7643d65c5
commit 240e8a0378
12 changed files with 243 additions and 199 deletions

View File

@ -1,13 +1,13 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { spring } from 'react-motion';
import Button from 'pl-fe/components/ui/button';
import HStack from 'pl-fe/components/ui/hstack';
import Stack from 'pl-fe/components/ui/stack';
import OptionalMotion from 'pl-fe/features/ui/util/optional-motion';
import { useCompose } from 'pl-fe/hooks/use-compose';
import Warning from './warning';
interface IClearLinkSuggestion {
composeId: string;
handleAccept: (key: string) => void;
@ -25,9 +25,10 @@ const ClearLinkSuggestion = ({
if (!suggestion) return null;
return (
<OptionalMotion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
{({ opacity, scaleX, scaleY }) => (
<Stack space={1} className='rounded border border-solid border-gray-400 bg-transparent px-2.5 py-2 text-xs text-gray-900 dark:border-gray-800 dark:text-white' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
<Warning
animated
message={
<Stack space={1}>
<span>
<FormattedMessage
id='compose.clear_link_suggestion.body'
@ -52,8 +53,8 @@ const ClearLinkSuggestion = ({
</Button>
</HStack>
</Stack>
)}
</OptionalMotion>
}
/>
);
};

View File

@ -1,17 +1,17 @@
import React from 'react';
import { defineMessages, FormattedMessage } from 'react-intl';
import { spring } from 'react-motion';
import { ignoreHashtagCasingSuggestion } from 'pl-fe/actions/compose';
import { changeSetting } from 'pl-fe/actions/settings';
import Button from 'pl-fe/components/ui/button';
import HStack from 'pl-fe/components/ui/hstack';
import Stack from 'pl-fe/components/ui/stack';
import OptionalMotion from 'pl-fe/features/ui/util/optional-motion';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useCompose } from 'pl-fe/hooks/use-compose';
import toast from 'pl-fe/toast';
import Warning from './warning';
const messages = defineMessages({
hashtagCasingSuggestionsDisabled: { id: 'compose.hashtag_casing_suggestion.disabled', defaultMessage: 'You will no longer receive suggestions about hashtag capitalization.' },
});
@ -41,9 +41,10 @@ const HashtagCasingSuggestion = ({
if (!suggestion) return null;
return (
<OptionalMotion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
{({ opacity, scaleX, scaleY }) => (
<Stack space={1} className='rounded border border-solid border-gray-400 bg-transparent px-2.5 py-2 text-xs text-gray-900 dark:border-gray-800 dark:text-white' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
<Warning
animated
message={
<Stack space={1}>
<span>
<FormattedMessage
id='compose.hashtag_casing_suggestion.body'
@ -68,8 +69,8 @@ const HashtagCasingSuggestion = ({
</Button>
</HStack>
</Stack>
)}
</OptionalMotion>
}
/>
);
};

View File

@ -1,7 +1,7 @@
import { animated, useSpring } from '@react-spring/web';
import React from 'react';
import { spring } from 'react-motion';
import Motion from '../../ui/util/optional-motion';
import { useSettings } from 'pl-fe/stores/settings';
interface IWarning {
message: React.ReactNode;
@ -9,17 +9,29 @@ interface IWarning {
}
/** Warning message displayed in ComposeForm. */
const Warning: React.FC<IWarning> = ({ message, animated }) => {
const Warning: React.FC<IWarning> = ({ message, animated: animate }) => {
const { reduceMotion } = useSettings();
const styles = useSpring({
from: {
opacity: 0,
transform: 'scale(0.85, 0.75)',
},
to: {
opacity: 1,
transform: 'scale(1, 1)',
},
immediate: !animate || reduceMotion,
});
const className = 'rounded border border-solid border-gray-400 bg-transparent px-2.5 py-2 text-xs text-gray-900 dark:border-gray-800 dark:text-white';
if (animated) return (
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
{({ opacity, scaleX, scaleY }) => (
<div className={className} style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
{message}
</div>
)}
</Motion>
if (!message) return null;
if (animate) return (
<animated.div className={className} style={styles}>
{message}
</animated.div>
);
return (

View File

@ -1,16 +0,0 @@
import React from 'react';
import { Motion, MotionProps } from 'react-motion';
import { useSettings } from 'pl-fe/stores/settings';
import ReducedMotion from './reduced-motion';
const OptionalMotion = (props: MotionProps) => {
const { reduceMotion } = useSettings();
return (
reduceMotion ? <ReducedMotion {...props} /> : <Motion {...props} />
);
};
export default OptionalMotion;

View File

@ -1,31 +0,0 @@
// Like react-motion's Motion, but reduces all animations to cross-fades
// for the benefit of users with motion sickness.
import React from 'react';
import { Motion, MotionProps } from 'react-motion';
const stylesToKeep = ['opacity', 'backgroundOpacity'];
const extractValue = (value: any) => {
// This is either an object with a "val" property or it's a number
return (typeof value === 'object' && value && 'val' in value) ? value.val : value;
};
const ReducedMotion: React.FC<MotionProps> = ({ style = {}, defaultStyle = {}, children }) => {
Object.keys(style).forEach(key => {
if (stylesToKeep.includes(key)) {
return;
}
// If it's setting an x or height or scale or some other value, we need
// to preserve the end-state value without actually animating it
style[key] = defaultStyle[key] = extractValue(style[key]);
});
return (
<Motion style={style} defaultStyle={defaultStyle}>
{children}
</Motion>
);
};
export default ReducedMotion;