diff --git a/Components/Callout.tsx b/Components/Callout.tsx new file mode 100644 index 0000000..d79e724 --- /dev/null +++ b/Components/Callout.tsx @@ -0,0 +1,46 @@ +import React, { useMemo } from 'react'; +import { ColorValue, StyleSheet, View } from 'react-native'; +import { useTheme } from '../Themes/ThemeContextProvider'; + +type CalloutProps = { + title: React.ReactNode; + children: React.ReactNode; + backgroundColor?: ColorValue; + borderColor?: ColorValue; + padding?: number; +}; + +function Callout(props: CalloutProps) { + const { currentTheme } = useTheme(); + + // This rule is needed because style sheets in a useMemo are not + // detected by eslint normally. + /* eslint react-native/no-unused-styles: off */ + const styles = useMemo( + () => + StyleSheet.create({ + container: { + backgroundColor: props.backgroundColor ?? currentTheme.background, + borderColor: props.borderColor, + borderRadius: 8, + borderWidth: props.borderColor ? 1 : 0, + padding: props.padding ?? 8, + width: '100%', + }, + title: { + alignItems: 'center', + flexDirection: 'row', + marginBottom: 4, + }, + }), + [props.backgroundColor, props.borderColor, props.padding, currentTheme], + ); + return ( + + {props.title} + {props.children} + + ); +} + +export default Callout; diff --git a/Components/WarningCallout.tsx b/Components/WarningCallout.tsx new file mode 100644 index 0000000..3c34e1b --- /dev/null +++ b/Components/WarningCallout.tsx @@ -0,0 +1,53 @@ +import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons'; +import React from 'react'; +import { StyleSheet, Text, View } from 'react-native'; +import { Colors } from '../Themes/Colors'; +import { useTheme } from '../Themes/ThemeContextProvider'; +import Callout from './Callout'; + +type WarningCalloutProps = { + titleText: string; + body: React.ReactNode; + padding?: number; +}; + +function WarningCallout(props: WarningCalloutProps) { + const { currentTheme } = useTheme(); + + const backgroundColor = currentTheme.warningSubtle; + const borderColor = Colors.warningLightMuted; + + return ( + + + {props.titleText} + + } + backgroundColor={backgroundColor} + borderColor={borderColor} + padding={props.padding} + > + {props.body} + + ); +} + +export default WarningCallout; + +const styles = StyleSheet.create({ + header: { + alignItems: 'center', + flexDirection: 'row', + }, + title: { + fontSize: 18, + fontWeight: '600', + marginLeft: 8, + }, +}); diff --git a/Themes/Themes.ts b/Themes/Themes.ts index 858150f..8628d99 100644 --- a/Themes/Themes.ts +++ b/Themes/Themes.ts @@ -37,6 +37,7 @@ export type Theme = { surfaceContainerHigh: ColorValue; surfaceContainerHighest: ColorValue; success: ColorValue; + warningSubtle: ColorValue; }; export const LightMode: Theme = { @@ -76,6 +77,7 @@ export const LightMode: Theme = { surfaceContainerHigh: Colors.surfaceContainerHighLight, surfaceContainerHighest: Colors.surfaceContainerHighestLight, success: Colors.success, + warningSubtle: Colors.warningLightSubtle, }; export const DarkMode: Theme = { @@ -115,4 +117,5 @@ export const DarkMode: Theme = { surfaceContainerHigh: Colors.surfaceContainerHighDark, surfaceContainerHighest: Colors.surfaceContainerHighestDark, success: Colors.success, + warningSubtle: Colors.warningDarkSubtle, };