Files
CBS-Mobile-App/src/components/Card/index.tsx
T

85 lines
2.4 KiB
TypeScript

import React, { memo, type PropsWithChildren } from 'react';
import { StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
import { useTheme } from '../../theme';
/**
* `default` is the resting card and the right answer almost always.
*
* `flat` has a border and no lift — use it for a card that sits inside another
* card, or for a grouped list where a shadow per row would be noise.
*
* `raised` is for the one object on a screen that has to sit above everything
* else, typically the hero balance card. Two raised cards on one screen means
* neither of them is.
*
* `muted` is a well rather than an object — a summary block, a read-only recap.
*
* `glass` is kept only because 13 call sites still pass it. There is no blur
* library in this app, so it never actually rendered as glass; it resolves to
* `default`.
*/
type CardVariant = 'default' | 'flat' | 'raised' | 'muted' | 'glass';
interface CardProps extends PropsWithChildren {
style?: StyleProp<ViewStyle>;
variant?: CardVariant;
/** Drops the built-in padding, for a card that holds an edge-to-edge list. */
noPadding?: boolean;
}
const CardComponent = ({
children,
style,
variant = 'default',
noPadding = false,
}: CardProps) => {
const theme = useTheme();
const { backgroundColor, elevation } = ((): {
backgroundColor: string;
elevation: ViewStyle;
} => {
switch (variant) {
case 'flat':
return { backgroundColor: theme.colors.surface, elevation: theme.elevation.level0 };
case 'raised':
return { backgroundColor: theme.colors.surface, elevation: theme.elevation.level2 };
case 'muted':
return { backgroundColor: theme.colors.surfaceMuted, elevation: theme.elevation.level0 };
case 'glass':
case 'default':
default:
return { backgroundColor: theme.colors.surface, elevation: theme.elevation.level1 };
}
})();
return (
<View
style={[
styles.card,
elevation,
{
backgroundColor,
borderRadius: theme.radius.lg,
padding: noPadding ? 0 : theme.spacing.md,
},
style,
]}
>
{children}
</View>
);
};
const styles = StyleSheet.create({
card: {
borderWidth: 1,
// Clipping is what lets a card hold an edge-to-edge list or image without
// the child squaring off the card's own corners.
overflow: 'hidden',
},
});
export const Card = memo(CardComponent);