Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,29 @@ const MyLoader = () => (
)
```

**Per-rect color overrides**

```jsx
import ContentLoader, { Rect } from 'react-content-loader'

const MyLoader = () => (
<ContentLoader viewBox="0 0 380 70">
<Rect x="0" y="0" rx="5" ry="5" width="70" height="70" />
<Rect
x="80"
y="17"
rx="4"
ry="4"
width="300"
height="13"
foregroundColor="#ddd"
backgroundColor="#f3f4f5"
/>
<Rect x="80" y="40" rx="3" ry="3" width="250" height="10" />
</ContentLoader>
)
```

**Still not clear?** Take a look at this working example at [codesandbox.io](https://codesandbox.io/s/moojk887z9)
Or try the components editable demo hands-on and install it from [bit.dev](https://bit.dev/danilowoz/react-content-loader)

Expand Down
3 changes: 2 additions & 1 deletion src/native/ContentLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as React from 'react'
import { Circle, Path, Rect } from 'react-native-svg'
import { Circle, Path } from 'react-native-svg'

import { Facebook, IContentLoaderProps } from '.'
import Rect from './Rect'
import Svg from './Svg'

const ContentLoader: React.FC<IContentLoaderProps> = props =>
Expand Down
17 changes: 17 additions & 0 deletions src/native/Rect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react'
import { Rect as SvgRect } from 'react-native-svg'

export type IRectProps = React.ComponentProps<typeof SvgRect> & {
foregroundColor?: string
backgroundColor?: string
}

const Rect: React.FC<IRectProps> = ({
foregroundColor,
backgroundColor,
...props
}) => <SvgRect {...props} />

Rect.displayName = 'Rect'

export default Rect
82 changes: 79 additions & 3 deletions src/native/Svg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import Svg, {
ClipPath,
Defs,
LinearGradient,
Rect,
Rect as SvgRect,
Stop,
} from 'react-native-svg'

import uid from '../shared/uid'
import { IContentLoaderProps } from './'
import Rect, { IRectProps } from './Rect'

const AnimatedLinearGradient = Animated.createAnimatedComponent(LinearGradient)

Expand Down Expand Up @@ -85,6 +86,36 @@ class NativeSvg extends Component<IContentLoaderProps> {
...props
} = this.props

const clipChildren: React.ReactNode[] = []
const overrideRects: Array<{
element: React.ReactElement<IRectProps>
foregroundColor: string
backgroundColor: string
idClip: string
idGradient: string
}> = []

React.Children.forEach(children, child => {
if (
isValidElement(child) &&
child.type === Rect &&
(child.props.foregroundColor != null ||
child.props.backgroundColor != null)
) {
const index = overrideRects.length
overrideRects.push({
element: child,
foregroundColor: child.props.foregroundColor ?? foregroundColor,
backgroundColor: child.props.backgroundColor ?? backgroundColor,
idClip: `${this.fixedId}-diff-override-${index}`,
idGradient: `${this.fixedId}-animated-diff-override-${index}`,
})
return
}

clipChildren.push(child)
})

const x1Animation = this.animatedValue.interpolate({
extrapolate: 'clamp',
inputRange: [-1, 2],
Expand All @@ -109,7 +140,7 @@ class NativeSvg extends Component<IContentLoaderProps> {
<Svg style={svgStyle} {...props}>
{beforeMask && isValidElement(beforeMask) ? beforeMask : null}

<Rect
<SvgRect
x="0"
y="0"
width="100%"
Expand All @@ -118,8 +149,26 @@ class NativeSvg extends Component<IContentLoaderProps> {
clipPath={`url(#${this.idGradient})`}
/>

{overrideRects.map(override => (
<SvgRect
key={override.idClip}
x="0"
y="0"
width="100%"
height="100%"
fill={`url(#${override.idClip})`}
clipPath={`url(#${override.idGradient})`}
/>
))}

<Defs>
<ClipPath id={this.idGradient}>{children}</ClipPath>
<ClipPath id={this.idGradient}>{clipChildren}</ClipPath>

{overrideRects.map(override => (
<ClipPath id={override.idGradient} key={override.idGradient}>
{override.element}
</ClipPath>
))}

<AnimatedLinearGradient
id={this.idClip}
Expand All @@ -132,6 +181,33 @@ class NativeSvg extends Component<IContentLoaderProps> {
<Stop offset={0.5} stopColor={foregroundColor} stopOpacity={foregroundOpacity} />
<Stop offset={1} stopColor={backgroundColor} stopOpacity={backgroundOpacity} />
</AnimatedLinearGradient>

{overrideRects.map(override => (
<AnimatedLinearGradient
id={override.idClip}
key={override.idClip}
x1={x1Animation}
x2={x2Animation}
y1={0}
y2={0}
>
<Stop
offset={0}
stopColor={override.backgroundColor}
stopOpacity={backgroundOpacity}
/>
<Stop
offset={0.5}
stopColor={override.foregroundColor}
stopOpacity={foregroundOpacity}
/>
<Stop
offset={1}
stopColor={override.backgroundColor}
stopOpacity={backgroundOpacity}
/>
</AnimatedLinearGradient>
))}
</Defs>
</Svg>
)
Expand Down
5 changes: 4 additions & 1 deletion src/native/__tests__/ContentLoader.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react'
import * as renderer from 'react-test-renderer'
import * as ShallowRenderer from 'react-test-renderer/shallow'
import { Rect as SvgRect } from 'react-native-svg'

import ContentLoader, { Circle, Rect } from '../ContentLoader'

Expand All @@ -18,9 +19,11 @@ describe('ContentLoader', () => {

it('should render custom element', () => {
const rect = customWrapper.findAllByType(Rect)
const svgRect = customWrapper.findAllByType(SvgRect)
const circle = customWrapper.findAllByType(Circle)

expect(rect.length).toBe(3)
expect(rect.length).toBe(2)
expect(svgRect.length).toBe(3)
expect(circle.length).toBe(1)
})
})
Expand Down
56 changes: 56 additions & 0 deletions src/native/__tests__/Svg.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,60 @@ describe('Svg', () => {
}).toThrow('No instances found with props: {"x":"123"}')
})
})

