├── .codesandbox
└── tasks.json
├── .devcontainer
└── devcontainer.json
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
└── workflows
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── .prettierrc.js
├── .storybook
├── main.ts
├── preview-head.html
└── preview.tsx
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── __mocks__
└── react-native-svg.js
├── babel.config.js
├── docs
└── index.stories.tsx
├── jest.native.config.js
├── jest.web.config.js
├── package.json
├── pnpm-lock.yaml
├── rollup.config.js
├── src
├── native
│ ├── ContentLoader.tsx
│ ├── Svg.tsx
│ ├── __tests__
│ │ ├── ContentLoader.test.tsx
│ │ ├── Svg.test.tsx
│ │ ├── __snapshots__
│ │ │ └── snapshots.test.tsx.snap
│ │ ├── presets
│ │ │ ├── BulletListStyle.test.tsx
│ │ │ ├── CodeStyle.test.tsx
│ │ │ ├── FacebookStyle.test.tsx
│ │ │ ├── InstagramStyle.test.tsx
│ │ │ ├── ListStyle.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ ├── BulletListStyle.test.tsx.snap
│ │ │ │ ├── CodeStyle.test.tsx.snap
│ │ │ │ ├── FacebookStyle.test.tsx.snap
│ │ │ │ ├── InstagramStyle.test.tsx.snap
│ │ │ │ └── ListStyle.test.tsx.snap
│ │ └── snapshots.test.tsx
│ ├── index.ts
│ ├── package.json
│ └── presets
│ │ ├── BulletListStyle.tsx
│ │ ├── CodeStyle.tsx
│ │ ├── FacebookStyle.tsx
│ │ ├── InstagramStyle.tsx
│ │ └── ListStyle.tsx
├── shared
│ └── uid.ts
└── web
│ ├── ContentLoader.tsx
│ ├── Svg.tsx
│ ├── __tests__
│ ├── ContentLoader.test.tsx
│ ├── Svg.test.tsx
│ ├── __snapshots__
│ │ └── snapshots.test.tsx.snap
│ ├── index.test.tsx
│ ├── presets
│ │ ├── BulletListStyle.test.tsx
│ │ ├── CodeStyle.test.tsx
│ │ ├── FacebookStyle.test.tsx
│ │ ├── InstagramStyle.test.tsx
│ │ ├── ListStyle.test.tsx
│ │ └── __snapshots__
│ │ │ ├── BulletListStyle.test.tsx.snap
│ │ │ ├── CodeStyle.test.tsx.snap
│ │ │ ├── FacebookStyle.test.tsx.snap
│ │ │ ├── InstagramStyle.test.tsx.snap
│ │ │ └── ListStyle.test.tsx.snap
│ ├── snapshots.test.tsx
│ └── uid.test.tsx
│ ├── index.ts
│ └── presets
│ ├── BulletListStyle.tsx
│ ├── CodeStyle.tsx
│ ├── FacebookStyle.tsx
│ ├── InstagramStyle.tsx
│ └── ListStyle.tsx
├── tsconfig.base.json
├── tsconfig.json
└── tsconfig.test.json
/.codesandbox/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // These tasks will run in order when initializing your CodeSandbox project.
3 | "setupTasks": [
4 | {
5 | "name": "Install Dependencies",
6 | "command": "pnpm install"
7 | }
8 | ],
9 |
10 | // These tasks can be run from CodeSandbox. Running one will open a log in the app.
11 | "tasks": {
12 | "dev": {
13 | "name": "dev",
14 | "command": "pnpm run dev",
15 | "runAtStart": true
16 | },
17 | "build": {
18 | "name": "build",
19 | "command": "pnpm run build",
20 | "runAtStart": false
21 | },
22 | "build:docs": {
23 | "name": "build:docs",
24 | "command": "pnpm run build:docs",
25 | "runAtStart": false
26 | },
27 | "test": {
28 | "name": "test",
29 | "command": "pnpm run test",
30 | "runAtStart": false
31 | },
32 | "test:unit": {
33 | "name": "test:unit",
34 | "command": "pnpm run test:unit",
35 | "runAtStart": false
36 | },
37 | "test:unit:web": {
38 | "name": "test:unit:web",
39 | "command": "pnpm run test:unit:web",
40 | "runAtStart": false
41 | },
42 | "test:unit:native": {
43 | "name": "test:unit:native",
44 | "command": "pnpm run test:unit:native",
45 | "runAtStart": false
46 | },
47 | "test:watch": {
48 | "name": "test:watch",
49 | "command": "pnpm run test:watch",
50 | "runAtStart": false
51 | },
52 | "test:tsc": {
53 | "name": "test:tsc",
54 | "command": "pnpm run test:tsc",
55 | "runAtStart": false
56 | },
57 | "test:tsc:watch": {
58 | "name": "test:tsc:watch",
59 | "command": "pnpm run test:tsc:watch",
60 | "runAtStart": false
61 | },
62 | "commit": {
63 | "name": "commit",
64 | "command": "pnpm run commit",
65 | "runAtStart": false
66 | },
67 | "format": {
68 | "name": "format",
69 | "command": "pnpm run format",
70 | "runAtStart": false
71 | },
72 | "release": {
73 | "name": "release",
74 | "command": "pnpm run release",
75 | "runAtStart": false
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the
2 | // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
3 | {
4 | "name": "Node.js & TypeScript",
5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6 | "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye"
7 |
8 | // Features to add to the dev container. More info: https://containers.dev/features.
9 | // "features": {},
10 |
11 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
12 | // "forwardPorts": [],
13 |
14 | // Use 'postCreateCommand' to run commands after the container is created.
15 | // "postCreateCommand": "yarn install",
16 |
17 | // Configure tool-specific properties.
18 | // "customizations": {},
19 |
20 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
21 | // "remoteUser": "root"
22 | }
23 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [danilowoz]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## What did you do?
2 | Please include the actual source code causing the issue.
3 |
4 | ## What did you expect to happen?
5 | Please mention the expected behaviour.
6 |
7 | ## What happened actually?
8 |
9 | ### Which versions of react-content-loader, and which browser are affected by this issue?
10 | Please also mention the version of react.
11 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Summary
2 | In this section, you should give the overview of the problem and the proposed changes.
3 |
4 | ## Related Issue #[issue number]
5 | If this PR is fixing any issue, then please include - Related Issue #[issue number]
6 |
7 | ## Any Breaking Changes
8 | If this PR is introducing any breaking changes then mention them in this section.
9 |
10 | ## Checklist
11 | - [] Are all the test cases passing?
12 | - [] If any new feature has been added, then are the test cases updated/added?
13 | - [] Has the documentation been updated for the proposed change, if required?
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for more information:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 | # https://containers.dev/guide/dependabot
6 |
7 | version: 2
8 | updates:
9 | - package-ecosystem: "devcontainers"
10 | directory: "/"
11 | schedule:
12 | interval: weekly
13 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - master
8 |
9 | jobs:
10 | check:
11 | runs-on: ubuntu-latest
12 | name: Lint, typecheck and test
13 |
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v3
17 |
18 | - uses: pnpm/action-setup@v3
19 | with:
20 | version: 8
21 |
22 | - name: Use Node.js 20
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: 20
26 | cache: 'pnpm'
27 |
28 | - name: Install dependencies
29 | run: pnpm i
30 |
31 | - name: Run tests
32 | run: pnpm run test
33 |
34 | - name: Build
35 | run: pnpm run build
36 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | prepare:
10 | runs-on: ubuntu-latest
11 | name: Checks
12 |
13 | steps:
14 | - name: Checkout code
15 | uses: actions/checkout@v3
16 |
17 | - uses: pnpm/action-setup@v3
18 | with:
19 | version: 8
20 |
21 | - name: Use Node.js 20
22 | uses: actions/setup-node@v3
23 | with:
24 | node-version: 20
25 | cache: 'pnpm'
26 |
27 | - name: Install dependencies
28 | run: pnpm i
29 |
30 | - name: Run tests
31 | run: pnpm run test
32 |
33 | build-and-release:
34 | runs-on: ubuntu-latest
35 | needs: prepare
36 | name: Release
37 |
38 | steps:
39 | - name: Checkout code
40 | uses: actions/checkout@v3
41 |
42 | - uses: pnpm/action-setup@v3
43 | with:
44 | version: 8
45 |
46 | - name: Use Node.js 20
47 | uses: actions/setup-node@v3
48 | with:
49 | node-version: 20
50 | cache: 'pnpm'
51 |
52 | - name: Install dependencies
53 | run: pnpm i
54 |
55 | - name: Build
56 | run: pnpm run build
57 |
58 | - name: Release
59 | env:
60 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62 | run: pnpm run release
63 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *~
3 | *.iml
4 | .*.haste_cache.*
5 | .DS_Store
6 | .idea
7 | npm-debug.log
8 | yarn-error.log
9 | node_modules
10 | dist
11 | coverage
12 | /native
13 | .docz/
14 | .rpt2_cache
15 | settings.json
16 | *.code-workspace
17 | yarn.lock
18 | yarn-error.log
19 | docs-build
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = require("@significa/prettier-config");
2 |
--------------------------------------------------------------------------------
/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import type { StorybookConfig } from "@storybook/react-vite";
2 | const config: StorybookConfig = {
3 | stories: ["../docs/**/*.mdx", "../docs/**/*.stories.@(js|jsx|ts|tsx)"],
4 | addons: [
5 |
6 | ],
7 | framework: {
8 | name: "@storybook/react-vite",
9 | options: {},
10 | },
11 | };
12 | export default config;
13 |
--------------------------------------------------------------------------------
/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.storybook/preview.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import type { Preview } from "@storybook/react";
4 |
5 | const preview: Preview = {
6 | parameters: {
7 |
8 | },
9 | decorators: [
10 | (Story) => (
11 |
12 |
13 |
14 | ),
15 | ],
16 | };
17 |
18 | export default preview;
19 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at danilowoz@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Julian Ćwirko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | SVG-Powered component to easily create placeholder loadings (like Facebook's cards loading).
9 |
10 | ## Features
11 |
12 | - :gear: **Customizable:** Feel free to change the colors, speed, sizes, and even **RTL**;
13 | - :ok_hand: **Plug and play:** with many presets to use, see the [examples](http://danilowoz.com/create-content-loader/#gallery);
14 | - :pencil2: **DIY:** use the [create-content-loader](https://danilowoz.com/create-content-loader) to create your own custom loaders easily;
15 | - 📱 **React Native support**: same API, as same powerful features;
16 | - ⚛️ **Really lightweight:** less than **2kB** and **0 dependencies** for web version;
17 |
18 | ## Index
19 |
20 | - [Getting Started](#gettingstarted)
21 | - [Usage](#usage)
22 | - [Native](#native)
23 | - [Options](#options)
24 | - [Examples](#examples)
25 | - [Troubleshooting](#troubleshooting)
26 | - [Similar packages](#similarpackages)
27 | - [Development](#development)
28 |
29 | ## Getting Started
30 |
31 | ```sh
32 | npm i react-content-loader --save
33 | ```
34 |
35 | ```sh
36 | yarn add react-content-loader
37 | ```
38 |
39 | ### For React Native
40 |
41 | ```sh
42 | npm i react-content-loader react-native-svg --save
43 | ```
44 |
45 | ```sh
46 | yarn add react-content-loader react-native-svg
47 | ```
48 |
49 | CDN from [JSDELIVR](https://www.jsdelivr.com/package/npm/react-content-loader)
50 |
51 | ## Usage
52 |
53 | There are two ways to use it:
54 |
55 | **1. Presets, see the [examples](https://danilowoz.com/react-content-loader/):**
56 |
57 | ```jsx
58 | import ContentLoader, { Facebook } from 'react-content-loader'
59 |
60 | const MyLoader = () =>
61 | const MyFacebookLoader = () =>
62 | ```
63 |
64 | **2. Custom mode, see the [online tool](https://danilowoz.com/create-content-loader)**
65 |
66 | ```jsx
67 | const MyLoader = () => (
68 |
69 | {/* Only SVG shapes */}
70 |
71 |
72 |
73 |
74 | )
75 | ```
76 |
77 | **Still not clear?** Take a look at this working example at [codesandbox.io](https://codesandbox.io/s/moojk887z9)
78 | Or try the components editable demo hands-on and install it from [bit.dev](https://bit.dev/danilowoz/react-content-loader)
79 |
80 | ## Native
81 |
82 | `react-content-loader` can be used with React Native in the same way as web version with the same import:
83 |
84 | **1. Presets, see the [examples](#examples):**
85 |
86 | ```jsx
87 | import ContentLoader, { Facebook } from 'react-content-loader/native'
88 |
89 | const MyLoader = () =>
90 | const MyFacebookLoader = () =>
91 | ```
92 |
93 | **2. Custom mode**
94 |
95 | **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:
96 |
97 | ```jsx
98 | import ContentLoader, { Rect, Circle } from 'react-content-loader/native'
99 |
100 | const MyLoader = () => (
101 |
102 |
103 |
104 |
105 |
106 | )
107 | ```
108 |
109 | ## Options
110 |
111 | |
Prop name and type
| Environment | Description |
112 | | ---------------------------------------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
113 | | **`animate?: boolean`** Defaults to `true` | React DOM React Native | Opt-out of animations with `false` |
114 | | **`title?: string`** Defaults to `Loading...` | React DOM only | It's used to describe what element it is. Use `''` (empty string) to remove. |
115 | | **`baseUrl?: string`** Defaults to an empty string | React DOM only | Required if you're using `` document ``. This prop is common used as: `` which will fill the SVG attribute with the relative path. Related [#93](https://github.com/danilowoz/react-content-loader/issues/93). |
116 | | **`speed?: number`** Defaults to `1.2` | React DOM React Native | Animation speed in seconds. |
117 | | **`viewBox?: string`** Defaults to `undefined` | React DOM React Native | Use viewBox props to set a custom viewBox value, for more information about how to use it, read the article [How to Scale SVG](https://css-tricks.com/scale-svg/). |
118 | | **`gradientRatio?: number`** Defaults to `1.2` | React DOM only | Width of the animated gradient as a fraction of the view box width. |
119 | | **`rtl?: boolean`** Defaults to `false` | React DOM React Native | Content right-to-left. |
120 | | **`backgroundColor?: string`** Defaults to `#f5f6f7` | React DOM React Native | Used as background of animation. |
121 | | **`foregroundColor?: string`** Defaults to `#eee` | React DOM React Native | Used as the foreground of animation. |
122 | | **`backgroundOpacity?: number`** Defaults to `1` | React DOM React Native | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in [Safari](#safari--ios) |
123 | | **`foregroundOpacity?: number`** Defaults to `1` | React DOM React Native | Animation opacity (0 = transparent, 1 = opaque) used to solve an issue in [Safari](#safari--ios) |
124 | | **`style?: React.CSSProperties`** Defaults to `{}` | React DOM only | |
125 | | **`uniqueKey?: string`** Defaults to random unique id | React DOM only | 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). |
126 | | **`beforeMask?: JSX.Element`** Defaults to null | React DOM React Native | Define custom shapes before content, see more [here](https://github.com/danilowoz/react-content-loader/issues/266). |
127 |
128 | See all options [live](https://danilowoz.com/react-content-loader/)
129 |
130 | ## Examples
131 |
132 | ##### Facebook Style
133 |
134 | ```jsx
135 | import { Facebook } from 'react-content-loader'
136 |
137 | const MyFacebookLoader = () =>
138 | ```
139 |
140 |
141 |
142 | ##### Instagram Style
143 |
144 | ```jsx
145 | import { Instagram } from 'react-content-loader'
146 |
147 | const MyInstagramLoader = () =>
148 | ```
149 |
150 |
151 |
152 | ##### Code Style
153 |
154 | ```jsx
155 | import { Code } from 'react-content-loader'
156 |
157 | const MyCodeLoader = () =>
158 | ```
159 |
160 |
161 |
162 | ##### List Style
163 |
164 | ```jsx
165 | import { List } from 'react-content-loader'
166 |
167 | const MyListLoader = () =>
168 | ```
169 |
170 |
171 |
172 | ##### Bullet list Style
173 |
174 | ```jsx
175 | import { BulletList } from 'react-content-loader'
176 |
177 | const MyBulletListLoader = () =>
178 | ```
179 |
180 |
181 |
182 | ### Custom Style
183 |
184 | For the custom mode, use the
185 | [online tool](https://danilowoz.com/create-content-loader).
186 |
187 | ```jsx
188 | const MyLoader = () => (
189 |
196 | {/* Only SVG shapes */}
197 |
198 |
199 |
200 |
201 | )
202 | ```
203 |
204 | 
205 |
206 | ## Troubleshooting
207 |
208 | #### Responsive - Mobile version
209 |
210 | In order to avoid unexpected behavior, the package doesn't have opinioned settings. So if it needs to be responsive, have in mind that the output of the package is a regular SVG, so it just needs the same attributes to become a regular SVG responsive, which means:
211 |
212 | ```jsx
213 | import { Code } from 'react-content-loader'
214 |
215 | const MyCodeLoader = () => (
216 |
222 | )
223 | ```
224 |
225 | #### Server-side rendering (SSR) - Match snapshot
226 |
227 | As the main component generates random values to match the id of the SVG element with background style, it can encounter unexpected errors and unmatching warning on render, once the random value of id will be generated twice, in case of SSR: server and client; or in case of snapshot test: on the first match and re-running the test.
228 |
229 | To fix it, set the prop [`uniqueKey`](https://github.com/danilowoz/react-content-loader#uniquekey-string---web-only), then the id will not be random anymore:
230 |
231 | ```jsx
232 | import { Facebook } from 'react-content-loader'
233 |
234 | const MyFacebookLoader = () =>
235 | ```
236 |
237 | #### **Alpha is not working: Safari / iOS**
238 |
239 | When using `rgba` as a `backgroundColor` or `foregroundColor` 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 a `rgba` value for `backgroundColor`/`foregroundColor`, use the `rgb` equivalent and move the alpha channel value to the `backgroundOpacity`/`foregroundOpacity` props.
240 |
241 | ```jsx
242 | {/* Opaque color in Safari and iOS */}
243 |
246 |
247 |
248 | {/_ Semi-transparent color in Safari and iOS _/}
249 |
254 |
255 |
256 | ```
257 |
258 | #### **Black box in Safari / iOS (again)**
259 |
260 | Using the 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 `` and the issue has been solved.
261 |
262 |
263 |
264 | See: [#93](https://github.com/danilowoz/react-content-loader/issues/93) / [109](https://github.com/danilowoz/react-content-loader/issues/109)
265 |
266 | #### Browser supports SVG-Animate
267 |
268 | Old browsers don't support animation in SVG ([compatibility list](https://caniuse.com/#search=SVGAnimateElement)), and if your project must support IE, for examples, here's a couple of ways to make sure that browser supports SVG Animate:
269 |
270 | - `window.SVGAnimateElement`
271 | - `document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#SVG-Animation", "1.1")`
272 | - Or even use https://modernizr.com/
273 |
274 | ## Similar packages
275 |
276 | - 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);
277 | - [Preact](https://github.com/bonitasoft/preact-content-loader);
278 | - Vue.js: [vue-content-loading](https://github.com/LucasLeandro1204/vue-content-loading), [vue-content-loader](https://github.com/egoist/vue-content-loader);
279 | - Angular: [ngx-content-loading](https://github.com/Gbuomprisco/ngx-content-loading), [ngx-content-loader](https://github.com/NetanelBasal/ngx-content-loader).
280 |
281 | ---
282 |
283 | ## Development
284 |
285 | Fork the repo and then clone it
286 |
287 | ```
288 | $ git clone git@github.com:YourUsername/react-content-loader.git && cd react-content-loader
289 | ```
290 |
291 | `$ npm i`: Install the dependencies;
292 |
293 | `$ npm run build`: Build to production;
294 |
295 | `$ npm run dev`: Run the Storybook to see your changes;
296 |
297 | `$ npm run test`: Run all tests: type checking, unit tests on web and native;
298 |
299 | `$ npm run test:watch`: Watch unit tests;
300 |
301 | ### React Native
302 |
303 | As React Native doesn't support symbolic links (to link the dependency to another folder) and as there is no playground to check your contributions (like storybook), this is recommended strategy to run the project locally:
304 |
305 | 1. Create a new React Native from scratch, either Metro or create-react-native-app;
306 | 2. Install the dependency to your root project:
307 | `yarn add react-content-loader react-native-svg`
308 | 3. Open the project just created and clone this repository there;
309 | 4. Create your loading component and point the `react-content-loader` to the project just cloned, like:
310 | `import ContentLoader, { Rect, Circle } from './react-content-loader/native'`
311 |
312 | ### Commit messages
313 |
314 | 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.
315 |
316 | ## License
317 |
318 | [MIT](https://github.com/danilowoz/react-content-loader/blob/master/LICENSE)
319 |
--------------------------------------------------------------------------------
/__mocks__/react-native-svg.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const createComponent = function(name) {
4 | return class extends React.Component {
5 | // overwrite the displayName, since this is a class created dynamically
6 | static displayName = name;
7 |
8 | render() {
9 | return React.createElement(name, this.props, this.props.children);
10 | }
11 | };
12 | };
13 |
14 | // Mock all react-native-svg exports
15 | // from https://github.com/magicismight/react-native-svg/blob/master/index.js
16 | const Svg = createComponent('Svg');
17 | const Circle = createComponent('Circle');
18 | const Ellipse = createComponent('Ellipse');
19 | const G = createComponent('G');
20 | const Text = createComponent('Text');
21 | const TextPath = createComponent('TextPath');
22 | const TSpan = createComponent('TSpan');
23 | const Path = createComponent('Path');
24 | const Polygon = createComponent('Polygon');
25 | const Polyline = createComponent('Polyline');
26 | const Line = createComponent('Line');
27 | const Rect = createComponent('Rect');
28 | const Use = createComponent('Use');
29 | const Image = createComponent('Image');
30 | const Symbol = createComponent('Symbol');
31 | const Defs = createComponent('Defs');
32 | const LinearGradient = createComponent('LinearGradient');
33 | const RadialGradient = createComponent('RadialGradient');
34 | const Stop = createComponent('Stop');
35 | const ClipPath = createComponent('ClipPath');
36 | const Pattern = createComponent('Pattern');
37 | const Mask = createComponent('Mask');
38 |
39 | export {
40 | Svg,
41 | Circle,
42 | Ellipse,
43 | G,
44 | Text,
45 | TextPath,
46 | TSpan,
47 | Path,
48 | Polygon,
49 | Polyline,
50 | Line,
51 | Rect,
52 | Use,
53 | Image,
54 | Symbol,
55 | Defs,
56 | LinearGradient,
57 | RadialGradient,
58 | Stop,
59 | ClipPath,
60 | Pattern,
61 | Mask,
62 | };
63 |
64 | export default Svg;
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | 'module:metro-react-native-babel-preset',
4 | '@babel/preset-typescript',
5 | ],
6 | plugins: [['@babel/plugin-transform-private-methods', { loose: true }]],
7 | }
8 |
--------------------------------------------------------------------------------
/docs/index.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SyntaxHighlighter from 'react-syntax-highlighter'
3 | import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs'
4 |
5 | import ContentLoader, {
6 | BulletList,
7 | Code,
8 | Facebook,
9 | Instagram,
10 | List,
11 | } from '../src/web'
12 |
13 | export default {
14 | title: 'React Content Loader',
15 | }
16 |
17 | const SyntaxCode = ({ children }) => {
18 | return (
19 |
20 | {children}
21 |
22 | )
23 | }
24 |
25 | /**
26 | * Animated
27 | */
28 | export const animate = () => {
29 | return (
30 | <>
31 | {''}
32 |
33 |
34 | {''}
35 |
36 | >
37 | )
38 | }
39 |
40 | animate.story = {
41 | parameters: {
42 | notes: `##\`animate?: boolean\`
43 |
44 | Defaults to \`true\`. Opt-out of animations with \`false\``,
45 | },
46 | }
47 |
48 | /**
49 | * Background color
50 | */
51 | export const backgroundColor = () => {
52 | return (
53 | <>
54 | {''}
55 |
56 | >
57 | )
58 | }
59 |
60 | backgroundColor.story = {
61 | parameters: {
62 | notes: `## \`backgroundColor?: string\`
63 |
64 | Defaults to \`#f5f6f7\` which is used as background of animation.`,
65 | },
66 | }
67 |
68 | /**
69 | * Foreground color
70 | */
71 | export const foregroundColor = () => {
72 | return (
73 | <>
74 | {''}
75 |
76 | >
77 | )
78 | }
79 |
80 | foregroundColor.story = {
81 | parameters: {
82 | notes: `## \`foregroundColor?: string\`
83 |
84 | Defaults to \`#eee\` which is used as foreground of animation.`,
85 | },
86 | }
87 |
88 | /**
89 | * Background opacity
90 | */
91 | export const backgroundOpacity = () => {
92 | return (
93 | <>
94 | {''}
95 |
96 | >
97 | )
98 | }
99 |
100 | backgroundOpacity.story = {
101 | parameters: {
102 | notes: `## \`backgroundOpacity?: number\`
103 |
104 | Defaults to \`1\`. Background opacity (0 = transparent, 1 = opaque) used to solve a issue in [Safari](#bugfix-in-safari)`,
105 | },
106 | }
107 |
108 | /**
109 | * Foreground opacity
110 | */
111 | export const foregroundOpacity = () => {
112 | return (
113 | <>
114 | {''}
115 |
116 | >
117 | )
118 | }
119 |
120 | foregroundOpacity.story = {
121 | parameters: {
122 | notes: `## \`foregroundOpacity?: number\`
123 |
124 | Defaults to \`1\`. Animation opacity (0 = transparent, 1 = opaque) used to solve a issue in [Safari](#bugfix-in-safari)`,
125 | },
126 | }
127 |
128 | /**
129 | * Base URL
130 | */
131 | export const baseURL = () => {
132 | return (
133 | <>
134 | {''}
135 |
136 | >
137 | )
138 | }
139 |
140 | baseURL.story = {
141 | parameters: {
142 | notes: `## \`baseUrl?: string\`
143 |
144 | Required if you're using \`\` in the \`\`. Defaults to an empty string. This prop is common used as: \`\` which will fill the SVG attribute with the relative path. Related [#93](https://github.com/danilowoz/react-content-loader/issues/93).
145 | `,
146 | },
147 | }
148 |
149 | /**
150 | * Children
151 | */
152 | export const children = () => {
153 | return (
154 | <>
155 |