UNPKG

@fto-consult/expo-ui

Version:

Bibliothèque de composants UI Expo,react-native

145 lines (133 loc) • 3.58 kB
import React, { useMemo, useState } from "react"; import { Animated, StyleSheet} from "react-native"; import View from "$ecomponents/View"; import PropTypes from "prop-types"; const Types = { TEXT: "text", CIRCLE: "circle", RECT: "rect", }; const Defaults = { type: Types.TEXT, textVerticalPadding: 2, textBorderRadius: 4, skeletonColor: "#9ec0c4", widthVariance: 20, heightVariance: 0, pulseSpeed: 1000, lines: 1, }; export default function Skeleton(props) { const { children, loading, type = Defaults.type, color = Defaults.skeletonColor, width, widthVariance = Defaults.widthVariance, height, heightVariance = Defaults.heightVariance, pulseSpeed = Defaults.pulseSpeed, lines = Defaults.lines, } = props; const [childLayout, setChildLayout] = useState({ height: 0, width: 0, x: 0, y: 0, }); const [fadeAnim] = useState(new Animated.Value(1)); React.useEffect(() => { Animated.loop( Animated.sequence([ Animated.timing(fadeAnim, { toValue: 0.6, duration: pulseSpeed, }), Animated.timing(fadeAnim, { toValue: 1, duration: pulseSpeed, }), ]) ).start(); }, [loading, pulseSpeed]); const styles = useStyles({ loading, color, }); return ( <View style={styles.container}> <View style={styles.skeletonContainer}> {new Array(lines).fill(0).map((_, index) => { let calculatedWidth = calculateVariance( width || childLayout.width, widthVariance ); let calculatedHeight = calculateVariance( height || childLayout.height, heightVariance ); calculatedHeight /= lines; if (type === Types.TEXT) calculatedHeight -= Defaults.textVerticalPadding * 2; return ( <Animated.View key={index} style={[ styles.base, type === Types.TEXT && styles.text, type === Types.RECT && styles.rect, type === Types.CIRCLE && styles.circle, { opacity: loading ? fadeAnim : 0, width: calculatedWidth, height: calculatedHeight, }, ]} /> ); })} </View> </View> ); } Skeleton.propTypes = { type: PropTypes.oneOf(Object.values(Types)), loading: PropTypes.bool.isRequired, color: PropTypes.string, width: PropTypes.number, widthVariance: PropTypes.number, height: PropTypes.number, heightVariance: PropTypes.number, pulseSpeed: PropTypes.number, lines: PropTypes.number, }; const calculateVariance = (size, variance) => { return size - size * (Math.random() * (variance / 100.0)); }; const useStyles = (props) => { const { loading, color } = props; return StyleSheet.create({ container: { position: "relative" }, skeletonContainer: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, }, base: { backgroundColor: color, opacity: loading ? 100 : 0, }, text: { marginTop: Defaults.textVerticalPadding, marginBottom: Defaults.textVerticalPadding, borderRadius: Defaults.textBorderRadius, }, rect: {}, circle: { borderRadius: 1000, }, }); };