react-native-paper
Version:
Material design for React Native
205 lines (179 loc) • 5.11 kB
text/typescript
import React from 'react';
import type { ColorValue, StyleProp, ViewStyle } from 'react-native';
import { StyleSheet, Animated } from 'react-native';
import overlay from '../../styles/overlay';
import { black, white } from '../../styles/themes/v2/colors';
import type { InternalTheme, ThemeProp } from '../../types';
export type AppbarModes = 'small' | 'medium' | 'large' | 'center-aligned';
const borderStyleProperties = [
'borderRadius',
'borderTopLeftRadius',
'borderTopRightRadius',
'borderBottomRightRadius',
'borderBottomLeftRadius',
];
export const getAppbarBackgroundColor = (
theme: InternalTheme,
elevation: number,
customBackground?: ColorValue,
elevated?: boolean
) => {
const { isV3, dark: isDarkTheme, mode, colors } = theme;
const isAdaptiveMode = mode === 'adaptive';
if (customBackground) {
return customBackground;
}
if (!isV3) {
if (isDarkTheme && isAdaptiveMode) {
return overlay(elevation, colors?.surface);
}
return colors.primary;
}
if (elevated) {
return theme.colors.elevation.level2;
}
return colors.surface;
};
export const getAppbarColor = ({
color,
isDark,
isV3,
}: BaseProps & { color: string }) => {
if (typeof color !== 'undefined') {
return color;
}
if (isDark) {
return white;
}
if (isV3) {
return undefined;
}
return black;
};
export const getAppbarBorders = (
style:
| Animated.Value
| Animated.AnimatedInterpolation<string | number>
| Animated.WithAnimatedObject<ViewStyle>
) => {
const borders: Record<string, number> = {};
for (const property of borderStyleProperties) {
const value = style[property as keyof typeof style];
if (value) {
borders[property] = value;
}
}
return borders;
};
type BaseProps = {
isDark: boolean;
isV3: boolean;
};
type RenderAppbarContentProps = BaseProps & {
children: React.ReactNode;
shouldCenterContent?: boolean;
isV3: boolean;
renderOnly?: (string | boolean)[];
renderExcept?: string[];
mode?: AppbarModes;
theme?: ThemeProp;
};
export const DEFAULT_APPBAR_HEIGHT = 56;
const MD3_DEFAULT_APPBAR_HEIGHT = 64;
export const modeAppbarHeight = {
small: MD3_DEFAULT_APPBAR_HEIGHT,
medium: 112,
large: 152,
'center-aligned': MD3_DEFAULT_APPBAR_HEIGHT,
};
export const modeTextVariant = {
small: 'titleLarge',
medium: 'headlineSmall',
large: 'headlineMedium',
'center-aligned': 'titleLarge',
} as const;
/**
* Filtruje akcje w Appbarze na podstawie właściwości isLeading.
* @param children - Dzieci komponentu Appbar do przefiltrowania
* @param isLeading - Czy filtrować akcje wiodące (true) czy niewiodące (false). Domyślnie false.
* @returns Przefiltrowana tablica elementów React
*/
export const filterAppbarActions = (
children: React.ReactNode,
isLeading = false
) => {
return React.Children.toArray(children).filter((child) => {
if (!React.isValidElement(child)) return false;
return isLeading ? child.props.isLeading : !child.props.isLeading;
});
};
export const renderAppbarContent = ({
children,
isDark,
shouldCenterContent = false,
isV3,
renderOnly,
renderExcept,
mode = 'small',
theme,
}: RenderAppbarContentProps) => {
return React.Children.toArray(children as React.ReactNode | React.ReactNode[])
.filter((child) => child != null && typeof child !== 'boolean')
.filter((child) =>
// @ts-expect-error: TypeScript complains about the type of type but it doesn't matter
renderExcept ? !renderExcept.includes(child.type.displayName) : child
)
.filter((child) =>
// @ts-expect-error: TypeScript complains about the type of type but it doesn't matter
renderOnly ? renderOnly.includes(child.type.displayName) : child
)
.map((child, i) => {
if (
!React.isValidElement(child) ||
![
'Appbar.Content',
'Appbar.Action',
'Appbar.BackAction',
'Tooltip',
].includes(
// @ts-expect-error: TypeScript complains about the type of type but it doesn't matter
child.type.displayName
)
) {
return child;
}
const props: {
color?: string;
style?: StyleProp<ViewStyle>;
mode?: AppbarModes;
theme?: ThemeProp;
} = {
theme,
color: getAppbarColor({ color: child.props.color, isDark, isV3 }),
};
// @ts-expect-error: TypeScript complains about the type of type but it doesn't matter
if (child.type.displayName === 'Appbar.Content') {
props.mode = mode;
props.style = [
isV3
? i === 0 && !shouldCenterContent && styles.v3Spacing
: i !== 0 && styles.v2Spacing,
shouldCenterContent && styles.centerAlignedContent,
child.props.style,
];
props.color;
}
return React.cloneElement(child, props);
});
};
const styles = StyleSheet.create({
centerAlignedContent: {
alignItems: 'center',
},
v2Spacing: {
marginLeft: 8,
},
v3Spacing: {
marginLeft: 12,
},
});