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,
};