template_0205
This commit is contained in:
249
RN_TEMPLATE/app/components/EmptyState.tsx
Normal file
249
RN_TEMPLATE/app/components/EmptyState.tsx
Normal file
@@ -0,0 +1,249 @@
|
||||
import { Image, ImageProps, ImageStyle, StyleProp, TextStyle, View, ViewStyle } from "react-native"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useAppTheme } from "@/theme/context"
|
||||
import type { ThemedStyle } from "@/theme/types"
|
||||
|
||||
import { Button, ButtonProps } from "./Button"
|
||||
import { Text, TextProps } from "./Text"
|
||||
|
||||
const sadFace = require("@assets/images/sad-face.png")
|
||||
|
||||
interface EmptyStateProps {
|
||||
/**
|
||||
* An optional prop that specifies the text/image set to use for the empty state.
|
||||
*/
|
||||
preset?: "generic"
|
||||
/**
|
||||
* Style override for the container.
|
||||
*/
|
||||
style?: StyleProp<ViewStyle>
|
||||
/**
|
||||
* An Image source to be displayed above the heading.
|
||||
*/
|
||||
imageSource?: ImageProps["source"]
|
||||
/**
|
||||
* Style overrides for image.
|
||||
*/
|
||||
imageStyle?: StyleProp<ImageStyle>
|
||||
/**
|
||||
* Pass any additional props directly to the Image component.
|
||||
*/
|
||||
ImageProps?: Omit<ImageProps, "source">
|
||||
/**
|
||||
* The heading text to display if not using `headingTx`.
|
||||
*/
|
||||
heading?: TextProps["text"]
|
||||
/**
|
||||
* Heading text which is looked up via i18n.
|
||||
*/
|
||||
headingTx?: TextProps["tx"]
|
||||
/**
|
||||
* Optional heading options to pass to i18n. Useful for interpolation
|
||||
* as well as explicitly setting locale or translation fallbacks.
|
||||
*/
|
||||
headingTxOptions?: TextProps["txOptions"]
|
||||
/**
|
||||
* Style overrides for heading text.
|
||||
*/
|
||||
headingStyle?: StyleProp<TextStyle>
|
||||
/**
|
||||
* Pass any additional props directly to the heading Text component.
|
||||
*/
|
||||
HeadingTextProps?: TextProps
|
||||
/**
|
||||
* The content text to display if not using `contentTx`.
|
||||
*/
|
||||
content?: TextProps["text"]
|
||||
/**
|
||||
* Content text which is looked up via i18n.
|
||||
*/
|
||||
contentTx?: TextProps["tx"]
|
||||
/**
|
||||
* Optional content options to pass to i18n. Useful for interpolation
|
||||
* as well as explicitly setting locale or translation fallbacks.
|
||||
*/
|
||||
contentTxOptions?: TextProps["txOptions"]
|
||||
/**
|
||||
* Style overrides for content text.
|
||||
*/
|
||||
contentStyle?: StyleProp<TextStyle>
|
||||
/**
|
||||
* Pass any additional props directly to the content Text component.
|
||||
*/
|
||||
ContentTextProps?: TextProps
|
||||
/**
|
||||
* The button text to display if not using `buttonTx`.
|
||||
*/
|
||||
button?: TextProps["text"]
|
||||
/**
|
||||
* Button text which is looked up via i18n.
|
||||
*/
|
||||
buttonTx?: TextProps["tx"]
|
||||
/**
|
||||
* Optional button options to pass to i18n. Useful for interpolation
|
||||
* as well as explicitly setting locale or translation fallbacks.
|
||||
*/
|
||||
buttonTxOptions?: TextProps["txOptions"]
|
||||
/**
|
||||
* Style overrides for button.
|
||||
*/
|
||||
buttonStyle?: ButtonProps["style"]
|
||||
/**
|
||||
* Style overrides for button text.
|
||||
*/
|
||||
buttonTextStyle?: ButtonProps["textStyle"]
|
||||
/**
|
||||
* Called when the button is pressed.
|
||||
*/
|
||||
buttonOnPress?: ButtonProps["onPress"]
|
||||
/**
|
||||
* Pass any additional props directly to the Button component.
|
||||
*/
|
||||
ButtonProps?: ButtonProps
|
||||
}
|
||||
|
||||
interface EmptyStatePresetItem {
|
||||
imageSource: ImageProps["source"]
|
||||
heading: TextProps["text"]
|
||||
content: TextProps["text"]
|
||||
button: TextProps["text"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A component to use when there is no data to display. It can be utilized to direct the user what to do next.
|
||||
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/EmptyState/}
|
||||
* @param {EmptyStateProps} props - The props for the `EmptyState` component.
|
||||
* @returns {JSX.Element} The rendered `EmptyState` component.
|
||||
*/
|
||||
export function EmptyState(props: EmptyStateProps) {
|
||||
const {
|
||||
theme,
|
||||
themed,
|
||||
theme: { spacing },
|
||||
} = useAppTheme()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const EmptyStatePresets = {
|
||||
generic: {
|
||||
imageSource: sadFace,
|
||||
heading: t("emptyStateComponent:generic.heading"),
|
||||
content: t("emptyStateComponent:generic.content"),
|
||||
button: t("emptyStateComponent:generic.button"),
|
||||
} as EmptyStatePresetItem,
|
||||
} as const
|
||||
|
||||
const preset = EmptyStatePresets[props.preset ?? "generic"]
|
||||
|
||||
const {
|
||||
button = preset.button,
|
||||
buttonTx,
|
||||
buttonOnPress,
|
||||
buttonTxOptions,
|
||||
content = preset.content,
|
||||
contentTx,
|
||||
contentTxOptions,
|
||||
heading = preset.heading,
|
||||
headingTx,
|
||||
headingTxOptions,
|
||||
imageSource = preset.imageSource,
|
||||
style: $containerStyleOverride,
|
||||
buttonStyle: $buttonStyleOverride,
|
||||
buttonTextStyle: $buttonTextStyleOverride,
|
||||
contentStyle: $contentStyleOverride,
|
||||
headingStyle: $headingStyleOverride,
|
||||
imageStyle: $imageStyleOverride,
|
||||
ButtonProps,
|
||||
ContentTextProps,
|
||||
HeadingTextProps,
|
||||
ImageProps,
|
||||
} = props
|
||||
|
||||
const isImagePresent = !!imageSource
|
||||
const isHeadingPresent = !!(heading || headingTx)
|
||||
const isContentPresent = !!(content || contentTx)
|
||||
const isButtonPresent = !!(button || buttonTx)
|
||||
|
||||
const $containerStyles = [$containerStyleOverride]
|
||||
const $imageStyles = [
|
||||
$image,
|
||||
(isHeadingPresent || isContentPresent || isButtonPresent) && { marginBottom: spacing.xxxs },
|
||||
$imageStyleOverride,
|
||||
ImageProps?.style,
|
||||
]
|
||||
const $headingStyles = [
|
||||
themed($heading),
|
||||
isImagePresent && { marginTop: spacing.xxxs },
|
||||
(isContentPresent || isButtonPresent) && { marginBottom: spacing.xxxs },
|
||||
$headingStyleOverride,
|
||||
HeadingTextProps?.style,
|
||||
]
|
||||
const $contentStyles = [
|
||||
themed($content),
|
||||
(isImagePresent || isHeadingPresent) && { marginTop: spacing.xxxs },
|
||||
isButtonPresent && { marginBottom: spacing.xxxs },
|
||||
$contentStyleOverride,
|
||||
ContentTextProps?.style,
|
||||
]
|
||||
const $buttonStyles = [
|
||||
(isImagePresent || isHeadingPresent || isContentPresent) && { marginTop: spacing.xl },
|
||||
$buttonStyleOverride,
|
||||
ButtonProps?.style,
|
||||
]
|
||||
|
||||
return (
|
||||
<View style={$containerStyles}>
|
||||
{isImagePresent && (
|
||||
<Image
|
||||
source={imageSource}
|
||||
{...ImageProps}
|
||||
style={$imageStyles}
|
||||
tintColor={theme.colors.palette.neutral900}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isHeadingPresent && (
|
||||
<Text
|
||||
preset="subheading"
|
||||
text={heading}
|
||||
tx={headingTx}
|
||||
txOptions={headingTxOptions}
|
||||
{...HeadingTextProps}
|
||||
style={$headingStyles}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isContentPresent && (
|
||||
<Text
|
||||
text={content}
|
||||
tx={contentTx}
|
||||
txOptions={contentTxOptions}
|
||||
{...ContentTextProps}
|
||||
style={$contentStyles}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isButtonPresent && (
|
||||
<Button
|
||||
onPress={buttonOnPress}
|
||||
text={button}
|
||||
tx={buttonTx}
|
||||
txOptions={buttonTxOptions}
|
||||
textStyle={$buttonTextStyleOverride}
|
||||
{...ButtonProps}
|
||||
style={$buttonStyles}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const $image: ImageStyle = { alignSelf: "center" }
|
||||
const $heading: ThemedStyle<TextStyle> = ({ spacing }) => ({
|
||||
textAlign: "center",
|
||||
paddingHorizontal: spacing.lg,
|
||||
})
|
||||
const $content: ThemedStyle<TextStyle> = ({ spacing }) => ({
|
||||
textAlign: "center",
|
||||
paddingHorizontal: spacing.lg,
|
||||
})
|
||||
Reference in New Issue
Block a user