diff --git a/src/components/Avatar/AvatarIcon.tsx b/src/components/Avatar/AvatarIcon.tsx index 9c3b813e59..5e0c5d0c00 100644 --- a/src/components/Avatar/AvatarIcon.tsx +++ b/src/components/Avatar/AvatarIcon.tsx @@ -2,13 +2,11 @@ import { StyleSheet, View } from 'react-native'; import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import { white } from '../../theme/colors'; +import { cornerFull } from '../../theme/tokens/sys/shape'; import type { ThemeProp } from '../../types'; -import getContrastingColor from '../../utils/getContrastingColor'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; - -const defaultSize = 64; +import { DEFAULT_SIZE, ICON_SIZE_RATIO, resolveAvatarColors } from './utils'; export type Props = ViewProps & { /** @@ -45,17 +43,18 @@ export type Props = ViewProps & { */ const Avatar = ({ icon, - size = defaultSize, + size = DEFAULT_SIZE, style, theme: themeOverrides, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { backgroundColor = theme.colors?.primary, ...restStyle } = - StyleSheet.flatten(style) || {}; - const textColor = - rest.color ?? - getContrastingColor(backgroundColor, white, 'rgba(0, 0, 0, .54)'); + const { backgroundColor, ...restStyle } = StyleSheet.flatten(style) || {}; + const { background, textColor } = resolveAvatarColors({ + theme, + backgroundColor, + color: rest.color, + }); return ( - + ); }; diff --git a/src/components/Avatar/AvatarImage.tsx b/src/components/Avatar/AvatarImage.tsx index 51330dd70c..17d61354ff 100644 --- a/src/components/Avatar/AvatarImage.tsx +++ b/src/components/Avatar/AvatarImage.tsx @@ -8,11 +8,11 @@ import type { ViewStyle, } from 'react-native'; +import { DEFAULT_SIZE, resolveAvatarColors } from './utils'; import { useInternalTheme } from '../../core/theming'; +import { cornerFull } from '../../theme/tokens/sys/shape'; import type { ThemeProp } from '../../types'; -const defaultSize = 64; - export type AvatarImageSource = | ImageSourcePropType | ((props: { size: number }) => React.ReactNode); @@ -74,7 +74,7 @@ export type Props = ViewProps & { * ``` */ const AvatarImage = ({ - size = defaultSize, + size = DEFAULT_SIZE, source, style, onError, @@ -87,8 +87,9 @@ const AvatarImage = ({ testID, ...rest }: Props) => { - const { colors } = useInternalTheme(themeOverrides); - const { backgroundColor = colors?.primary } = StyleSheet.flatten(style) || {}; + const theme = useInternalTheme(themeOverrides); + const { backgroundColor } = StyleSheet.flatten(style) || {}; + const { background } = resolveAvatarColors({ theme, backgroundColor }); return ( { const theme = useInternalTheme(themeOverrides); - const { backgroundColor = theme.colors?.primary, ...restStyle } = - StyleSheet.flatten(style) || {}; - const textColor = - customColor ?? - getContrastingColor(backgroundColor, white, 'rgba(0, 0, 0, .54)'); + const { backgroundColor, ...restStyle } = StyleSheet.flatten(style) || {}; + const { background, textColor } = resolveAvatarColors({ + theme, + backgroundColor, + color: customColor, + }); const { fontScale } = useWindowDimensions(); return ( @@ -77,8 +76,8 @@ const AvatarText = ({ { width: size, height: size, - borderRadius: size / 2, - backgroundColor, + borderRadius: cornerFull, + backgroundColor: background, }, styles.container, restStyle, @@ -88,6 +87,7 @@ const AvatarText = ({ { + const usingDefault = backgroundColor == null; + const background = backgroundColor ?? theme.colors.primaryContainer; + const textColor = + color ?? + (usingDefault + ? theme.colors.onPrimaryContainer + : getContrastingColor(background, white, 'rgba(0, 0, 0, .54)')); + return { background, textColor }; +}; diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index 4f28cf0390..b7f44d90f5 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Animated, StyleSheet, View } from 'react-native'; +import { Animated, Easing, StyleSheet, View } from 'react-native'; import type { StyleProp, ViewStyle } from 'react-native'; import type { LayoutChangeEvent } from 'react-native'; @@ -14,6 +14,7 @@ import { useInternalTheme } from '../core/theming'; import type { $Omit, $RemoveChildren, Theme, ThemeProp } from '../types'; const DEFAULT_MAX_WIDTH = 960; +const ICON_SIZE = 40; export type Props = $Omit<$RemoveChildren, 'mode'> & { /** @@ -148,7 +149,7 @@ const Banner = ({ const showCallback = useLatestCallback(onShowAnimationFinished); const hideCallback = useLatestCallback(onHideAnimationFinished); - const { scale } = theme.animation; + const { duration, easing } = theme.motion; const opacity = position.interpolate({ inputRange: [0, 0.1, 1], @@ -159,20 +160,22 @@ const Banner = ({ if (visible) { // show Animated.timing(position, { - duration: 250 * scale, + duration: duration.medium1, toValue: 1, useNativeDriver: false, + easing: Easing.bezier(...easing.standard), }).start(showCallback); } else { // hide Animated.timing(position, { - duration: 200 * scale, + duration: duration.short4, toValue: 0, useNativeDriver: false, + easing: Easing.bezier(...easing.standard), }).start(hideCallback); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [visible, position, scale]); + }, [visible, position, duration, easing]); const handleLayout = ({ nativeEvent }: LayoutChangeEvent) => { const { height } = nativeEvent.layout; @@ -221,7 +224,7 @@ const Banner = ({ {icon ? ( - + ) : null} )} { Animated.timing(spinAnim, { toValue: sortDirection === 'ascending' ? 0 : 1, - duration: 150, + duration: duration.short3, + easing: Easing.bezier(...easing.standard), useNativeDriver: true, }).start(); - }, [sortDirection, spinAnim]); + }, [sortDirection, spinAnim, duration, easing]); const textColor = theme.colors.onSurface; @@ -132,6 +141,7 @@ const DataTableTitle = ({ {icon}