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}