diff --git a/src/Constants.ts b/src/Constants.ts index 6a583c6..63fd75d 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -16,6 +16,7 @@ type _animationDirection = export interface ICustomViewStyle extends ViewStyle { children?: ICustomViewStyle[]; key?: number | string; + component?: JSX.Element; } export interface ISkeletonContentProps { diff --git a/src/SkeletonContent.tsx b/src/SkeletonContent.tsx index 8b9bff5..089330d 100644 --- a/src/SkeletonContent.tsx +++ b/src/SkeletonContent.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { StyleSheet, View } from 'react-native'; +import { StyleSheet, View, ViewStyle } from 'react-native'; import { LinearGradient } from 'expo-linear-gradient'; import Animated, { interpolateNode } from 'react-native-reanimated'; import { @@ -30,16 +30,39 @@ const styles = StyleSheet.create({ position: 'absolute', width: '100%' }, - container: { - alignItems: 'center', - flex: 1, - justifyContent: 'center' - }, gradientChild: { flex: 1 } }); +const childrenToLayout = (children: any, layout: ICustomViewStyle[] = []) => { + React.Children.forEach(children, child => { + const style: ViewStyle = Array.isArray(child.props.style) + ? Object.assign({}, ...child.props.style) + : child.props.style; + + // If children is not View component + // Then assume it is custom layout component + if (child.type?.displayName !== 'View') { + return layout.push({ + ...style, + component: child + }); + } + + if (child.props?.children) { + return layout.push({ + ...style, + children: childrenToLayout(child.props.children) + }); + } + + return layout.push({ ...style }); + }); + + return layout; +}; + const useLayout = () => { const [size, setSize] = useState({ width: 0, height: 0 }); @@ -52,7 +75,7 @@ const useLayout = () => { }; const SkeletonContent: React.FunctionComponent = ({ - containerStyle = styles.container, + containerStyle, easing = DEFAULT_EASING, duration = DEFAULT_DURATION, layout = [], @@ -358,32 +381,33 @@ const SkeletonContent: React.FunctionComponent = ({ childrenItems: any, prefix: string | number = '' ): JSX.Element[] => { - if (bonesLayout && bonesLayout.length > 0) { - const iterator: number[] = new Array(bonesLayout.length).fill(0); - return iterator.map((_, i) => { - // has a nested layout - if (bonesLayout[i].children && bonesLayout[i].children!.length > 0) { - const containerPrefix = bonesLayout[i].key || `bone_container_${i}`; - const { children: childBones, ...layoutStyle } = bonesLayout[i]; - return getBoneContainer( - layoutStyle, - getBones(childBones, [], containerPrefix), - containerPrefix - ); - } - if (animationType === 'pulse' || animationType === 'none') { - return getStaticBone(bonesLayout[i], prefix ? `${prefix}_${i}` : i); - } - return getShiverBone(bonesLayout[i], prefix ? `${prefix}_${i}` : i); - }); - // no layout, matching children's layout + let bones = bonesLayout; + + if (!bones || bones.length === 0) { + bones = childrenToLayout(childrenItems); } - return React.Children.map(childrenItems, (child, i) => { - const styling = child.props.style || {}; + + return bones.map((bone, i) => { + if (bone.component) { + return bone.component; + } + + // has a nested layout + if (bone.children && bone.children!.length > 0) { + const containerPrefix = bone.key || `bone_container_${i}`; + const { children: childBones, ...layoutStyle } = bone; + return getBoneContainer( + layoutStyle, + getBones(childBones, [], containerPrefix), + containerPrefix + ); + } + if (animationType === 'pulse' || animationType === 'none') { - return getStaticBone(styling, i); + return getStaticBone(bone, prefix ? `${prefix}_${i}` : i); } - return getShiverBone(styling, i); + + return getShiverBone(bone, prefix ? `${prefix}_${i}` : i); }); };