mirror of
https://github.com/zhigang1992/react-content-loader.git
synced 2026-01-12 22:50:00 +08:00
feat(codebase): react native support (#153)
re #148, re #137, re #106, re #89, re #16, re #6 * build(rollup): native bundle * feat(native): stylized components * build(typescript): react native check * feat(typing): improves * build(tsconfig): by environment * test(web native): setup * test(native): support * docs(readme): native documentation
This commit is contained in:
6
.eslintrc.json
Normal file
6
.eslintrc.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "react-app",
|
||||
"rules": {
|
||||
"@typescript-eslint/no-angle-bracket-type-assertion": 0 // I don't know wtf it is
|
||||
}
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@ yarn-error.log
|
||||
node_modules
|
||||
dist
|
||||
coverage
|
||||
/native
|
||||
.docz/
|
||||
.rpt2_cache
|
||||
settings.json
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- 9
|
||||
- 10
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
install:
|
||||
- npm install
|
||||
- npm install -g codecov
|
||||
script:
|
||||
- npm run test
|
||||
- npm run build
|
||||
after_success:
|
||||
- npm run coverage
|
||||
- npm run release
|
||||
|
||||
206
README.md
206
README.md
@@ -5,26 +5,28 @@
|
||||
<img width="400" alt="Example's react-content-loader" src="https://user-images.githubusercontent.com/4838076/34308760-ec55df82-e735-11e7-843b-2e311fa7b7d0.gif" />
|
||||
</p>
|
||||
|
||||
SVG-Powered component to easily create placeholder loadings (like Facebook's cards loading).
|
||||
SVG-Powered component to easily create placeholder loadings (like Facebook's cards loading).
|
||||
|
||||
## Features
|
||||
|
||||
- :gear: **Customizable:** Feel free to change the colors, speed, sizes and even **RTL**;
|
||||
- :ok_hand: **Plug and play:** with many presets to use, see the [examples](#examples);
|
||||
- :pencil2: **DIY:** use the [create-content-loader](https://danilowoz.github.io/create-content-loader/) to create your own custom loaders easily;
|
||||
- ⚛️ **Lightweight:** only **1.4kB** gzipped and **0 dependencies**;
|
||||
- :gear: **Customizable:** Feel free to change the colors, speed, sizes and even **RTL**;
|
||||
- :ok_hand: **Plug and play:** with many presets to use, see the [examples](#examples);
|
||||
- :pencil2: **DIY:** use the [create-content-loader](https://danilowoz.github.io/create-content-loader/) to create your own custom loaders easily;
|
||||
- 📱 **React Native support**: same API, as same powerful features;
|
||||
- ⚛️ **Really lightweight:** less than **2kB** and **0 dependencies** for web version;
|
||||
|
||||
## Index
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Usage](#usage)
|
||||
- [Native](#native)
|
||||
- [Options](#options)
|
||||
- [Examples](#examples)
|
||||
- [Similar packages](#similar-packages)
|
||||
- [Similar packages](#similar-packages)
|
||||
- [Development](#development)
|
||||
- [Known Issues](#known-issues)
|
||||
- [Known Issues](#known-issues)
|
||||
|
||||
## Getting Started
|
||||
## Getting Started
|
||||
|
||||
```sh
|
||||
npm i react-content-loader --save
|
||||
@@ -34,13 +36,13 @@ npm i react-content-loader --save
|
||||
yarn add react-content-loader
|
||||
```
|
||||
|
||||
CDN from [JSDELIVR](https://www.jsdelivr.com/package/npm/react-content-loader)
|
||||
CDN from [JSDELIVR](https://www.jsdelivr.com/package/npm/react-content-loader)
|
||||
|
||||
## Usage
|
||||
|
||||
There are two ways to use it:
|
||||
There are two ways to use it:
|
||||
|
||||
**1. Presets, see the [examples](#examples):**
|
||||
**1. Presets, see the [examples](#examples):**
|
||||
|
||||
```jsx
|
||||
import ContentLoader, { Facebook } from 'react-content-loader'
|
||||
@@ -49,12 +51,12 @@ const MyLoader = () => <ContentLoader />
|
||||
const MyFacebookLoader = () => <Facebook />
|
||||
```
|
||||
|
||||
**2. Custom mode, see the [online tool](https://danilowoz.github.io/create-react-content-loader/)**
|
||||
**2. Custom mode, see the [online tool](https://danilowoz.github.io/create-react-content-loader/)**
|
||||
|
||||
```jsx
|
||||
const MyLoader = () => (
|
||||
<ContentLoader>
|
||||
{/* Only SVG shapes */}
|
||||
{/* Only SVG shapes */}
|
||||
<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" />
|
||||
<rect x="80" y="40" rx="3" ry="3" width="250" height="10" />
|
||||
@@ -65,79 +67,110 @@ const MyLoader = () => (
|
||||
**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)
|
||||
|
||||
## Native
|
||||
|
||||
`react-content-loader` can be used with React Native in the same way as web version with the same import:
|
||||
|
||||
**1. Presets, see the [examples](#examples):**
|
||||
|
||||
```jsx
|
||||
import ContentLoader, { Facebook } from 'react-content-loader/native'
|
||||
|
||||
const MyLoader = () => <ContentLoader />
|
||||
const MyFacebookLoader = () => <Facebook />
|
||||
```
|
||||
|
||||
**2. Custom mode**
|
||||
|
||||
**To create custom loaders there is an important difference:** as React Native doesn't have any native module for SVG components, it's necessary to import the shapes from [react-native-svg](https://github.com/react-native-community/react-native-svg) or use the named export Rect and Circle from `react-content-loader` import:
|
||||
|
||||
```jsx
|
||||
import ContentLoader, { Rect, Circle } from 'react-content-loader/native'
|
||||
|
||||
const MyLoader = () => (
|
||||
<ContentLoader>
|
||||
<Circle cx="30" cy="30" r="30" />
|
||||
<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" />
|
||||
<Rect x="80" y="40" rx="3" ry="3" width="250" height="10" />
|
||||
</ContentLoader>
|
||||
)
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
#### **`animate?: boolean`**
|
||||
|
||||
Defaults to `true`. Opt-out of animations with `false`
|
||||
Defaults to `true`. Opt-out of animations with `false`
|
||||
|
||||
#### **`ariaLabel? string | boolean`**
|
||||
#### **`ariaLabel? string | boolean`** - _Web only_
|
||||
|
||||
Defaults to `Loading interface...`. It's used to describe what element it is. Use `false` to remove.
|
||||
Defaults to `Loading interface...`. It's used to describe what element it is. Use `false` to remove.
|
||||
|
||||
#### **`baseUrl? string`**
|
||||
#### **`baseUrl? string`** - _Web only_
|
||||
|
||||
Required if you're using `<base url="/" />` in your `<head/>`. Defaults to an empty string. This prop is common used as: `<ContentLoader baseUrl={window.location.pathname} />` which will fill the SVG attribute with the relative path. Related [#93](https://github.com/danilowoz/react-content-loader/issues/93).
|
||||
Required if you're using `<base url="/" />` document `<head/>`.
|
||||
Defaults to an empty string. This prop is common used as: `<ContentLoader baseUrl={window.location.pathname} />` which will fill the SVG attribute with the relative path. Related [#93](https://github.com/danilowoz/react-content-loader/issues/93).
|
||||
|
||||
#### **`speed?: number`**
|
||||
|
||||
Defaults to `2`. Animation speed in seconds.
|
||||
Defaults to `2`. Animation speed in seconds.
|
||||
|
||||
#### **`interval?: number`**
|
||||
#### **`interval?: number`** - _Web only_
|
||||
|
||||
Defaults to `0.25`. Interval of time between runs of the animation, as a fraction of the animation speed.
|
||||
Defaults to `0.25`. Interval of time between runs of the animation, as a fraction of the animation speed.
|
||||
|
||||
#### **`className? string`**
|
||||
|
||||
Defaults to an empty string. The classname will be set in the `<svg />` element.
|
||||
Defaults to an empty string. The classname will be set in the `<svg />` element.
|
||||
|
||||
#### **`width? number`**
|
||||
|
||||
Defaults to `400`. It will be set in the viewbox attr in the `<svg />`.
|
||||
Defaults to `400`. It will be set in the viewbox attr in the `<svg />`.
|
||||
|
||||
#### **`height? number`**
|
||||
|
||||
Defaults to `130`. It will be set in the viewbox attr in the `<svg />`.
|
||||
Defaults to `130`. It will be set in the viewbox attr in the `<svg />`.
|
||||
|
||||
#### **`gradientRatio? number`**
|
||||
#### **`gradientRatio? number`** - _Web only_
|
||||
|
||||
Defaults to `2`. Width of the animated gradient as a fraction of the viewbox width.
|
||||
Defaults to `2`. Width of the animated gradient as a fraction of the viewbox width.
|
||||
|
||||
#### **`rtl? boolean`**
|
||||
|
||||
Defaults to `false`. Content right-to-left.
|
||||
Defaults to `false`. Content right-to-left.
|
||||
|
||||
#### **`preserveAspectRatio?: string`**
|
||||
|
||||
Defaults to `xMidYMid meet`. Aspect ratio option of `<svg/>`. See the available options [here](https://github.com/danilowoz/react-content-loader/blob/master/src/interface.ts#L7).
|
||||
Defaults to `xMidYMid meet`. Aspect ratio option of `<svg/>`. See the available options [here](https://github.com/danilowoz/react-content-loader/blob/master/src/interface.ts#L7).
|
||||
|
||||
#### **`primaryColor?: string`**
|
||||
|
||||
Defaults to `#f3f3f3` which is used as background of animation.
|
||||
Defaults to `#f3f3f3` which is used as background of animation.
|
||||
|
||||
#### **`secondaryColor?: string`**
|
||||
|
||||
Defaults to `#ecebeb` which is used as the placeholder/layer of animation.
|
||||
Defaults to `#ecebeb` which is used as the placeholder/layer of animation.
|
||||
|
||||
#### **`primaryOpacity?: string`**
|
||||
#### **`primaryOpacity?: string`** - _Web only_
|
||||
|
||||
Defaults to `1`. Background opacity (0 = transparent, 1 = opaque) used to solve a issue in [Safari](#safari--ios)
|
||||
Defaults to `1`. Background opacity (0 = transparent, 1 = opaque) used to solve a issue in [Safari](#safari--ios)
|
||||
|
||||
#### **`secondaryOpacity?: string`**
|
||||
#### **`secondaryOpacity?: string`** - _Web only_
|
||||
|
||||
Defaults to `1`. Animation opacity (0 = transparent, 1 = opaque) used to solve a issue in [Safari](#safari--ios)
|
||||
Defaults to `1`. Animation opacity (0 = transparent, 1 = opaque) used to solve a issue in [Safari](#safari--ios)
|
||||
|
||||
#### **`style?: React.CSSProperties`**
|
||||
|
||||
Defaults to an empty object.
|
||||
Defaults to an empty object.
|
||||
|
||||
#### **`uniquekey?: string`**
|
||||
#### **`uniquekey?: string`** - _Web only_
|
||||
|
||||
Defaults to random unique id. Use the same value of prop key, that will solve inconsistency on the SSR, see more [here](https://github.com/danilowoz/react-content-loader/issues/78).
|
||||
Defaults to random unique id. Use the same value of prop key, that will solve inconsistency on the SSR, see more [here](https://github.com/danilowoz/react-content-loader/issues/78).
|
||||
|
||||
## Examples
|
||||
|
||||
##### Facebook Style
|
||||
##### Facebook Style
|
||||
|
||||
```jsx
|
||||
import { Facebook } from 'react-content-loader'
|
||||
@@ -145,9 +178,9 @@ import { Facebook } from 'react-content-loader'
|
||||
const MyFacebookLoader = () => <Facebook />
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
##### Instagram Style
|
||||
##### Instagram Style
|
||||
|
||||
```jsx
|
||||
import { Instagram } from 'react-content-loader'
|
||||
@@ -155,9 +188,9 @@ import { Instagram } from 'react-content-loader'
|
||||
const MyInstagramLoader = () => <Instagram />
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
##### Code Style
|
||||
##### Code Style
|
||||
|
||||
```jsx
|
||||
import { Code } from 'react-content-loader'
|
||||
@@ -165,9 +198,9 @@ import { Code } from 'react-content-loader'
|
||||
const MyCodeLoader = () => <Code />
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
##### List Style
|
||||
##### List Style
|
||||
|
||||
```jsx
|
||||
import { List } from 'react-content-loader'
|
||||
@@ -175,9 +208,9 @@ import { List } from 'react-content-loader'
|
||||
const MyListLoader = () => <List />
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
##### Bullet list Style
|
||||
##### Bullet list Style
|
||||
|
||||
```jsx
|
||||
import { BulletList } from 'react-content-loader'
|
||||
@@ -185,12 +218,12 @@ import { BulletList } from 'react-content-loader'
|
||||
const MyBulletListLoader = () => <BulletList />
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
### Custom Style
|
||||
### Custom Style
|
||||
|
||||
For the custom mode, use the
|
||||
[online tool](https://danilowoz.github.io/create-react-content-loader/).
|
||||
For the custom mode, use the
|
||||
[online tool](https://danilowoz.github.io/create-react-content-loader/).
|
||||
|
||||
```jsx
|
||||
const MyLoader = () => (
|
||||
@@ -200,10 +233,14 @@ const MyLoader = () => (
|
||||
primaryColor={'#333'}
|
||||
secondaryColor={'#999'}
|
||||
>
|
||||
{/* Only SVG shapes */}
|
||||
{/* Only SVG shapes */}
|
||||
|
||||
<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" />
|
||||
|
||||
<rect x="80" y="40" rx="3" ry="3" width="250" height="10" />
|
||||
|
||||
</ContentLoader>
|
||||
)
|
||||
```
|
||||
@@ -212,69 +249,70 @@ const MyLoader = () => (
|
||||
|
||||

|
||||
|
||||
## Similar packages
|
||||
## Similar packages
|
||||
|
||||
- React Native: [rn-placeholder](https://github.com/mfrachet/rn-placeholder), [react-native-svg-animated-linear-gradient](https://github.com/virusvn/react-native-svg-animated-linear-gradient);
|
||||
- React Native: [rn-placeholder](https://github.com/mfrachet/rn-placeholder), [react-native-svg-animated-linear-gradient](https://github.com/virusvn/react-native-svg-animated-linear-gradient);
|
||||
- [Preact](https://github.com/bonitasoft/preact-content-loader);
|
||||
- Vue.js: [vue-content-loading](https://github.com/LucasLeandro1204/vue-content-loading), [vue-content-loader](https://github.com/egoist/vue-content-loader);
|
||||
- Angular: [ngx-content-loading](https://github.com/Gbuomprisco/ngx-content-loading), [ngx-content-loader](https://github.com/NetanelBasal/ngx-content-loader).
|
||||
- Vue.js: [vue-content-loading](https://github.com/LucasLeandro1204/vue-content-loading), [vue-content-loader](https://github.com/egoist/vue-content-loader);
|
||||
- Angular: [ngx-content-loading](https://github.com/Gbuomprisco/ngx-content-loading), [ngx-content-loader](https://github.com/NetanelBasal/ngx-content-loader).
|
||||
|
||||
## Development
|
||||
|
||||
Fork the repo then clone it
|
||||
Fork the repo then clone it
|
||||
|
||||
`$ git clone git@github.com:YourUsername/react-content-loader.git && cd react-content-loader`
|
||||
```
|
||||
$ git clone git@github.com:YourUsername/react-content-loader.git && cd react-content-loader
|
||||
```
|
||||
|
||||
`$ yarn`: Install the dependencies;
|
||||
`$ npm i`: Install the dependencies;
|
||||
|
||||
`$ yarn build`: Build to production;
|
||||
`$ npm run build`: Build to production;
|
||||
|
||||
`$ yarn dev`: Run the docz to see your changes;
|
||||
`$ npm run dev`: Run the docz to see your changes;
|
||||
|
||||
`$ yarn test`: Run all tests: type checking and unit tests;
|
||||
`$ npm run test`: Run all tests: type checking, unit tests on web and native;
|
||||
|
||||
`$ yarn test:watch`: Watch unit tests;
|
||||
`$ yarn test:watch`: Watch unit tests;
|
||||
|
||||
`$ yarn tsc`: Typescript checking;
|
||||
`$ yarn tsc`: Typescript checking;
|
||||
|
||||
`$ yarn tsc:watch`: Typescript checking with watching;
|
||||
`$ yarn tsc:watch`: Typescript checking with watching;
|
||||
|
||||
### Commit messages
|
||||
### Commit messages
|
||||
|
||||
Commit messages should follow the [commit message convention](https://conventionalcommits.org/) so, changelogs could be generated automatically by that. Commit messages are validated automatically upon commit. If you aren't familiar with the commit message convention, you can use yarn commit (or `npm run commit`) instead of git commit, which provides an interactive CLI for generating proper commit messages.
|
||||
Commit messages should follow the [commit message convention](https://conventionalcommits.org/) so, changelogs could be generated automatically by that. Commit messages are validated automatically upon commit. If you aren't familiar with the commit message convention, you can use yarn commit (or `npm run commit`) instead of git commit, which provides an interactive CLI for generating proper commit messages.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/danilowoz/react-content-loader/blob/master/LICENSE)
|
||||
|
||||
## Known Issues
|
||||
## Known Issues
|
||||
|
||||
##### **Alpha is not working: Safari / iOS**
|
||||
##### **Alpha is not working: Safari / iOS**
|
||||
|
||||
When using `rgba` as a `primaryColor` or `secondaryColor` value, [Safari does not respect the alpha channel](https://github.com/w3c/svgwg/issues/180), meaning that the color will be opaque. To prevent this, instead of using an `rgba` value for `primaryColor`/`secondaryColor`, use the `rgb` equivalent and move the alpha channel value to the `primaryOpacity`/`secondaryOpacity` props.
|
||||
When using `rgba` as a `primaryColor` or `secondaryColor` value, [Safari does not respect the alpha channel](https://github.com/w3c/svgwg/issues/180), meaning that the color will be opaque. To prevent this, instead of using an `rgba` value for `primaryColor`/`secondaryColor`, use the `rgb` equivalent and move the alpha channel value to the `primaryOpacity`/`secondaryOpacity` props.
|
||||
|
||||
```jsx
|
||||
{/* Opaque color in Safari and iOS */}
|
||||
{/* Opaque color in Safari and iOS */}
|
||||
<ContentLoader
|
||||
primaryColor="rgba(0,0,0,0.06)"
|
||||
secondaryColor="rgba(0,0,0,0.12)">
|
||||
primaryColor="rgba(0,0,0,0.06)"
|
||||
secondaryColor="rgba(0,0,0,0.12)">
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
{/_ Semi-transparent color in Safari and iOS _/}
|
||||
{/_ Semi-transparent color in Safari and iOS _/}
|
||||
<ContentLoader
|
||||
primaryColor="rgb(0,0,0)"
|
||||
secondaryColor="rgb(0,0,0)"
|
||||
primaryOpacity={0.06}
|
||||
secondaryOpacity={0.12}>
|
||||
primaryColor="rgb(0,0,0)"
|
||||
secondaryColor="rgb(0,0,0)"
|
||||
primaryOpacity={0.06}
|
||||
secondaryOpacity={0.12}>
|
||||
|
||||
|
||||
```
|
||||
|
||||
##### **Black box in Safari / iOS (again)**
|
||||
##### **Black box in Safari / iOS (again)**
|
||||
|
||||
Using base tag on a page that contains SVG elements fails to render and it looks like a black box. Just remove the **base-href** tag from the `<head />` and issue solved.
|
||||
Using base tag on a page that contains SVG elements fails to render and it looks like a black box. Just remove the **base-href** tag from the `<head />` and issue solved.
|
||||
|
||||
<img width="200" alt="Black box" src="https://user-images.githubusercontent.com/11562881/39406054-2f308de6-4bce-11e8-91fb-bbb35e29fc10.png" />
|
||||
<img width="200" alt="Black box" src="https://user-images.githubusercontent.com/11562881/39406054-2f308de6-4bce-11e8-91fb-bbb35e29fc10.png" />
|
||||
|
||||
See: [#93](https://github.com/danilowoz/react-content-loader/issues/93) / [109](https://github.com/danilowoz/react-content-loader/issues/109)
|
||||
See: [#93](https://github.com/danilowoz/react-content-loader/issues/93) / [109](
|
||||
|
||||
18
__mocks__/jestSetupFile.js
Normal file
18
__mocks__/jestSetupFile.js
Normal file
@@ -0,0 +1,18 @@
|
||||
jest.mock('Animated', () => {
|
||||
return {
|
||||
Value: () => {
|
||||
return {
|
||||
addListener: callback => callback({ value: 0 }),
|
||||
setValue: () => {},
|
||||
}
|
||||
},
|
||||
timing: (value, config) => {
|
||||
return {
|
||||
start: callback => {
|
||||
value.setValue(config.toValue)
|
||||
callback && callback()
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
64
__mocks__/react-native-svg.js
vendored
Normal file
64
__mocks__/react-native-svg.js
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
|
||||
const createComponent = function(name) {
|
||||
return class extends React.Component {
|
||||
// overwrite the displayName, since this is a class created dynamically
|
||||
static displayName = name;
|
||||
|
||||
render() {
|
||||
return React.createElement(name, this.props, this.props.children);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Mock all react-native-svg exports
|
||||
// from https://github.com/magicismight/react-native-svg/blob/master/index.js
|
||||
const Svg = createComponent('Svg');
|
||||
const Circle = createComponent('Circle');
|
||||
const Ellipse = createComponent('Ellipse');
|
||||
const G = createComponent('G');
|
||||
const Text = createComponent('Text');
|
||||
const TextPath = createComponent('TextPath');
|
||||
const TSpan = createComponent('TSpan');
|
||||
const Path = createComponent('Path');
|
||||
const Polygon = createComponent('Polygon');
|
||||
const Polyline = createComponent('Polyline');
|
||||
const Line = createComponent('Line');
|
||||
const Rect = createComponent('Rect');
|
||||
const Use = createComponent('Use');
|
||||
const Image = createComponent('Image');
|
||||
const Symbol = createComponent('Symbol');
|
||||
const Defs = createComponent('Defs');
|
||||
const LinearGradient = createComponent('LinearGradient');
|
||||
const RadialGradient = createComponent('RadialGradient');
|
||||
const Stop = createComponent('Stop');
|
||||
const ClipPath = createComponent('ClipPath');
|
||||
const Pattern = createComponent('Pattern');
|
||||
const Mask = createComponent('Mask');
|
||||
|
||||
export {
|
||||
Svg,
|
||||
Circle,
|
||||
Ellipse,
|
||||
G,
|
||||
Text,
|
||||
TextPath,
|
||||
TSpan,
|
||||
Path,
|
||||
Polygon,
|
||||
Polyline,
|
||||
Line,
|
||||
Rect,
|
||||
Use,
|
||||
Image,
|
||||
Symbol,
|
||||
Defs,
|
||||
LinearGradient,
|
||||
RadialGradient,
|
||||
Stop,
|
||||
ClipPath,
|
||||
Pattern,
|
||||
Mask,
|
||||
};
|
||||
|
||||
export default Svg;
|
||||
@@ -15,7 +15,7 @@ import ContentLoader, {
|
||||
|
||||
## Usage
|
||||
|
||||
## Different Type of Loaders
|
||||
## Presets
|
||||
|
||||
### Facebook Style Loader
|
||||
|
||||
|
||||
23
jest.native.config.js
Normal file
23
jest.native.config.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const { defaults: tsjPreset } = require('ts-jest/presets')
|
||||
|
||||
module.exports = {
|
||||
...tsjPreset,
|
||||
preset: "react-native",
|
||||
collectCoverage: true,
|
||||
coverageDirectory: './coverage/native',
|
||||
"transformIgnorePatterns": [
|
||||
"/node_modules/(?!react-native|react-clone-referenced-element|react-navigation)"
|
||||
],
|
||||
transform: {
|
||||
...tsjPreset.transform,
|
||||
'^.+\\.js$': require.resolve('react-native/jest/preprocessor.js'),
|
||||
},
|
||||
testRegex: '/src/native/__tests__/.*(\\.|/)(test|spec)\\.[jt]sx?$',
|
||||
setupFiles: ['./__mocks__/jestSetupFile.js'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
babelConfig: false,
|
||||
tsConfig: 'tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
};
|
||||
17
jest.web.config.js
Normal file
17
jest.web.config.js
Normal file
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
collectCoverage: true,
|
||||
coverageDirectory: './coverage/',
|
||||
transform: {
|
||||
'^.+\\.(t|j)sx?$': 'ts-jest',
|
||||
},
|
||||
testRegex: '/src/__tests__/.*(\\.|/)(test|spec)\\.[jt]sx?$',
|
||||
roots: ['<rootDir>/src'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
babelConfig: false,
|
||||
tsConfig: 'tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
}
|
||||
24941
package-lock.json
generated
Normal file
24941
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
146
package.json
146
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-content-loader",
|
||||
"version": "3.4.2",
|
||||
"version": "4.4.2",
|
||||
"description": "SVG-Powered component to easily create placeholder loadings (like Facebook cards loading)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -11,79 +11,101 @@
|
||||
"main": "dist/react-content-loader.cjs.js",
|
||||
"module": "dist/react-content-loader.es.js",
|
||||
"jsnext:main": "dist/react-content-loader.es.js",
|
||||
"types": "dist/src/index.d.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"bugs": {
|
||||
"url": "https://github.com/danilowoz/react-content-loader/issues"
|
||||
},
|
||||
"homepage": "https://github.com/danilowoz/react-content-loader",
|
||||
"keywords": [
|
||||
"react",
|
||||
"facebook-style",
|
||||
"react-native",
|
||||
"skeleton",
|
||||
"placeholder",
|
||||
"loader",
|
||||
"loading",
|
||||
"content",
|
||||
"svg"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"native"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"dev": "docz dev",
|
||||
"build": "rm -fr ./dist && rollup -c",
|
||||
"test": "npm run tsc && jest",
|
||||
"test:watch": "jest tests --watch",
|
||||
"tsc": "node_modules/.bin/tsc",
|
||||
"tsc:watch": "npm run tsc -- --watch",
|
||||
"build": "rm -fr ./dist ./native && rollup -c && npm run test:size",
|
||||
"test": "npm run test:tsc && npm run test:unit && npm run build",
|
||||
"test:unit": "npm run test:unit:web && npm run test:unit:native",
|
||||
"test:unit:web": "jest -c jest.web.config.js",
|
||||
"test:unit:native": "jest -c jest.native.config.js",
|
||||
"test:watch": "npm run test:unit -- --watch",
|
||||
"test:size": "bundlesize",
|
||||
"test:tsc": "node_modules/.bin/tsc",
|
||||
"test:tsc:watch": "npm run tsc -- --watch",
|
||||
"commit": "git-cz",
|
||||
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
||||
"coverage": "codecov",
|
||||
"lint": "eslint --ext .jsx --ext .ts,.tsx src/",
|
||||
"release": "semantic-release"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-native-svg": "9.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.4",
|
||||
"@babel/polyfill": "^7.4.4",
|
||||
"@babel/preset-env": "^7.4.4",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/register": "^7.4.4",
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@commitlint/cli": "^7.6.1",
|
||||
"@commitlint/config-conventional": "^7.6.0",
|
||||
"@types/jest": "^24.0.13",
|
||||
"@types/react": "^16.8.17",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/react-test-renderer": "^16.8.1",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-jest": "^24.8.0",
|
||||
"commitizen": "^3.1.1",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"docz": "^1.2.0",
|
||||
"docz-theme-default": "^1.2.0",
|
||||
"husky": "^2.3.0",
|
||||
"jest": "^24.8.0",
|
||||
"prettier": "^1.17.1",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-test-renderer": "^16.8.6",
|
||||
"remark-emoji": "^2.0.2",
|
||||
"rollup": "1.12.3",
|
||||
"rollup-plugin-analyzer": "^3.0.0",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-typescript2": "^0.21.1",
|
||||
"rollup-plugin-uglify": "^6.0.2",
|
||||
"semantic-release": "^15.13.12",
|
||||
"ts-jest": "^24.0.2",
|
||||
"tslint": "^5.16.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"typescript": "^3.4.5"
|
||||
"@babel/core": "7.5.5",
|
||||
"@babel/polyfill": "7.4.4",
|
||||
"@babel/preset-env": "7.5.5",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"@babel/register": "7.5.5",
|
||||
"@babel/runtime": "7.5.5",
|
||||
"@commitlint/cli": "8.1.0",
|
||||
"@commitlint/config-conventional": "8.1.0",
|
||||
"@types/jest": "24.0.18",
|
||||
"@types/react": "16.9.2",
|
||||
"@types/react-dom": "16.9.0",
|
||||
"@types/react-native": "0.60.11",
|
||||
"@types/react-test-renderer": "16.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "2.1.0",
|
||||
"@typescript-eslint/parser": "2.1.0",
|
||||
"babel-core": "6.26.3",
|
||||
"babel-eslint": "10.0.3",
|
||||
"babel-jest": "24.8.0",
|
||||
"bundlesize": "0.18.0",
|
||||
"commitizen": "4.0.3",
|
||||
"cz-conventional-changelog": "3.0.2",
|
||||
"docz": "1.2.0",
|
||||
"docz-theme-default": "1.2.0",
|
||||
"eslint": "6.3.0",
|
||||
"eslint-config-react-app": "5.0.1",
|
||||
"eslint-plugin-flowtype": "4.3.0",
|
||||
"eslint-plugin-import": "2.18.2",
|
||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||
"eslint-plugin-react": "7.14.3",
|
||||
"eslint-plugin-react-hooks": "2.0.1",
|
||||
"husky": "3.0.4",
|
||||
"jest": "24.9.0",
|
||||
"metro-react-native-babel-preset": "0.56.0",
|
||||
"prettier": "1.18.2",
|
||||
"react": "16.9.0",
|
||||
"react-dom": "16.9.0",
|
||||
"react-native": "0.60.5",
|
||||
"react-test-renderer": "16.9.0",
|
||||
"remark-emoji": "2.0.2",
|
||||
"rollup": "1.20.2",
|
||||
"rollup-plugin-copy": "3.1.0",
|
||||
"rollup-plugin-replace": "2.2.0",
|
||||
"rollup-plugin-typescript2": "0.23.0",
|
||||
"rollup-plugin-uglify": "6.0.2",
|
||||
"semantic-release": "15.13.24",
|
||||
"ts-jest": "24.0.2",
|
||||
"typescript": "3.5.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.6"
|
||||
"react": "^16.0.0",
|
||||
"react-native": "^0.60.5"
|
||||
},
|
||||
"dependencies": {},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm run format && npm run tsc",
|
||||
"pre-commit": "npm run lint && npm run format",
|
||||
"pre-push": "npm run test",
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
@@ -98,24 +120,14 @@
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"jest": {
|
||||
"verbose": true,
|
||||
"collectCoverage": true,
|
||||
"coverageDirectory": "./coverage/",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)sx?$": "ts-jest"
|
||||
"bundlesize": [
|
||||
{
|
||||
"path": "./dist/react-content-loader.min.js",
|
||||
"maxSize": "2 kB"
|
||||
},
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
|
||||
"roots": [
|
||||
"<rootDir>/src"
|
||||
],
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
{
|
||||
"path": "./native/react-content-loader.native.es.js",
|
||||
"maxSize": "3 kB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
import replace from 'rollup-plugin-replace'
|
||||
import { uglify } from 'rollup-plugin-uglify'
|
||||
import typescript from 'rollup-plugin-typescript2'
|
||||
import analyze from 'rollup-plugin-analyzer'
|
||||
import copy from 'rollup-plugin-copy'
|
||||
|
||||
import pkg from './package.json'
|
||||
|
||||
const mergeAll = objs => Object.assign({}, ...objs)
|
||||
|
||||
const cjs = {
|
||||
exports: 'named',
|
||||
format: 'cjs',
|
||||
sourcemap: true,
|
||||
};
|
||||
|
||||
const esm = {
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
};
|
||||
|
||||
const globals = { react: 'React', 'react-dom': 'ReactDOM' };
|
||||
|
||||
const commonPlugins = [
|
||||
typescript({
|
||||
typescript: require('typescript'),
|
||||
@@ -13,7 +27,6 @@ const commonPlugins = [
|
||||
]
|
||||
|
||||
const configBase = {
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
exports: 'named',
|
||||
},
|
||||
@@ -27,16 +40,14 @@ const configBase = {
|
||||
const umdConfig = mergeAll([
|
||||
configBase,
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: mergeAll([
|
||||
configBase.output,
|
||||
{
|
||||
file: `dist/${pkg.name}.js`,
|
||||
format: 'umd',
|
||||
name: 'ContentLoader',
|
||||
globals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
},
|
||||
globals
|
||||
},
|
||||
]),
|
||||
external: Object.keys(pkg.peerDependencies || {}),
|
||||
@@ -46,6 +57,7 @@ const umdConfig = mergeAll([
|
||||
const devUmdConfig = mergeAll([
|
||||
umdConfig,
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
plugins: umdConfig.plugins.concat(
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify('development'),
|
||||
@@ -57,6 +69,7 @@ const devUmdConfig = mergeAll([
|
||||
const prodUmdConfig = mergeAll([
|
||||
umdConfig,
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: mergeAll([
|
||||
umdConfig.output,
|
||||
{ file: umdConfig.output.file.replace(/\.js$/, '.min.js') },
|
||||
@@ -81,12 +94,29 @@ const prodUmdConfig = mergeAll([
|
||||
const webConfig = mergeAll([
|
||||
configBase,
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: [
|
||||
mergeAll([configBase.output, { file: pkg.module, format: 'es' }]),
|
||||
mergeAll([configBase.output, { file: pkg.main, format: 'cjs' }]),
|
||||
mergeAll([configBase.output, { ...esm, file: pkg.module }]),
|
||||
mergeAll([configBase.output, { ...cjs, file: pkg.main, }]),
|
||||
],
|
||||
plugins: configBase.plugins.concat(analyze()),
|
||||
plugins: configBase.plugins.concat(),
|
||||
},
|
||||
])
|
||||
|
||||
export default [devUmdConfig, prodUmdConfig, webConfig]
|
||||
const nativeConfig = mergeAll([
|
||||
configBase,
|
||||
{
|
||||
input: './src/native/index.ts',
|
||||
output: [
|
||||
mergeAll([configBase.output, { ...esm, file: `native/${pkg.name}.native.es.js` }]),
|
||||
mergeAll([configBase.output, { ...cjs, file: `native/${pkg.name}.native.cjs.js`, }]),
|
||||
],
|
||||
plugins: configBase.plugins.concat(copy({
|
||||
targets: [
|
||||
{ src: 'src/native/package.json', dest: 'native' },
|
||||
]
|
||||
}))
|
||||
},
|
||||
])
|
||||
|
||||
export default [devUmdConfig, prodUmdConfig, webConfig, nativeConfig]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
import Svg from './Svg'
|
||||
|
||||
import { IContentLoaderProps } from './interface'
|
||||
import Svg from './Svg'
|
||||
import { IContentLoaderProps } from './'
|
||||
|
||||
export const defaultProps: IContentLoaderProps = {
|
||||
animate: true,
|
||||
@@ -29,13 +29,12 @@ const InitialComponent: React.FunctionComponent<
|
||||
|
||||
const ContentLoader = (props: IContentLoaderProps) => {
|
||||
const mergedProps = { ...defaultProps, ...props }
|
||||
const children = props.children ? (
|
||||
props.children
|
||||
) : (
|
||||
<InitialComponent {...mergedProps} />
|
||||
)
|
||||
|
||||
return <Svg {...mergedProps}>{children}</Svg>
|
||||
return (
|
||||
<Svg {...mergedProps}>
|
||||
{props.children || <InitialComponent {...mergedProps} />}
|
||||
</Svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContentLoader
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import { IContentLoaderProps } from './interface'
|
||||
import { IContentLoaderProps } from './'
|
||||
import uid from './uid'
|
||||
|
||||
export default ({
|
||||
|
||||
@@ -94,7 +94,7 @@ describe('Svg', () => {
|
||||
expect(linearGradient.props.id).toBe(`${uniquekey}-idGradient`)
|
||||
})
|
||||
|
||||
it('render two components with diferents ids', () => {
|
||||
it('render two components with different ids', () => {
|
||||
// Wrapper
|
||||
const { clipPath, linearGradient } = partsOfComponent
|
||||
|
||||
8
src/__tests__/dependency.test.tsx
Normal file
8
src/__tests__/dependency.test.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import pkg from '../../package.json'
|
||||
|
||||
describe('package.json', () => {
|
||||
it(`has only one dependency which is for support react-native`, () => {
|
||||
expect('react-native-svg' in pkg.dependencies).toBe(true)
|
||||
expect(Object.keys(pkg.dependencies).length).toBe(1)
|
||||
})
|
||||
})
|
||||
@@ -1,7 +0,0 @@
|
||||
import pkg from '../../package.json'
|
||||
|
||||
describe('package.json', () => {
|
||||
it(`has no dependencies`, () => {
|
||||
expect(Object.keys(pkg.dependencies).length).toBe(0)
|
||||
})
|
||||
})
|
||||
41
src/index.ts
41
src/index.ts
@@ -1,5 +1,44 @@
|
||||
import ContentLoader from './Holder'
|
||||
export { IContentLoaderProps } from './interface'
|
||||
|
||||
export interface IContentLoaderProps {
|
||||
animate?: boolean
|
||||
ariaLabel?: string
|
||||
children?: React.ReactNode
|
||||
baseUrl?: string
|
||||
className?: string
|
||||
height?: number
|
||||
preserveAspectRatio?:
|
||||
| 'none'
|
||||
| 'xMinYMin meet'
|
||||
| 'xMidYMin meet'
|
||||
| 'xMaxYMin meet'
|
||||
| 'xMinYMid meet'
|
||||
| 'xMidYMid meet'
|
||||
| 'xMaxYMid meet'
|
||||
| 'xMinYMax meet'
|
||||
| 'xMidYMax meet'
|
||||
| 'xMaxYMax meet'
|
||||
| 'xMinYMin slice'
|
||||
| 'xMidYMin slice'
|
||||
| 'xMaxYMin slice'
|
||||
| 'xMinYMid slice'
|
||||
| 'xMidYMid slice'
|
||||
| 'xMaxYMid slice'
|
||||
| 'xMinYMax slice'
|
||||
| 'xMidYMax slice'
|
||||
| 'xMaxYMax slice'
|
||||
primaryColor?: string
|
||||
primaryOpacity?: number
|
||||
rtl?: boolean
|
||||
secondaryColor?: string
|
||||
secondaryOpacity?: number
|
||||
speed?: number
|
||||
interval?: number
|
||||
style?: React.CSSProperties
|
||||
uniquekey?: string
|
||||
width?: number
|
||||
gradientRatio?: number
|
||||
}
|
||||
|
||||
export { default as Facebook } from './stylized/FacebookStyle'
|
||||
export { default as Instagram } from './stylized/InstagramStyle'
|
||||
|
||||
50
src/native/Holder.tsx
Normal file
50
src/native/Holder.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import * as React from 'react'
|
||||
import { Circle, Rect } from 'react-native-svg'
|
||||
|
||||
import Svg from './Svg'
|
||||
import { IContentLoaderProps } from './'
|
||||
|
||||
type DefaultProps = Pick<
|
||||
Required<IContentLoaderProps>,
|
||||
| 'animate'
|
||||
| 'height'
|
||||
| 'preserveAspectRatio'
|
||||
| 'primaryColor'
|
||||
| 'rtl'
|
||||
| 'secondaryColor'
|
||||
| 'speed'
|
||||
| 'style'
|
||||
| 'width'
|
||||
>
|
||||
|
||||
export const defaultProps: DefaultProps = {
|
||||
animate: true,
|
||||
height: 130,
|
||||
preserveAspectRatio: 'none',
|
||||
primaryColor: '#f0f0f0',
|
||||
rtl: false,
|
||||
secondaryColor: '#e0e0e0',
|
||||
speed: 1,
|
||||
style: {},
|
||||
width: 400,
|
||||
}
|
||||
|
||||
const InitialComponent: React.FunctionComponent<
|
||||
IContentLoaderProps
|
||||
> = props => (
|
||||
<Rect x="0" y="0" rx="5" ry="5" width={props.width} height={props.height} />
|
||||
)
|
||||
|
||||
const ContentLoader = (props: IContentLoaderProps) => {
|
||||
const mergedProps = { ...defaultProps, ...props }
|
||||
|
||||
return (
|
||||
<Svg {...mergedProps}>
|
||||
{props.children || <InitialComponent {...mergedProps} />}
|
||||
</Svg>
|
||||
)
|
||||
}
|
||||
|
||||
export { Circle, Rect }
|
||||
|
||||
export default ContentLoader
|
||||
132
src/native/Svg.tsx
Normal file
132
src/native/Svg.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Animated } from 'react-native'
|
||||
|
||||
import Svg, {
|
||||
ClipPath,
|
||||
Defs,
|
||||
LinearGradient,
|
||||
Rect,
|
||||
Stop,
|
||||
} from 'react-native-svg'
|
||||
|
||||
import { IContentLoaderProps } from './'
|
||||
import offsetValueBound from './offsetValueBound'
|
||||
import uid from '../uid'
|
||||
|
||||
type RequiredIContentLoaderProps = IContentLoaderProps &
|
||||
Pick<
|
||||
Required<IContentLoaderProps>,
|
||||
| 'animate'
|
||||
| 'height'
|
||||
| 'preserveAspectRatio'
|
||||
| 'primaryColor'
|
||||
| 'rtl'
|
||||
| 'secondaryColor'
|
||||
| 'speed'
|
||||
| 'style'
|
||||
| 'width'
|
||||
>
|
||||
|
||||
interface State {
|
||||
offset: number
|
||||
}
|
||||
|
||||
class NativeSvg extends Component<RequiredIContentLoaderProps, State> {
|
||||
state = { offset: -1 }
|
||||
|
||||
animatedValue = new Animated.Value(0)
|
||||
|
||||
idClip = this.props.id ? `${this.props.id}-idClip` : uid()
|
||||
|
||||
idGradient = this.props.id ? `${this.props.id}-idGradient` : uid()
|
||||
|
||||
setAnimation = () => {
|
||||
// Turn in seconds to keep compatible with web one
|
||||
const durInSeconds = this.props.speed * 1000
|
||||
|
||||
Animated.timing(this.animatedValue, {
|
||||
toValue: 2,
|
||||
delay: durInSeconds,
|
||||
duration: durInSeconds,
|
||||
useNativeDriver: true,
|
||||
}).start(() => {
|
||||
this.animatedValue.setValue(-1)
|
||||
this.setAnimation()
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
if (this.props.animate) {
|
||||
this.setAnimation()
|
||||
|
||||
this.animatedValue.addListener(({ value }) => {
|
||||
this.setState({
|
||||
offset: value,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
children,
|
||||
height,
|
||||
primaryColor,
|
||||
secondaryColor,
|
||||
width,
|
||||
rtl,
|
||||
style,
|
||||
...props
|
||||
} = this.props
|
||||
|
||||
const offset1 = offsetValueBound(this.state.offset - 1)
|
||||
const offset2 = offsetValueBound(this.state.offset)
|
||||
const offset3 = offsetValueBound(this.state.offset + 1)
|
||||
|
||||
const rtlStyle = rtl ? { transform: [{ rotateY: '180deg' }] } : {}
|
||||
const composedStyle = { ...style, ...rtlStyle }
|
||||
|
||||
// Remove unnecessary keys
|
||||
delete props.id
|
||||
delete props.animate
|
||||
delete props.speed
|
||||
|
||||
return (
|
||||
<Svg
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
width={width}
|
||||
height={height}
|
||||
preserveAspectRatio="none"
|
||||
style={composedStyle}
|
||||
{...props}
|
||||
>
|
||||
<Rect
|
||||
x="0"
|
||||
y="0"
|
||||
width={width}
|
||||
height={height}
|
||||
fill={`url(#${this.idClip})`}
|
||||
clipPath={`url(#${this.idGradient})`}
|
||||
/>
|
||||
|
||||
<Defs>
|
||||
<ClipPath id={this.idGradient}>{children}</ClipPath>
|
||||
|
||||
<LinearGradient
|
||||
id={this.idClip}
|
||||
x1={'-100%'}
|
||||
y1={0}
|
||||
x2={'100%'}
|
||||
y2={0}
|
||||
>
|
||||
<Stop offset={offset1} stopColor={primaryColor} />
|
||||
<Stop offset={offset2} stopColor={secondaryColor} />
|
||||
<Stop offset={offset3} stopColor={primaryColor} />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
</Svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default NativeSvg
|
||||
127
src/native/__tests__/Holder.test.tsx
Normal file
127
src/native/__tests__/Holder.test.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import * as React from 'react'
|
||||
import * as renderer from 'react-test-renderer'
|
||||
import * as ShallowRenderer from 'react-test-renderer/shallow'
|
||||
|
||||
import ContentLoader, { Rect, Circle } from '../Holder'
|
||||
|
||||
describe('Holder', () => {
|
||||
describe('when type is custom', () => {
|
||||
const customWrapper = renderer.create(
|
||||
<ContentLoader animate={false}>
|
||||
<Rect x="80" y="17" rx="4" ry="4" width="300" height="13" />
|
||||
<Rect x="82" y="44" rx="3" ry="3" width="250" height="10" />
|
||||
<Circle cx="35" cy="35" r="35" />
|
||||
</ContentLoader>
|
||||
).root
|
||||
|
||||
it('should render custom element', () => {
|
||||
const rect = customWrapper.findAllByType(Rect)
|
||||
const circle = customWrapper.findAllByType(Circle)
|
||||
|
||||
expect(rect.length).toBe(3)
|
||||
expect(circle.length).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Props are propagated', () => {
|
||||
const noPropsComponent = ShallowRenderer.createRenderer()
|
||||
noPropsComponent.render(<ContentLoader />)
|
||||
|
||||
const withPropsComponent = ShallowRenderer.createRenderer()
|
||||
withPropsComponent.render(
|
||||
<ContentLoader
|
||||
animate={false}
|
||||
height={200}
|
||||
preserveAspectRatio="xMaxYMax meet"
|
||||
primaryColor="#000"
|
||||
rtl
|
||||
secondaryColor="#fff"
|
||||
speed={10}
|
||||
style={{ marginBottom: '10px' }}
|
||||
width={200}
|
||||
/>
|
||||
)
|
||||
|
||||
const { props: propsFromEmpty } = noPropsComponent.getRenderOutput()
|
||||
const { props: propsFromFullField } = withPropsComponent.getRenderOutput()
|
||||
|
||||
it("`speed` is a number and it's used", () => {
|
||||
// defaultProps
|
||||
expect(typeof propsFromEmpty.speed).toBe('number')
|
||||
expect(propsFromEmpty.speed).toBe(1)
|
||||
// custom props
|
||||
expect(typeof propsFromFullField.speed).toBe('number')
|
||||
expect(propsFromFullField.speed).toBe(10)
|
||||
})
|
||||
|
||||
it("`height` is a number and it's used", () => {
|
||||
// defaultProps
|
||||
expect(typeof propsFromEmpty.height).toBe('number')
|
||||
expect(propsFromEmpty.height).toBe(130)
|
||||
// custom props
|
||||
expect(typeof propsFromFullField.height).toBe('number')
|
||||
expect(propsFromFullField.height).toBe(200)
|
||||
})
|
||||
|
||||
it("`width` is a number and it's used", () => {
|
||||
// defaultProps
|
||||
expect(typeof propsFromEmpty.width).toBe('number')
|
||||
expect(propsFromEmpty.width).toBe(400)
|
||||
// custom props
|
||||
expect(typeof propsFromFullField.width).toBe('number')
|
||||
expect(propsFromFullField.width).toBe(200)
|
||||
})
|
||||
|
||||
it("`animate` is a boolean and it's used", () => {
|
||||
// defaultProps
|
||||
expect(typeof propsFromEmpty.animate).toBe('boolean')
|
||||
expect(propsFromEmpty.animate).toBe(true)
|
||||
// custom props
|
||||
expect(typeof propsFromFullField.animate).toBe('boolean')
|
||||
expect(propsFromFullField.animate).toBe(false)
|
||||
})
|
||||
|
||||
it("`primaryColor` is a string and it's used", () => {
|
||||
// defaultProps
|
||||
expect(typeof propsFromEmpty.primaryColor).toBe('string')
|
||||
expect(propsFromEmpty.primaryColor).toBe('#f0f0f0')
|
||||
// custom props
|
||||
expect(typeof propsFromFullField.primaryColor).toBe('string')
|
||||
expect(propsFromFullField.primaryColor).toBe('#000')
|
||||
})
|
||||
|
||||
it("`secondaryColor` is a string and it's used", () => {
|
||||
// defaultProps
|
||||
expect(typeof propsFromEmpty.secondaryColor).toBe('string')
|
||||
expect(propsFromEmpty.secondaryColor).toBe('#e0e0e0')
|
||||
// custom props
|
||||
expect(typeof propsFromFullField.secondaryColor).toBe('string')
|
||||
expect(propsFromFullField.secondaryColor).toBe('#fff')
|
||||
})
|
||||
|
||||
it("`preserveAspectRatio` is a string and it's used", () => {
|
||||
// defaultProps
|
||||
expect(typeof propsFromEmpty.preserveAspectRatio).toBe('string')
|
||||
expect(propsFromEmpty.preserveAspectRatio).toBe('none')
|
||||
// custom props
|
||||
expect(typeof propsFromFullField.preserveAspectRatio).toBe('string')
|
||||
expect(propsFromFullField.preserveAspectRatio).toBe('xMaxYMax meet')
|
||||
})
|
||||
|
||||
it("`style` is an object and it's used", () => {
|
||||
// defaultProps
|
||||
expect(propsFromEmpty.style).toMatchObject({})
|
||||
// custom props
|
||||
expect(propsFromFullField.style).toMatchObject({ marginBottom: '10px' })
|
||||
})
|
||||
|
||||
it("`rtl` is a boolean and it's used", () => {
|
||||
// defaultProps
|
||||
expect(typeof propsFromEmpty.rtl).toBe('boolean')
|
||||
expect(propsFromEmpty.rtl).toBe(false)
|
||||
// custom props
|
||||
expect(typeof propsFromFullField.rtl).toBe('boolean')
|
||||
expect(propsFromFullField.rtl).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
80
src/native/__tests__/Svg.test.tsx
Normal file
80
src/native/__tests__/Svg.test.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import * as React from 'react'
|
||||
import * as renderer from 'react-test-renderer'
|
||||
|
||||
import ContentLoader from '..'
|
||||
import Svg, { ClipPath, LinearGradient, Stop } from 'react-native-svg'
|
||||
|
||||
interface IPredicateArgs {
|
||||
type: any
|
||||
props: any
|
||||
}
|
||||
|
||||
describe('Svg', () => {
|
||||
const wrapper = renderer.create(<ContentLoader animate={false} />).root
|
||||
const predicateRectClipPath = ({ type, props }: IPredicateArgs) =>
|
||||
type === 'Rect' && props.clipPath
|
||||
const partsOfComponent = {
|
||||
allLinearGradient: wrapper.findAllByType(LinearGradient),
|
||||
allRectClipPath: wrapper.findAll(predicateRectClipPath),
|
||||
allStops: wrapper.findAllByType(Stop),
|
||||
clipPath: wrapper.findByType(ClipPath),
|
||||
linearGradient: wrapper.findByType(LinearGradient),
|
||||
rectClipPath: wrapper.find(predicateRectClipPath),
|
||||
svg: wrapper.findByType(Svg),
|
||||
}
|
||||
|
||||
describe('it has basic elements necessary to work ', () => {
|
||||
it('has a `rect` with `clipPath`', () => {
|
||||
const { allRectClipPath } = partsOfComponent
|
||||
|
||||
expect(allRectClipPath.length).toBe(1)
|
||||
})
|
||||
|
||||
it('has a `linearGradient`', () => {
|
||||
const { allLinearGradient } = partsOfComponent
|
||||
|
||||
expect(allLinearGradient.length).toBe(1)
|
||||
})
|
||||
|
||||
it('has three `stop`', () => {
|
||||
const { allStops } = partsOfComponent
|
||||
|
||||
expect(allStops.length).toBe(3)
|
||||
})
|
||||
|
||||
it('has `stop` inside the `linearGradient`', () => {
|
||||
const { linearGradient } = partsOfComponent
|
||||
const stopsIntoLinearGradient = linearGradient.findAllByType(Stop)
|
||||
|
||||
expect(stopsIntoLinearGradient.length).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('unique key', () => {
|
||||
it('render two components with different ids', () => {
|
||||
// Wrapper
|
||||
const { clipPath, linearGradient } = partsOfComponent
|
||||
|
||||
// Another component
|
||||
const anotherComp = renderer.create(<ContentLoader animate={false} />)
|
||||
.root
|
||||
const anotherClipPath = anotherComp.findByType(ClipPath)
|
||||
const anotherLinearGradient = anotherComp.findByType(LinearGradient)
|
||||
|
||||
expect(clipPath.props.id).not.toBe(anotherClipPath.props.id)
|
||||
expect(linearGradient.props.id).not.toBe(anotherLinearGradient.props.id)
|
||||
})
|
||||
|
||||
it('clipPath id and rect clipPath url are the same', () => {
|
||||
const { clipPath, rectClipPath } = partsOfComponent
|
||||
|
||||
expect(rectClipPath.props.clipPath).toBe(`url(#${clipPath.props.id})`)
|
||||
})
|
||||
|
||||
it('linearGradient id and rect clipPath fill are the same', () => {
|
||||
const { linearGradient, rectClipPath } = partsOfComponent
|
||||
|
||||
expect(rectClipPath.props.fill).toBe(`url(#${linearGradient.props.id})`)
|
||||
})
|
||||
})
|
||||
})
|
||||
14
src/native/__tests__/offsetValueBound.test.ts
Normal file
14
src/native/__tests__/offsetValueBound.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import offsetValueBound from '../offsetValueBound'
|
||||
|
||||
describe('offset value bound ', () => {
|
||||
it('should return an integer', () => {
|
||||
expect(offsetValueBound(-1)).toBe(0)
|
||||
expect(offsetValueBound(0)).toBe(0)
|
||||
})
|
||||
|
||||
it('should return a max value 1', () => {
|
||||
expect(offsetValueBound(1)).toBe(1)
|
||||
expect(offsetValueBound(1.1)).toBe(1)
|
||||
expect(offsetValueBound(2)).toBe(1)
|
||||
})
|
||||
})
|
||||
21
src/native/__tests__/stylized/BulletListStyle.test.tsx
Normal file
21
src/native/__tests__/stylized/BulletListStyle.test.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'react-native'
|
||||
import * as React from 'react'
|
||||
import * as renderer from 'react-test-renderer'
|
||||
|
||||
import BulletListStyle from '../../stylized/BulletListStyle'
|
||||
|
||||
describe('BulletListStyle', () => {
|
||||
const wrapper = renderer.create(
|
||||
<BulletListStyle id="BulletListStyle" animate={false} speed={20} />
|
||||
)
|
||||
|
||||
test('renders correctly', () => {
|
||||
const tree = wrapper.toJSON()
|
||||
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('props are propagated ', () => {
|
||||
expect(wrapper.root.props.speed).toEqual(20)
|
||||
})
|
||||
})
|
||||
21
src/native/__tests__/stylized/CodeStyle.test.tsx
Normal file
21
src/native/__tests__/stylized/CodeStyle.test.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'react-native'
|
||||
import * as React from 'react'
|
||||
import * as renderer from 'react-test-renderer'
|
||||
|
||||
import CodeStyle from '../../stylized/CodeStyle'
|
||||
|
||||
describe('CodeStyle', () => {
|
||||
const wrapper = renderer.create(
|
||||
<CodeStyle id="CodeStyle" animate={false} speed={20} />
|
||||
)
|
||||
|
||||
test('renders correctly', () => {
|
||||
const tree = wrapper.toJSON()
|
||||
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('props are propagated ', () => {
|
||||
expect(wrapper.root.props.speed).toEqual(20)
|
||||
})
|
||||
})
|
||||
21
src/native/__tests__/stylized/FacebookStyle.test.tsx
Normal file
21
src/native/__tests__/stylized/FacebookStyle.test.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'react-native'
|
||||
import * as React from 'react'
|
||||
import * as renderer from 'react-test-renderer'
|
||||
|
||||
import FacebookStyle from '../../stylized/FacebookStyle'
|
||||
|
||||
describe('FacebookStyle', () => {
|
||||
const wrapper = renderer.create(
|
||||
<FacebookStyle id="FacebookStyle" animate={false} speed={20} />
|
||||
)
|
||||
|
||||
test('renders correctly', () => {
|
||||
const tree = wrapper.toJSON()
|
||||
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('props are propagated ', () => {
|
||||
expect(wrapper.root.props.speed).toEqual(20)
|
||||
})
|
||||
})
|
||||
21
src/native/__tests__/stylized/InstagramStyle.test.tsx
Normal file
21
src/native/__tests__/stylized/InstagramStyle.test.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'react-native'
|
||||
import * as React from 'react'
|
||||
import * as renderer from 'react-test-renderer'
|
||||
|
||||
import InstagramStyle from '../../stylized/InstagramStyle'
|
||||
|
||||
describe('InstagramStyle', () => {
|
||||
const wrapper = renderer.create(
|
||||
<InstagramStyle id="InstagramStyle" animate={false} speed={20} />
|
||||
)
|
||||
|
||||
test('renders correctly', () => {
|
||||
const tree = wrapper.toJSON()
|
||||
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('props are propagated ', () => {
|
||||
expect(wrapper.root.props.speed).toEqual(20)
|
||||
})
|
||||
})
|
||||
21
src/native/__tests__/stylized/ListStyle.test.tsx
Normal file
21
src/native/__tests__/stylized/ListStyle.test.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'react-native'
|
||||
import * as React from 'react'
|
||||
import * as renderer from 'react-test-renderer'
|
||||
|
||||
import ListStyle from '../../stylized/ListStyle'
|
||||
|
||||
describe('ListStyle', () => {
|
||||
const wrapper = renderer.create(
|
||||
<ListStyle id="ListStyle" animate={false} speed={20} />
|
||||
)
|
||||
|
||||
test('renders correctly', () => {
|
||||
const tree = wrapper.toJSON()
|
||||
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('props are propagated ', () => {
|
||||
expect(wrapper.root.props.speed).toEqual(20)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,98 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`BulletListStyle renders correctly 1`] = `
|
||||
<Svg
|
||||
height={130}
|
||||
preserveAspectRatio="none"
|
||||
style={Object {}}
|
||||
viewBox="0 0 400 130"
|
||||
width={400}
|
||||
>
|
||||
<Rect
|
||||
clipPath="url(#BulletListStyle-idGradient)"
|
||||
fill="url(#BulletListStyle-idClip)"
|
||||
height={130}
|
||||
width={400}
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<Defs>
|
||||
<ClipPath
|
||||
id="BulletListStyle-idGradient"
|
||||
>
|
||||
<Circle
|
||||
cx="10"
|
||||
cy="20"
|
||||
r="8"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="5"
|
||||
ry="5"
|
||||
width="220"
|
||||
x="25"
|
||||
y="15"
|
||||
/>
|
||||
<Circle
|
||||
cx="10"
|
||||
cy="50"
|
||||
r="8"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="5"
|
||||
ry="5"
|
||||
width="220"
|
||||
x="25"
|
||||
y="45"
|
||||
/>
|
||||
<Circle
|
||||
cx="10"
|
||||
cy="80"
|
||||
r="8"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="5"
|
||||
ry="5"
|
||||
width="220"
|
||||
x="25"
|
||||
y="75"
|
||||
/>
|
||||
<Circle
|
||||
cx="10"
|
||||
cy="110"
|
||||
r="8"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="5"
|
||||
ry="5"
|
||||
width="220"
|
||||
x="25"
|
||||
y="105"
|
||||
/>
|
||||
</ClipPath>
|
||||
<LinearGradient
|
||||
id="BulletListStyle-idClip"
|
||||
x1="-100%"
|
||||
x2="100%"
|
||||
y1={0}
|
||||
y2={0}
|
||||
>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#e0e0e0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
</Svg>
|
||||
`;
|
||||
@@ -0,0 +1,118 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CodeStyle renders correctly 1`] = `
|
||||
<Svg
|
||||
height={130}
|
||||
preserveAspectRatio="none"
|
||||
style={Object {}}
|
||||
viewBox="0 0 400 130"
|
||||
width={400}
|
||||
>
|
||||
<Rect
|
||||
clipPath="url(#CodeStyle-idGradient)"
|
||||
fill="url(#CodeStyle-idClip)"
|
||||
height={130}
|
||||
width={400}
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<Defs>
|
||||
<ClipPath
|
||||
id="CodeStyle-idGradient"
|
||||
>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="70"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="100"
|
||||
x="80"
|
||||
y="0"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="10"
|
||||
x="190"
|
||||
y="0"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="130"
|
||||
x="15"
|
||||
y="20"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="130"
|
||||
x="155"
|
||||
y="20"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="90"
|
||||
x="15"
|
||||
y="40"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="60"
|
||||
x="115"
|
||||
y="40"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="60"
|
||||
x="185"
|
||||
y="40"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="30"
|
||||
x="0"
|
||||
y="60"
|
||||
/>
|
||||
</ClipPath>
|
||||
<LinearGradient
|
||||
id="CodeStyle-idClip"
|
||||
x1="-100%"
|
||||
x2="100%"
|
||||
y1={0}
|
||||
y2={0}
|
||||
>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#e0e0e0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
</Svg>
|
||||
`;
|
||||
@@ -0,0 +1,91 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FacebookStyle renders correctly 1`] = `
|
||||
<Svg
|
||||
height={130}
|
||||
preserveAspectRatio="none"
|
||||
style={Object {}}
|
||||
viewBox="0 0 400 130"
|
||||
width={400}
|
||||
>
|
||||
<Rect
|
||||
clipPath="url(#FacebookStyle-idGradient)"
|
||||
fill="url(#FacebookStyle-idClip)"
|
||||
height={130}
|
||||
width={400}
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<Defs>
|
||||
<ClipPath
|
||||
id="FacebookStyle-idGradient"
|
||||
>
|
||||
<Rect
|
||||
height="6"
|
||||
rx="4"
|
||||
ry="4"
|
||||
width="117"
|
||||
x="70"
|
||||
y="15"
|
||||
/>
|
||||
<Rect
|
||||
height="6"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="85"
|
||||
x="70"
|
||||
y="35"
|
||||
/>
|
||||
<Rect
|
||||
height="6"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="350"
|
||||
x="0"
|
||||
y="80"
|
||||
/>
|
||||
<Rect
|
||||
height="6"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="380"
|
||||
x="0"
|
||||
y="100"
|
||||
/>
|
||||
<Rect
|
||||
height="6"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="201"
|
||||
x="0"
|
||||
y="120"
|
||||
/>
|
||||
<Circle
|
||||
cx="30"
|
||||
cy="30"
|
||||
r="30"
|
||||
/>
|
||||
</ClipPath>
|
||||
<LinearGradient
|
||||
id="FacebookStyle-idClip"
|
||||
x1="-100%"
|
||||
x2="100%"
|
||||
y1={0}
|
||||
y2={0}
|
||||
>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#e0e0e0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
</Svg>
|
||||
`;
|
||||
@@ -0,0 +1,75 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`InstagramStyle renders correctly 1`] = `
|
||||
<Svg
|
||||
height={480}
|
||||
preserveAspectRatio="none"
|
||||
style={Object {}}
|
||||
viewBox="0 0 400 480"
|
||||
width={400}
|
||||
>
|
||||
<Rect
|
||||
clipPath="url(#InstagramStyle-idGradient)"
|
||||
fill="url(#InstagramStyle-idClip)"
|
||||
height={480}
|
||||
width={400}
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<Defs>
|
||||
<ClipPath
|
||||
id="InstagramStyle-idGradient"
|
||||
>
|
||||
<Circle
|
||||
cx="30"
|
||||
cy="30"
|
||||
r="30"
|
||||
/>
|
||||
<Rect
|
||||
height="13"
|
||||
rx="4"
|
||||
ry="4"
|
||||
width="100"
|
||||
x="75"
|
||||
y="13"
|
||||
/>
|
||||
<Rect
|
||||
height="8"
|
||||
rx="4"
|
||||
ry="4"
|
||||
width="50"
|
||||
x="75"
|
||||
y="37"
|
||||
/>
|
||||
<Rect
|
||||
height="400"
|
||||
rx="5"
|
||||
ry="5"
|
||||
width="400"
|
||||
x="0"
|
||||
y="70"
|
||||
/>
|
||||
</ClipPath>
|
||||
<LinearGradient
|
||||
id="InstagramStyle-idClip"
|
||||
x1="-100%"
|
||||
x2="100%"
|
||||
y1={0}
|
||||
y2={0}
|
||||
>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#e0e0e0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
</Svg>
|
||||
`;
|
||||
@@ -0,0 +1,94 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ListStyle renders correctly 1`] = `
|
||||
<Svg
|
||||
height={130}
|
||||
preserveAspectRatio="none"
|
||||
style={Object {}}
|
||||
viewBox="0 0 400 130"
|
||||
width={400}
|
||||
>
|
||||
<Rect
|
||||
clipPath="url(#ListStyle-idGradient)"
|
||||
fill="url(#ListStyle-idClip)"
|
||||
height={130}
|
||||
width={400}
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<Defs>
|
||||
<ClipPath
|
||||
id="ListStyle-idGradient"
|
||||
>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="250"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="220"
|
||||
x="20"
|
||||
y="20"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="170"
|
||||
x="20"
|
||||
y="40"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="250"
|
||||
x="0"
|
||||
y="60"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="200"
|
||||
x="20"
|
||||
y="80"
|
||||
/>
|
||||
<Rect
|
||||
height="10"
|
||||
rx="3"
|
||||
ry="3"
|
||||
width="80"
|
||||
x="20"
|
||||
y="100"
|
||||
/>
|
||||
</ClipPath>
|
||||
<LinearGradient
|
||||
id="ListStyle-idClip"
|
||||
x1="-100%"
|
||||
x2="100%"
|
||||
y1={0}
|
||||
y2={0}
|
||||
>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#e0e0e0"
|
||||
/>
|
||||
<Stop
|
||||
offset={0}
|
||||
stopColor="#f0f0f0"
|
||||
/>
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
</Svg>
|
||||
`;
|
||||
11
src/native/index.ts
Normal file
11
src/native/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import ContentLoader, { Rect, Circle } from './Holder'
|
||||
export { IContentLoaderProps } from './interface'
|
||||
|
||||
export { default as Facebook } from './stylized/FacebookStyle'
|
||||
export { default as Instagram } from './stylized/InstagramStyle'
|
||||
export { default as Code } from './stylized/CodeStyle'
|
||||
export { default as List } from './stylized/ListStyle'
|
||||
export { default as BulletList } from './stylized/BulletListStyle'
|
||||
|
||||
export { Rect, Circle }
|
||||
export default ContentLoader
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as ReactNative from 'react-native'
|
||||
|
||||
export interface IContentLoaderProps {
|
||||
animate?: boolean
|
||||
ariaLabel?: string
|
||||
children?: React.ReactNode
|
||||
baseUrl?: string
|
||||
className?: string
|
||||
height?: number
|
||||
preserveAspectRatio?:
|
||||
@@ -25,15 +25,11 @@ export interface IContentLoaderProps {
|
||||
| 'xMinYMax slice'
|
||||
| 'xMidYMax slice'
|
||||
| 'xMaxYMax slice'
|
||||
id?: string
|
||||
primaryColor?: string
|
||||
primaryOpacity?: number
|
||||
rtl?: boolean
|
||||
secondaryColor?: string
|
||||
secondaryOpacity?: number
|
||||
speed?: number
|
||||
interval?: number
|
||||
style?: React.CSSProperties
|
||||
uniquekey?: string
|
||||
style?: ReactNative.ViewStyle
|
||||
width?: number
|
||||
gradientRatio?: number
|
||||
}
|
||||
13
src/native/offsetValueBound.ts
Normal file
13
src/native/offsetValueBound.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const offsetValueBound = (value: number): number => {
|
||||
if (value > 1) {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
export default offsetValueBound
|
||||
8
src/native/package.json
Normal file
8
src/native/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "react-content-loader/native",
|
||||
"private": true,
|
||||
"main": "./react-content-loader.native.cjs.js",
|
||||
"module": "./react-content-loader.native.es.js",
|
||||
"jsnext:main": "./react-content-loader.native.es.js",
|
||||
"types": "./native/index.d.ts"
|
||||
}
|
||||
17
src/native/stylized/BulletListStyle.tsx
Normal file
17
src/native/stylized/BulletListStyle.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder, { Rect, Circle } from '../Holder'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props}>
|
||||
<Circle cx="10" cy="20" r="8" />
|
||||
<Rect x="25" y="15" rx="5" ry="5" width="220" height="10" />
|
||||
<Circle cx="10" cy="50" r="8" />
|
||||
<Rect x="25" y="45" rx="5" ry="5" width="220" height="10" />
|
||||
<Circle cx="10" cy="80" r="8" />
|
||||
<Rect x="25" y="75" rx="5" ry="5" width="220" height="10" />
|
||||
<Circle cx="10" cy="110" r="8" />
|
||||
<Rect x="25" y="105" rx="5" ry="5" width="220" height="10" />
|
||||
</Holder>
|
||||
)
|
||||
21
src/native/stylized/CodeStyle.tsx
Normal file
21
src/native/stylized/CodeStyle.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder, { Rect } from '../Holder'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props}>
|
||||
<Rect x="0" y="0" rx="3" ry="3" width="70" height="10" />
|
||||
<Rect x="80" y="0" rx="3" ry="3" width="100" height="10" />
|
||||
<Rect x="190" y="0" rx="3" ry="3" width="10" height="10" />
|
||||
|
||||
<Rect x="15" y="20" rx="3" ry="3" width="130" height="10" />
|
||||
<Rect x="155" y="20" rx="3" ry="3" width="130" height="10" />
|
||||
|
||||
<Rect x="15" y="40" rx="3" ry="3" width="90" height="10" />
|
||||
<Rect x="115" y="40" rx="3" ry="3" width="60" height="10" />
|
||||
<Rect x="185" y="40" rx="3" ry="3" width="60" height="10" />
|
||||
|
||||
<Rect x="0" y="60" rx="3" ry="3" width="30" height="10" />
|
||||
</Holder>
|
||||
)
|
||||
15
src/native/stylized/FacebookStyle.tsx
Normal file
15
src/native/stylized/FacebookStyle.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder, { Rect, Circle } from '../Holder'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props}>
|
||||
<Rect x="70" y="15" rx="4" ry="4" width="117" height="6" />
|
||||
<Rect x="70" y="35" rx="3" ry="3" width="85" height="6" />
|
||||
<Rect x="0" y="80" rx="3" ry="3" width="350" height="6" />
|
||||
<Rect x="0" y="100" rx="3" ry="3" width="380" height="6" />
|
||||
<Rect x="0" y="120" rx="3" ry="3" width="201" height="6" />
|
||||
<Circle cx="30" cy="30" r="30" />
|
||||
</Holder>
|
||||
)
|
||||
14
src/native/stylized/InstagramStyle.tsx
Normal file
14
src/native/stylized/InstagramStyle.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder, { Rect, Circle } from '../Holder'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props} height={480}>
|
||||
<Circle cx="30" cy="30" r="30" />
|
||||
|
||||
<Rect x="75" y="13" rx="4" ry="4" width="100" height="13" />
|
||||
<Rect x="75" y="37" rx="4" ry="4" width="50" height="8" />
|
||||
<Rect x="0" y="70" rx="5" ry="5" width="400" height="400" />
|
||||
</Holder>
|
||||
)
|
||||
15
src/native/stylized/ListStyle.tsx
Normal file
15
src/native/stylized/ListStyle.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder, { Rect } from '../Holder'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props}>
|
||||
<Rect x="0" y="0" rx="3" ry="3" width="250" height="10" />
|
||||
<Rect x="20" y="20" rx="3" ry="3" width="220" height="10" />
|
||||
<Rect x="20" y="40" rx="3" ry="3" width="170" height="10" />
|
||||
<Rect x="0" y="60" rx="3" ry="3" width="250" height="10" />
|
||||
<Rect x="20" y="80" rx="3" ry="3" width="200" height="10" />
|
||||
<Rect x="20" y="100" rx="3" ry="3" width="80" height="10" />
|
||||
</Holder>
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder from '../Holder'
|
||||
import { IContentLoaderProps } from '../interface'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder from '../Holder'
|
||||
import { IContentLoaderProps } from '../interface'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder from '../Holder'
|
||||
import { IContentLoaderProps } from '../interface'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder from '../Holder'
|
||||
import { IContentLoaderProps } from '../interface'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props} height={480}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import Holder from '../Holder'
|
||||
import { IContentLoaderProps } from '../interface'
|
||||
import { IContentLoaderProps } from '../'
|
||||
|
||||
export default (props: IContentLoaderProps) => (
|
||||
<Holder {...props}>
|
||||
|
||||
17
tsconfig.base.json
Normal file
17
tsconfig.base.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"lib": ["es6"],
|
||||
"declaration": true,
|
||||
"declarationDir": "./dist/types",
|
||||
"declarationMap": true,
|
||||
"noEmit": true,
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": false,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["./src/**/*"],
|
||||
"exclude": ["node_modules", "src/__tests__"]
|
||||
}
|
||||
@@ -1,18 +1,3 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationDir": "./dist/types",
|
||||
"outDir": "./dist",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true,
|
||||
"resolveJsonModule": true, // To load package.json
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
"extends": "./tsconfig.base.json"
|
||||
}
|
||||
|
||||
7
tsconfig.test.json
Normal file
7
tsconfig.test.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["dom"],
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": ["tslint:recommended", "tslint-config-prettier"],
|
||||
"jsRules": {},
|
||||
"rules": {},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
Reference in New Issue
Block a user