Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions example/src/Examples/CardExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Text,
Switch,
} from 'react-native-paper';
import { PreferencesContext } from '..';
import ScreenWrapper from '../ScreenWrapper';

const CardExample = () => {
Expand All @@ -19,6 +20,8 @@ const CardExample = () => {
const [isOutlined, setIsOutlined] = React.useState(false);
const mode = isOutlined ? 'outlined' : 'elevated';

const preferences = React.useContext(PreferencesContext);

return (
<ScreenWrapper contentContainerStyle={styles.content}>
<View style={styles.preference}>
Expand Down Expand Up @@ -120,6 +123,23 @@ const CardExample = () => {
</Paragraph>
</Card.Content>
</Card>
<Card
style={styles.card}
onPress={() => {
preferences.toggleTheme();
}}
mode={mode}
>
<Card.Title
title="Pressable Theme Change"
left={(props) => <Avatar.Icon {...props} icon="format-paint" />}
/>
<Card.Content>
<Paragraph>
This is pressable card. If you press me, I will switch the theme.
</Paragraph>
</Card.Content>
</Card>
</ScrollView>
</ScreenWrapper>
);
Expand Down
2 changes: 1 addition & 1 deletion example/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const CustomDefaultTheme = {
},
};

const PreferencesContext = React.createContext<any>(null);
export const PreferencesContext = React.createContext<any>(null);

const DrawerContent = () => {
return (
Expand Down
77 changes: 60 additions & 17 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type ElevatedCardProps = {
elevation?: number;
};

type HandlePressType = 'in' | 'out';

type Props = React.ComponentProps<typeof Surface> & {
/**
* Resting elevation of the card which controls the drop shadow.
Expand Down Expand Up @@ -111,30 +113,69 @@ const Card = ({
accessible,
...rest
}: (OutlinedCardProps | ElevatedCardProps) & Props) => {
// Default animated value
const { current: elevation } = React.useRef<Animated.Value>(
new Animated.Value(cardElevation)
);
// Dark adaptive animated value, used in case of toggling the theme,
// it prevents animating the background with native drivers inside Surface
const { current: elevationDarkAdaptive } = React.useRef<Animated.Value>(
new Animated.Value(cardElevation)
);
const { animation, dark, mode, roundness } = theme;

const prevDarkRef = React.useRef<boolean>(dark);
React.useEffect(() => {
prevDarkRef.current = dark;
});

const prevDark = prevDarkRef.current;
const isAdaptiveMode = mode === 'adaptive';
const animationDuration = 150 * animation.scale;

React.useEffect(() => {
/**
* Resets animations values if updating to dark adaptive mode,
* otherwise, any card that is in the middle of animation while
* toggling the theme will stay at that animated value until
* the next press-in
*/
if (dark && isAdaptiveMode && !prevDark) {
elevation.setValue(cardElevation);
elevationDarkAdaptive.setValue(cardElevation);
}
}, [
prevDark,
dark,
isAdaptiveMode,
cardElevation,
elevation,
elevationDarkAdaptive,
]);

const runElevationAnimation = (pressType: HandlePressType) => {
const isPressTypeIn = pressType === 'in';
if (dark && isAdaptiveMode) {
Animated.timing(elevationDarkAdaptive, {
toValue: isPressTypeIn ? 8 : cardElevation,
duration: animationDuration,
useNativeDriver: false,
}).start();
} else {
Animated.timing(elevation, {
toValue: isPressTypeIn ? 8 : cardElevation,
duration: animationDuration,
useNativeDriver: true,
}).start();
}
};

const handlePressIn = () => {
const {
dark,
mode,
animation: { scale },
} = theme;
Animated.timing(elevation, {
toValue: 8,
duration: 150 * scale,
useNativeDriver: !dark || mode === 'exact',
}).start();
runElevationAnimation('in');
};

const handlePressOut = () => {
Animated.timing(elevation, {
toValue: cardElevation,
duration: 150 * animation.scale,
useNativeDriver: !dark || mode === 'exact',
}).start();
runElevationAnimation('out');
};

const total = React.Children.count(children);
Expand All @@ -143,15 +184,17 @@ const Card = ({
? (child.type as any).displayName
: null
);
const borderColor = color(theme.dark ? white : black)
const borderColor = color(dark ? white : black)
.alpha(0.12)
.rgb()
.string();
const computedElevation =
dark && isAdaptiveMode ? elevationDarkAdaptive : elevation;

return (
<Surface
style={[
{ borderRadius: roundness, elevation, borderColor },
{ borderRadius: roundness, elevation: computedElevation, borderColor },
cardMode === 'outlined' ? styles.outlined : {},
style,
]}
Expand Down