From 2cb2b81570d13ba4ab8d7ef3197b057356d29832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Mon, 29 Jun 2026 10:58:09 +0200 Subject: [PATCH 1/3] refactor: non standard component; avatar --- src/components/Avatar/AvatarIcon.tsx | 25 ++++---- src/components/Avatar/AvatarImage.tsx | 17 +++--- src/components/Avatar/AvatarText.tsx | 24 ++++---- src/components/Avatar/utils.ts | 33 ++++++++++ .../__snapshots__/Avatar.test.tsx.snap | 60 ++++++++++++++----- 5 files changed, 110 insertions(+), 49 deletions(-) create mode 100644 src/components/Avatar/utils.ts 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/__tests__/__snapshots__/Avatar.test.tsx.snap b/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap index 9e55666cef..074f5ba05d 100644 --- a/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap @@ -5,8 +5,8 @@ exports[`renders avatar with icon 1`] = ` style={ [ { - "backgroundColor": "rgba(103, 80, 164, 1)", - "borderRadius": 32, + "backgroundColor": "rgba(234, 221, 255, 1)", + "borderRadius": 9999, "height": 64, "width": 64, }, @@ -26,7 +26,7 @@ exports[`renders avatar with icon 1`] = ` style={ [ { - "color": "#ffffff", + "color": "rgba(33, 0, 93, 1)", "fontSize": 38.4, }, [ @@ -56,7 +56,7 @@ exports[`renders avatar with icon and custom background color 1`] = ` [ { "backgroundColor": "#f44336", - "borderRadius": 32, + "borderRadius": 9999, "height": 64, "width": 64, }, @@ -105,8 +105,8 @@ exports[`renders avatar with image 1`] = ` style={ [ { - "backgroundColor": "rgba(103, 80, 164, 1)", - "borderRadius": 32, + "backgroundColor": "rgba(234, 221, 255, 1)", + "borderRadius": 9999, "height": 64, "width": 64, }, @@ -123,7 +123,7 @@ exports[`renders avatar with image 1`] = ` } style={ { - "borderRadius": 32, + "borderRadius": 9999, "height": 64, "width": 64, } @@ -137,8 +137,8 @@ exports[`renders avatar with text 1`] = ` style={ [ { - "backgroundColor": "rgba(103, 80, 164, 1)", - "borderRadius": 32, + "backgroundColor": "rgba(234, 221, 255, 1)", + "borderRadius": 9999, "height": 64, "width": 64, }, @@ -172,7 +172,14 @@ exports[`renders avatar with text 1`] = ` "textAlignVertical": "center", }, { - "color": "#ffffff", + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "color": "rgba(33, 0, 93, 1)", "fontSize": 32, "lineHeight": 64, }, @@ -192,7 +199,7 @@ exports[`renders avatar with text and custom background color 1`] = ` [ { "backgroundColor": "#f44336", - "borderRadius": 32, + "borderRadius": 9999, "height": 64, "width": 64, }, @@ -225,6 +232,13 @@ exports[`renders avatar with text and custom background color 1`] = ` "textAlign": "center", "textAlignVertical": "center", }, + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, { "color": "#ffffff", "fontSize": 32, @@ -245,8 +259,8 @@ exports[`renders avatar with text and custom colors 1`] = ` style={ [ { - "backgroundColor": "rgba(103, 80, 164, 1)", - "borderRadius": 32, + "backgroundColor": "rgba(234, 221, 255, 1)", + "borderRadius": 9999, "height": 64, "width": 64, }, @@ -279,6 +293,13 @@ exports[`renders avatar with text and custom colors 1`] = ` "textAlign": "center", "textAlignVertical": "center", }, + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, { "color": "#FFFFFF", "fontSize": 32, @@ -299,8 +320,8 @@ exports[`renders avatar with text and custom size 1`] = ` style={ [ { - "backgroundColor": "rgba(103, 80, 164, 1)", - "borderRadius": 48, + "backgroundColor": "rgba(234, 221, 255, 1)", + "borderRadius": 9999, "height": 96, "width": 96, }, @@ -334,7 +355,14 @@ exports[`renders avatar with text and custom size 1`] = ` "textAlignVertical": "center", }, { - "color": "#ffffff", + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "color": "rgba(33, 0, 93, 1)", "fontSize": 48, "lineHeight": 96, }, From 6e07fc718bb8fb76f5f4dd524e12df2455a2d0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muzyk?= Date: Mon, 29 Jun 2026 14:22:42 +0200 Subject: [PATCH 2/3] refactor: banner --- src/components/Banner.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) 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} Date: Mon, 29 Jun 2026 15:32:28 +0200 Subject: [PATCH 3/3] refactor: data table --- src/components/DataTable/DataTableCell.tsx | 1 + .../DataTable/DataTablePagination.tsx | 3 +- src/components/DataTable/DataTableTitle.tsx | 20 +- .../__snapshots__/DataTable.test.tsx.snap | 297 ++++++++++-------- 4 files changed, 182 insertions(+), 139 deletions(-) diff --git a/src/components/DataTable/DataTableCell.tsx b/src/components/DataTable/DataTableCell.tsx index ce59206af6..39769f6936 100644 --- a/src/components/DataTable/DataTableCell.tsx +++ b/src/components/DataTable/DataTableCell.tsx @@ -105,6 +105,7 @@ const CellContent = ({ return ( )} { 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}