Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export { default as Provider } from './provider/index'
export { default as Radio } from './radio/index'
export { default as Result } from './result/index'
export { default as SearchBar } from './search-bar/index'
export { default as Skeleton } from './skeleton/index'
export { default as Slider } from './slider/index'
export { default as Stepper } from './stepper/index'
export { default as Steps } from './steps/index'
Expand Down
14 changes: 14 additions & 0 deletions components/skeleton/PropsType.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { StyleProp, ViewStyle } from 'react-native'
import { SkeletonStyle } from './style'

export interface SkeletonProps {
animated?: boolean
style?: StyleProp<ViewStyle>
styles?: Partial<SkeletonStyle>
}

export type SkeletonTitleProps = SkeletonProps

export interface SkeletonParagraphProps extends SkeletonProps {
lineCount?: number
}
62 changes: 62 additions & 0 deletions components/skeleton/Skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useEffect, useRef } from 'react'
import { Animated, StyleProp, View, ViewStyle } from 'react-native'
import { useTheme } from '../style'
import { SkeletonProps } from './PropsType'
import SkeletonStyles from './style'

// Animated skeleton component
const AnimatedSkeleton: React.FC<{
style?: StyleProp<ViewStyle>
animated?: boolean
}> = ({ style, animated }) => {
const opacityValue = useRef(new Animated.Value(0)).current

useEffect(() => {
if (animated) {
const animation = Animated.loop(
Animated.timing(opacityValue, {
toValue: 1,
duration: 1500,
useNativeDriver: true,
}),
)
animation.start()
return () => {
animation.stop()
opacityValue.setValue(0)
}
}
}, [animated, opacityValue])

if (animated) {
const opacity = opacityValue.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [1, 0.25, 1],
})
return <Animated.View style={[style, { opacity }]} />
}

return <View style={style} />
}
Comment thread
lqr131115 marked this conversation as resolved.

const InternalSkeleton: React.FC<SkeletonProps> = (props) => {
const { animated, style, styles: customStyles, ...restProps } = props
const themeStyles = useTheme({
styles: customStyles,
themeStyles: SkeletonStyles,
})

return (
<AnimatedSkeleton
style={[themeStyles.skeleton, style]}
animated={animated}
{...restProps}
/>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)
}

const Skeleton = React.memo(InternalSkeleton) as typeof InternalSkeleton

Skeleton.displayName = 'Skeleton'

export default Skeleton
49 changes: 49 additions & 0 deletions components/skeleton/SkeletonParagraph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react'
import { View } from 'react-native'
import { useTheme } from '../style'
import { SkeletonParagraphProps } from './PropsType'
import InternalSkeleton from './Skeleton'
import SkeletonStyles from './style'

const InternalSkeletonParagraph: React.FC<SkeletonParagraphProps> = (props) => {
const {
animated,
lineCount = 4,
style,
styles: customStyles,
...restProps
} = props

const themeStyles = useTheme({
styles: customStyles,
themeStyles: SkeletonStyles,
})

const _lineCount = typeof lineCount === 'number' ? Math.max(lineCount, 1) : 4
Comment thread
lqr131115 marked this conversation as resolved.
Outdated
return (
<View style={[themeStyles.skeletonParagraph, style]} {...restProps}>
{Array.from({ length: _lineCount }).map((_, index) => {
const isLastLine = index === _lineCount - 1
return (
<InternalSkeleton
key={index}
animated={animated}
style={[
themeStyles.skeletonParagraphLine,
isLastLine && themeStyles.skeletonParagraphLastLine,
]}
styles={customStyles}
/>
)
})}
</View>
)
}

const SkeletonParagraph = React.memo(
InternalSkeletonParagraph,
) as typeof InternalSkeletonParagraph

SkeletonParagraph.displayName = 'SkeletonParagraph'

export default SkeletonParagraph
31 changes: 31 additions & 0 deletions components/skeleton/SkeletonTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import { useTheme } from '../style'
import { SkeletonTitleProps } from './PropsType'
import InternalSkeleton from './Skeleton'
import SkeletonStyles from './style'

const InternalSkeletonTitle: React.FC<SkeletonTitleProps> = (props) => {
const { animated, style, styles: customStyles, ...restProps } = props

const themeStyles = useTheme({
styles: customStyles,
themeStyles: SkeletonStyles,
})

return (
<InternalSkeleton
animated={animated}
style={[themeStyles.skeletonTitle, style]}
styles={customStyles}
{...restProps}
/>
)
}
Comment thread
lqr131115 marked this conversation as resolved.

const SkeletonTitle = React.memo(
InternalSkeletonTitle,
) as typeof InternalSkeletonTitle

SkeletonTitle.displayName = 'SkeletonTitle'

export default SkeletonTitle
Loading