describe('rect color overrides', () => {
it('uses default colors when no overrides are provided', () => {
const wrapperWithDefaults = renderer.create(
<ContentLoader animate={false} foregroundColor="#111" backgroundColor="#222">
<Rect x="0" y="0" width="10" height="10" />
</ContentLoader>
).root

const linearGradient = wrapperWithDefaults.findByType(LinearGradient)
const stops = linearGradient.findAllByType(Stop)

expect(stops[0].props.stopColor).toBe('#222')
expect(stops[1].props.stopColor).toBe('#111')
expect(stops[2].props.stopColor).toBe('#222')
})

it('allows a rect to override colors without affecting others', () => {
const wrapperWithOverrides = renderer.create(
<ContentLoader animate={false} foregroundColor="#111" backgroundColor="#222">
<Rect
x="0"
y="0"
width="10"
height="10"
foregroundColor="#333"
backgroundColor="#444"
/>
<Rect x="20" y="0" width="10" height="10" />
</ContentLoader>
).root

const allLinearGradient =
wrapperWithOverrides.findAllByType(LinearGradient)
const overrideGradient = allLinearGradient.find(gradient =>
gradient.props.id.includes('override')
)
const defaultGradient = allLinearGradient.find(
gradient => !gradient.props.id.includes('override')
)

expect(allLinearGradient.length).toBe(2)
expect(overrideGradient).toBeTruthy()
expect(defaultGradient).toBeTruthy()

const defaultStops = defaultGradient!.findAllByType(Stop)
const overrideStops = overrideGradient!.findAllByType(Stop)

expect(defaultStops[0].props.stopColor).toBe('#222')
expect(defaultStops[1].props.stopColor).toBe('#111')
expect(defaultStops[2].props.stopColor).toBe('#222')
expect(overrideStops[0].props.stopColor).toBe('#444')
expect(overrideStops[1].props.stopColor).toBe('#333')
expect(overrideStops[2].props.stopColor).toBe('#444')
})
})
})
5 changes: 4 additions & 1 deletion src/native/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SvgProps } from 'react-native-svg'

import ContentLoader from './ContentLoader'
import Rect, { IRectProps } from './Rect'

export interface IContentLoaderProps extends SvgProps {
animate?: boolean
Expand All @@ -21,5 +22,7 @@ export { default as Code } from './presets/CodeStyle'
export { default as List } from './presets/ListStyle'
export { default as BulletList } from './presets/BulletListStyle'

export { Circle, Rect, Path } from './ContentLoader'
export { Circle, Path } from './ContentLoader'
export { Rect }
export type { IRectProps }
export default ContentLoader
16 changes: 16 additions & 0 deletions src/web/Rect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as React from 'react'

export interface IRectProps extends React.SVGAttributes<SVGRectElement> {
foregroundColor?: string
backgroundColor?: string
}

const Rect: React.FC<IRectProps> = ({
foregroundColor,
backgroundColor,
...props
}) => <rect {...props} />

Rect.displayName = 'Rect'

export default Rect
Loading