├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ └── deploy-website.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── blog │ └── .gitkeep ├── docs │ ├── installation.md │ ├── introduction.md │ ├── playground.md │ ├── theming.md │ └── usage.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── components │ │ ├── DemoCodes │ │ │ ├── AnchorTag.js │ │ │ ├── Colors.js │ │ │ ├── Size.js │ │ │ ├── Style.js │ │ │ ├── Theming.js │ │ │ └── UsingIcons.js │ │ ├── Example │ │ │ ├── Example.js │ │ │ ├── Example.scss │ │ │ ├── Example1.js │ │ │ ├── Example2.js │ │ │ ├── Example3.js │ │ │ └── Example4.js │ │ └── Playground │ │ │ ├── EditorPlayground.js │ │ │ ├── EditorPlayground.scss │ │ │ ├── InteractivePlayground.js │ │ │ ├── InteractivePlayground.scss │ │ │ └── Playground.js │ ├── css │ │ ├── bootstrap.scss │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ └── index.module.css └── static │ ├── .nojekyll │ └── img │ ├── dependencies.svg │ ├── favicon.ico │ ├── logo │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── logo.png │ ├── preview.gif │ └── social_preview.png ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── css │ └── index.css └── index.js └── types └── index.d.ts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | stats.html 24 | 25 | # docs 26 | 27 | # Dependencies 28 | /docs/node_modules 29 | 30 | # Production 31 | /docs/build 32 | 33 | # Generated files 34 | /docs/.docusaurus 35 | /docs/.cache-loader 36 | 37 | # Misc 38 | /docs/.DS_Store 39 | /docs/.env.local 40 | /docs/.env.development.local 41 | /docs/.env.test.local 42 | /docs/.env.production.local 43 | 44 | /docs/npm-debug.log* 45 | /docs/yarn-debug.log* 46 | /docs/yarn-error.log* 47 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // .eslintrc.js 2 | module.exports = { 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | node: true, 7 | }, 8 | extends: [ 9 | 'eslint:recommended', 10 | 'plugin:react/recommended', 11 | 'plugin:prettier/recommended', 12 | ], 13 | settings: { 14 | react: { 15 | version: 'detect', 16 | }, 17 | }, 18 | parserOptions: { 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | ecmaVersion: 'latest', 23 | sourceType: 'module', 24 | }, 25 | plugins: ['react'], 26 | rules: { 27 | 'react/react-in-jsx-scope': 'off', 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'monthly' 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/deploy-website.yml: -------------------------------------------------------------------------------- 1 | name: Deploy docs to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | paths: 9 | - docs/** 10 | 11 | workflow_dispatch: 12 | 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | concurrency: 19 | group: ${{ github.workflow }}-${{ github.ref }} 20 | cancel-in-progress: true 21 | 22 | defaults: 23 | run: 24 | working-directory: ./docs 25 | 26 | jobs: 27 | build: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v3 31 | 32 | - uses: actions/setup-node@v3 33 | with: 34 | node-version: 16.x 35 | cache: 'npm' 36 | cache-dependency-path: docs/package-lock.json 37 | 38 | - name: Setup Pages 39 | uses: actions/configure-pages@v1 40 | 41 | - name: Restore cache 42 | uses: actions/cache@v3 43 | with: 44 | path: | 45 | **/node_modules 46 | key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} 47 | 48 | - name: Install dependencies 49 | run: npm ci --legacy-peer-deps 50 | 51 | - name: Build with docusaurus 52 | run: npm run build 53 | 54 | - name: Upload artifact 55 | uses: actions/upload-pages-artifact@v1 56 | with: 57 | path: ./docs/build 58 | 59 | deploy: 60 | environment: 61 | name: github-pages 62 | url: ${{ steps.deployment.outputs.page_url }} 63 | runs-on: ubuntu-latest 64 | needs: build 65 | steps: 66 | - name: Deploy to GitHub Pages 67 | id: deployment 68 | uses: actions/deploy-pages@v1 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | stats.html 24 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /src 2 | /docs 3 | /.github 4 | .babelrc 5 | .eslintignore 6 | .eslintrc.js 7 | .prettierignore 8 | .prettierrc 9 | webpack.config.js 10 | CODE_OF_CONDUCT.md 11 | CONTRIBUTING.md 12 | rollup.config.js 13 | stats.html 14 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | stats.html 24 | 25 | # docs 26 | 27 | # Dependencies 28 | /docs/node_modules 29 | 30 | # Production 31 | /docs/build 32 | 33 | # Generated files 34 | /docs/.docusaurus 35 | /docs/.cache-loader 36 | 37 | # Misc 38 | /docs/.DS_Store 39 | /docs/.env.local 40 | /docs/.env.development.local 41 | /docs/.env.test.local 42 | /docs/.env.production.local 43 | 44 | /docs/npm-debug.log* 45 | /docs/yarn-debug.log* 46 | /docs/yarn-error.log* 47 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "arrowParens": "always", 4 | "bracketSpacing": true, 5 | "printWidth": 80, 6 | "singleQuote": true, 7 | "tabWidth": 2 8 | } 9 | -------------------------------------------------------------------------------- /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 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at fdkhadra@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍 4 | 5 | If you have found an issue or would like to request a new feature, simply create a new issue detailing the request. We also welcome pull requests. See below for information on getting started with development and submitting pull requests. 6 | 7 | Please note we have a [code of conduct](https://github.com/arifszn/reactive-button/blob/main/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 8 | 9 | ## Found an Issue? 10 | 11 | If you find a bug in the source code or a mistake in the documentation, you can help us by 12 | submitting an issue to our [GitHub Repository](https://github.com/arifszn/reactive-button/issues/new). Even better you can submit a Pull Request 13 | with a fix. 14 | 15 | ## Submitting a Pull Request 16 | 17 | - If applicable, update the `readme` 18 | - Use `npm run lint` and `npm run prettier` before committing 19 | - Example for a commit message 20 | 21 | ``` 22 | Fix type validation for typescript 23 | ``` 24 | 25 | ### Developing 26 | 27 | Fork, then clone the repo: 28 | 29 | ```sh 30 | git clone https://github.com/your-username/reactive-button.git 31 | cd reactive-button 32 | ``` 33 | 34 | Install dependencies: 35 | 36 | ```sh 37 | npm install 38 | ``` 39 | 40 | ### Building 41 | 42 | Running the `build` task will create the `dist` version of the project. 43 | 44 | ```sh 45 | npm run build 46 | ``` 47 | 48 | ### Project Structure 49 | 50 | ```text 51 | root 52 | ├── dist 53 | └── src 54 | └── css 55 | └── types 56 | ``` 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ariful Alam 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | 5 | 6 |

3D animated react button component with progress bar.

7 | 8 |

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |

42 | 43 |

44 | Documentation 45 | · 46 | Report Bug 47 | · 48 | Request Feature 49 |

50 |

51 | 52 |

53 | 54 | Reactive Button Preview 55 | 56 |

57 | 58 | **Reactive Button** is a 3D animated react component to replace the traditional boring buttons. Change your button style without adding any UI framework. Comes with progress bar and the goal is to let the users visualize what is happening after a button click. 59 | 60 | - 3D 61 | - Animated 62 | - Supports icons 63 | - Zero dependency 64 | - Progress indicator 65 | - Notification message 66 | - Supports TypeScript 67 | - Super easy to setup 68 | - Extremely lightweight 69 | - Super easy to customize 70 | - And much more ! 71 | 72 | ## Resources 73 | 74 | - [Documentation](https://arifszn.github.io/reactive-button) 75 | - [Playground](https://arifszn.github.io/reactive-button/docs/playground) 76 | - [StackBlitz](https://stackblitz.com/edit/reactive-button) 77 | 78 | ## Installation 79 | 80 | Install via NPM. 81 | 82 | ```sh 83 | npm install reactive-button 84 | ``` 85 | 86 | Or via Yarn. 87 | 88 | ```sh 89 | yarn add reactive-button 90 | ``` 91 | 92 | ## Usage 93 | 94 | The targets of the below example: 95 | 96 | - Show a button showing the text '_Submit_'. 97 | - After clicking the button, change the button text to '_Loading_' and send an HTTP request. 98 | - Upon successful request, change the button text to '_Done_' as success notification. 99 | 100 | ```jsx 101 | import { useState } from 'react'; 102 | import ReactiveButton from 'reactive-button'; 103 | 104 | function App() { 105 | const [state, setState] = useState('idle'); 106 | 107 | const onClickHandler = () => { 108 | setState('loading'); 109 | 110 | // send an HTTP request 111 | setTimeout(() => { 112 | setState('success'); 113 | }, 2000); 114 | }; 115 | 116 | return ( 117 | 124 | ); 125 | } 126 | 127 | export default App; 128 | ``` 129 | 130 | ### Other Usage 131 | 132 | Reactive Button has all the functionalities of a normal button. 133 | 134 | #### Color 135 | 136 | It comes with 10 default color options. 137 | 138 | ```jsx 139 | return ( 140 | <> 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | ); 153 | ``` 154 | 155 | #### Size 156 | 157 | There are 4 sizes available. 158 | 159 | ```jsx 160 | return ( 161 | <> 162 | 163 | 164 | 165 | 166 | 167 | ); 168 | ``` 169 | 170 | #### Style 171 | 172 | Make the buttons more beautiful with these customization options. 173 | 174 | ```jsx 175 | return ( 176 | <> 177 | 178 | 179 | 180 | 181 | ); 182 | ``` 183 | 184 | #### Existing State 185 | 186 | In your project, There might be existing state for loading indicator which accepts boolean value only. If you don't want to define new state for Reactive Button, then utilize the existing state. 187 | 188 | ```jsx 189 | const [loading, setLoading] = useState(false); 190 | 191 | return ( 192 | 197 | ); 198 | ``` 199 | 200 | #### Without State 201 | 202 | `state` is not mandatory. 203 | 204 | ```jsx 205 | return ; 206 | ``` 207 | 208 | #### Using Icons 209 | 210 | You can use your own icons. Don't forget to wrap them with a parent element. 211 | 212 | ```jsx 213 | return ( 214 | 217 | Send 218 | 219 | } 220 | /> 221 | ); 222 | ``` 223 | 224 | #### Form Submit 225 | 226 | If you need to submit form by button clicking, set the type prop as 'submit'. 227 | 228 | ```jsx 229 | return ( 230 |
231 | 232 | 233 | 234 | 235 | ); 236 | ``` 237 | 238 | #### Anchor Tag 239 | 240 | To use Reactive button as anchor tag, simply wrap it with an anchor tag. 241 | 242 | ```jsx 243 | return ( 244 | 245 | 246 | 247 | ); 248 | ``` 249 | 250 | ## Available Props 251 | 252 | | Props | Type | Description | Default | 253 | | :-------------: | :---------------------: | :-------------------------------------------------------------------------------------------------------------------------------: | :----------: | 254 | | buttonState | `string` | `'idle'` \| `'loading'` \| `'success'` \| `'error'` | `'idle'` | 255 | | onClick | `function` | Callback function when clicking button | `() => {}` | 256 | | color | `string` | `'primary'` \| `'secondary'` \| `'dark'` \| `'light'` \| `'green'` \| `'red'` \| `'yellow'` \| `'teal'` \| `'violet'` \| `'blue'` | `'primary'` | 257 | | idleText | `string` \| `ReactNode` | Button text when idle | `'Click Me'` | 258 | | loadingText | `string` \| `ReactNode` | Button text when loading | `'Loading'` | 259 | | successText | `string` \| `ReactNode` | Button text when loading successful | `'Success'` | 260 | | errorText | `string` \| `ReactNode` | Button text when loading failed | `'Error'` | 261 | | type | `string` | `'button'` \| `'submit'` \| `'reset'` | `'button'` | 262 | | className | `string` | Button classnames | `''` | 263 | | style | `React.CSSProperties` | Custom style | `{}` | 264 | | outline | `boolean` | Enable outline effect | `false` | 265 | | shadow | `boolean` | Enable shadow effect | `false` | 266 | | rounded | `boolean` | Enable rounded button | `false` | 267 | | size | `string` | `'tiny'` \| `'small'` \| `'normal'` \| `'large'` | `'normal'` | 268 | | block | `boolean` | Block Button | `false` | 269 | | messageDuration | `number` | Success/Error message duration in millisecond | `2000` | 270 | | disabled | `boolean` | Disable button | `false` | 271 | | buttonRef | `React.Ref` \| `null` | Button reference | `null` | 272 | | width | `string` \| `null` | Override button width | `null` | 273 | | height | `string` \| `null` | Override button height | `null` | 274 | | animation | `boolean` | Button hover and click animation | `true` | 275 | 276 | ## Support 277 | 278 |

You can show your support by starring this project.

279 | 280 | Github Star 281 | 282 | 283 | ## Contribute 284 | 285 | To contribute, see the [contributing guide](https://github.com/arifszn/reactive-button/blob/main/CONTRIBUTING.md). 286 | 287 | ## License 288 | 289 | [MIT License](https://github.com/arifszn/reactive-button/blob/main/LICENSE) 290 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Reactive Button Doc 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ## Installation 6 | 7 | ```console 8 | yarn install 9 | ``` 10 | 11 | ## Local Development 12 | 13 | ```console 14 | yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ## Build 20 | 21 | ```console 22 | yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ## Deployment 28 | 29 | ```console 30 | GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/blog/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/blog/.gitkeep -------------------------------------------------------------------------------- /docs/docs/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: installation 3 | title: Installation 4 | --- 5 | 6 | ## Requirements 7 | 8 | - React version >= `16.8 or above` 9 | 10 | ## Installation 11 | 12 | Install via NPM 13 | 14 | ```sh 15 | npm install reactive-button 16 | ``` 17 | 18 | Install via Yarn 19 | 20 | ```sh 21 | yarn add reactive-button 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/docs/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: introduction 3 | title: Introduction 4 | sidebar_label: Introduction 5 | hide_title: true 6 | --- 7 | 8 |

9 | 10 | 11 | 12 |   13 | 14 | 15 | 16 |   17 | 18 |   19 | 20 |   21 | 22 | 23 | 24 |   25 | 26 | 27 | 28 |   29 | 30 | 31 | 32 |   33 | 34 | 35 | 36 |   37 | 38 | 39 | 40 |   41 | 42 | 43 | 44 |   45 | 46 | 47 | 48 |   49 | 50 | 51 | 52 |

53 | 54 | **Reactive Button** is a 3D animated react component to replace the traditional boring buttons. Change your button style without adding any UI framework. Comes with progress bar and the goal is to let the users visualize what is happening after a button click. 55 | 56 | ## Example 57 | 58 | import Example from '../src/components/Example/Example'; 59 | 60 | 61 | 62 | ## Features 63 | 64 | - 3D 65 | - Animated 66 | - Supports icons 67 | - Zero dependency 68 | - Progress indicator 69 | - Notification message 70 | - Supports TypeScript 71 | - Super easy to setup 72 | - Extremely lightweight 73 | - Super easy to customize 74 | - And much more ! 75 | 76 | ## Support 77 | 78 |

You can show your support by starring this project.

79 | 80 | Github Star 81 | 82 | 83 | ## Contribute 84 | 85 | To contribute, see the [contributing guide](https://github.com/arifszn/reactive-button/blob/main/CONTRIBUTING.md). 86 | 87 | ## License 88 | 89 | [MIT License](https://github.com/arifszn/reactive-button/blob/main/LICENSE) 90 | -------------------------------------------------------------------------------- /docs/docs/playground.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: playground 3 | title: Playground 4 | --- 5 | 6 | Playground for Reactive Button. 7 | 8 | ## Interactive Playground 9 | 10 | Toggle or change the props to see changes. 11 | 12 | import InteractivePlayground from '../src/components/Playground/InteractivePlayground'; 13 | 14 | 15 | 16 | ## Editor Playground 17 | 18 | Edit code to see changes. 19 | 20 | import EditorPlayground from '../src/components/Playground/EditorPlayground'; 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/docs/theming.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: theming 3 | title: Theming 4 | --- 5 | 6 | Reactive Button provides flexible styling option. Most of the common style changes can be achieved by using the style prop. 7 | 8 | import Playground from '../src/components/Playground/Playground' 9 | import Theming from '../src/components/DemoCodes/Theming' 10 | import ReactiveButton from 'reactive-button' 11 | 12 |
13 | 18 |
19 | 20 | However, you are free to create your own theme. 21 | 22 | ## Custom Theme 23 | 24 | Modify the values how you prefer and use it in your existing css file. 25 | 26 | 27 | 28 | ```css 29 | .reactive-btn-wrapper, 30 | .reactive-btn { 31 | --reactive-button-min-width: 100px !important; 32 | --reactive-button-min-height: 35px !important; 33 | --reactive-button-font-size: 14px !important; 34 | --reactive-button-font-weight: 400 !important; 35 | --reactive-button-border-radius: 0px !important; 36 | --reactive-button-text-color: #ffffff !important; 37 | --reactive-progress-color: rgba(0, 0, 0, 0.15) !important; 38 | 39 | --reactive-button-primary-color: rgba(88, 103, 221, 1) !important; 40 | --reactive-button-secondary-color: rgba(108, 117, 125, 1) !important; 41 | --reactive-button-dark-color: rgba(66, 78, 106, 1) !important; 42 | --reactive-button-light-color: rgba(183, 186, 191, 1) !important; 43 | --reactive-button-green-color: rgba(37, 162, 51, 1) !important; 44 | --reactive-button-red-color: rgba(244, 81, 108, 1) !important; 45 | --reactive-button-yellow-color: rgba(255, 193, 7, 1) !important; 46 | --reactive-button-teal-color: rgba(0, 181, 173, 1) !important; 47 | --reactive-button-violet-color: rgba(100, 53, 201, 1) !important; 48 | --reactive-button-blue-color: rgba(66, 153, 225, 1) !important; 49 | 50 | --reactive-progress-outline-primary-color: rgba(88, 103, 221, 0.3) !important; 51 | --reactive-progress-outline-secondary-color: rgba(108, 117, 125, 0.3) !important; 52 | --reactive-progress-outline-dark-color: rgba(66, 78, 106, 0.3) !important; 53 | --reactive-progress-outline-light-color: rgba(214, 212, 212, 0.3) !important; 54 | --reactive-progress-outline-green-color: rgba(37, 162, 51, 0.3) !important; 55 | --reactive-progress-outline-red-color: rgba(244, 81, 108, 0.3) !important; 56 | --reactive-progress-outline-yellow-color: rgba(255, 193, 7, 0.3) !important; 57 | --reactive-progress-outline-teal-color: rgba(0, 181, 173, 0.3) !important; 58 | --reactive-progress-outline-violet-color: rgba(100, 53, 201, 0.3) !important; 59 | --reactive-progress-outline-blue-color: rgba(66, 153, 225, 0.3) !important; 60 | } 61 | ``` 62 | 63 | -------------------------------------------------------------------------------- /docs/docs/usage.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: usage 3 | title: Usage 4 | --- 5 | 6 | import Playground from '../src/components/Playground/Playground' 7 | import UsingIcons from '../src/components/DemoCodes/UsingIcons' 8 | import AnchorTag from '../src/components/DemoCodes/AnchorTag' 9 | import Colors from '../src/components/DemoCodes/Colors' 10 | import Size from '../src/components/DemoCodes/Size' 11 | import Style from '../src/components/DemoCodes/Style' 12 | import ReactiveButton from 'reactive-button' 13 | import { faReply } from '@fortawesome/free-solid-svg-icons'; 14 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 15 | 16 | The targets of the below example: 17 | 18 | - Show a button showing the text '_Submit_'. 19 | - After clicking the button, change the button text to '_Loading_' and send an HTTP request. 20 | - Upon successful request, change the button text to '_Done_' as success notification. 21 | 22 | ```jsx 23 | import { useState } from 'react'; 24 | import ReactiveButton from 'reactive-button'; 25 | 26 | function App() { 27 | const [state, setState] = useState('idle'); 28 | 29 | const onClickHandler = () => { 30 | setState('loading'); 31 | 32 | // send an HTTP request 33 | setTimeout(() => { 34 | setState('success'); 35 | }, 2000); 36 | }; 37 | 38 | return ( 39 | 46 | ); 47 | } 48 | 49 | export default App; 50 | ``` 51 | 52 | ## Other Usage 53 | 54 | Reactive Button has all the functionalities of a normal button. 55 | 56 | ### Color 57 | 58 | Reactive Button comes with 10 default color options. 59 | 60 |
61 | 66 |
67 | 68 | ```jsx 69 | return ( 70 | <> 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ); 83 | ``` 84 | 85 | ### Size 86 | 87 | There are 4 sizes available. 88 | 89 |
90 | 95 |
96 | 97 | ```jsx 98 | return ( 99 | <> 100 | 101 | 102 | 103 | 104 | 105 | ); 106 | ``` 107 | 108 | ### Style 109 | 110 | Make the buttons more beautiful with these customization options. 111 | 112 |
113 | 118 |
119 | 120 | ```jsx 121 | return ( 122 | <> 123 | 124 | 125 | 126 | 127 | ); 128 | ``` 129 | 130 | ### Existing State 131 | 132 | In your project, There might be existing state for loading indicator which accepts boolean value only. If you don't want to define new state for Reactive Button, then utilize the existing state. 133 | 134 | ```jsx 135 | const [loading, setLoading] = useState(false); 136 | 137 | return ( 138 | 143 | ); 144 | ``` 145 | 146 | ### Without State 147 | 148 | `state` is not mandatory. 149 | 150 | ```jsx 151 | return ; 152 | ``` 153 | 154 | ### Using Icons 155 | 156 | You can use your own icons. Don't forget to wrap them with a parent element. 157 | 158 |
159 | 164 |
165 | 166 | ```jsx 167 | return ( 168 | 171 | Send 172 | 173 | } 174 | /> 175 | ); 176 | ``` 177 | 178 | ### Form Submit 179 | 180 | If you need to submit form by button clicking, set the type prop as 'submit'. 181 | 182 | ```jsx 183 | return ( 184 |
185 | 186 | 187 | 188 | 189 | ); 190 | ``` 191 | 192 | ### Anchor Tag 193 | 194 | To use Reactive button as anchor tag, simply wrap it with an anchor tag. 195 | 196 |
197 | 202 |
203 | 204 | ```jsx 205 | return ( 206 | 207 | 208 | 209 | ); 210 | ``` 211 | 212 | import Link from '@docusaurus/Link'; 213 | 214 | Note: For more usage, visit Playground. 215 | 216 | ## Available Props 217 | 218 | | Props | Type | Description | Default | 219 | | :-------------: | :---------------------: | :-------------------------------------------------------------------------------------------------------------------------------: | :----------: | 220 | | buttonState | `string` | `'idle'` \| `'loading'` \| `'success'` \| `'error'` | `'idle'` | 221 | | onClick | `function` | Callback function when clicking button | `() => {}` | 222 | | color | `string` | `'primary'` \| `'secondary'` \| `'dark'` \| `'light'` \| `'green'` \| `'red'` \| `'yellow'` \| `'teal'` \| `'violet'` \| `'blue'` | `'primary'` | 223 | | idleText | `string` \| `ReactNode` | Button text when idle | `'Click Me'` | 224 | | loadingText | `string` \| `ReactNode` | Button text when loading | `'Loading'` | 225 | | successText | `string` \| `ReactNode` | Button text when loading successful | `'Success'` | 226 | | errorText | `string` \| `ReactNode` | Button text when loading failed | `'Error'` | 227 | | type | `string` | `'button'` \| `'submit'` \| `'reset'` | `'button'` | 228 | | className | `string` | Button classnames | `''` | 229 | | style | `React.CSSProperties` | Custom style | `{}` | 230 | | outline | `boolean` | Enable outline effect | `false` | 231 | | shadow | `boolean` | Enable shadow effect | `false` | 232 | | rounded | `boolean` | Enable rounded button | `false` | 233 | | size | `string` | `'tiny'` \| `'small'` \| `'normal'` \| `'large'` | `'normal'` | 234 | | block | `boolean` | Block Button | `false` | 235 | | messageDuration | `number` | Success/Error message duration in millisecond | `2000` | 236 | | disabled | `boolean` | Disable button | `false` | 237 | | buttonRef | `React.Ref` \| `null` | Button reference | `null` | 238 | | width | `string` \| `null` | Override button width | `null` | 239 | | height | `string` \| `null` | Override button height | `null` | 240 | | animation | `boolean` | Button hover and click animation | `true` | 241 | -------------------------------------------------------------------------------- /docs/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require('prism-react-renderer/themes/palenight'); 5 | const darkCodeTheme = require('prism-react-renderer/themes/palenight'); 6 | 7 | /** @type {import('@docusaurus/types').DocusaurusConfig} */ 8 | module.exports = { 9 | title: 'Reactive Button', 10 | tagline: '3D animated react button component with progress bar', 11 | url: 'https://arifszn.github.io', 12 | baseUrl: '/reactive-button/', 13 | onBrokenLinks: 'throw', 14 | onBrokenMarkdownLinks: 'warn', 15 | favicon: 'img/favicon.ico', 16 | organizationName: 'arifszn', 17 | projectName: 'reactive-button', 18 | trailingSlash: false, 19 | customFields: { 20 | description: 21 | 'Reactive Button is a 3D animated react component to replace the traditional boring buttons. Change your button style without adding any UI framework. Comes with progress bar and the goal is to let the users visualize what is happening after a button click.', 22 | }, 23 | themeConfig: { 24 | navbar: { 25 | title: 'Reactive Button', 26 | hideOnScroll: false, 27 | logo: { 28 | alt: 'Logo', 29 | src: 'img/logo/logo.png', 30 | }, 31 | items: [ 32 | { 33 | type: 'doc', 34 | docId: 'introduction', 35 | position: 'left', 36 | label: 'Docs', 37 | }, 38 | { 39 | href: 'https://github.com/arifszn/reactive-button', 40 | position: 'right', 41 | 'aria-label': 'GitHub repository', 42 | className: 'header-github-link', 43 | }, 44 | ], 45 | }, 46 | footer: { 47 | style: 'light', 48 | copyright: `Copyright © ${new Date().getFullYear()} Ariful Alam`, 49 | }, 50 | metadata: [ 51 | { 52 | name: 'Reactive Button', 53 | content: '3D animated react button component with progress bar.', 54 | }, 55 | ], 56 | image: 'img/logo/logo.png', 57 | announcementBar: { 58 | id: 'reactive_button_support_us', 59 | content: 60 | '⭐️ If you like this project, give it a star on GitHub ⭐️', 61 | // backgroundColor: "#fafbfc", // Defaults to `#fff`. 62 | // textColor: "#091E42", // Defaults to `#000`. 63 | // isCloseable: true, // Defaults to `true`. 64 | }, 65 | colorMode: { 66 | defaultMode: 'light', 67 | disableSwitch: false, 68 | respectPrefersColorScheme: true, 69 | }, 70 | prism: { 71 | theme: lightCodeTheme, 72 | darkTheme: darkCodeTheme, 73 | }, 74 | }, 75 | presets: [ 76 | [ 77 | '@docusaurus/preset-classic', 78 | { 79 | docs: { 80 | sidebarPath: require.resolve('./sidebars.js'), 81 | }, 82 | theme: { 83 | customCss: require.resolve('./src/css/custom.css'), 84 | }, 85 | blog: false, 86 | sitemap: { 87 | changefreq: 'weekly', 88 | priority: 0.5, 89 | }, 90 | gtag: { 91 | trackingID: '2EH3VSS6EB', 92 | }, 93 | }, 94 | ], 95 | ], 96 | plugins: [ 97 | require.resolve('docusaurus-plugin-sass'), 98 | [ 99 | '@docusaurus/plugin-client-redirects', 100 | { 101 | redirects: [ 102 | { 103 | from: '/docs', 104 | to: '/docs/introduction', 105 | }, 106 | ], 107 | }, 108 | ], 109 | ], 110 | themes: [ 111 | [ 112 | require.resolve('@easyops-cn/docusaurus-search-local'), 113 | { 114 | hashed: true, 115 | highlightSearchTermsOnTargetPage: true, 116 | explicitSearchResultPath: true, 117 | }, 118 | ], 119 | ], 120 | }; 121 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactive-button-doc", 3 | "private": true, 4 | "scripts": { 5 | "docusaurus": "docusaurus", 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "docusaurus deploy", 10 | "clear": "docusaurus clear", 11 | "serve": "docusaurus serve", 12 | "write-translations": "docusaurus write-translations", 13 | "write-heading-ids": "docusaurus write-heading-ids" 14 | }, 15 | "dependencies": { 16 | "@docusaurus/core": "^2.0.1", 17 | "@docusaurus/plugin-client-redirects": "^2.1.0", 18 | "@docusaurus/preset-classic": "^2.0.1", 19 | "@easyops-cn/docusaurus-search-local": "^0.31.0", 20 | "@fortawesome/free-solid-svg-icons": "^6.2.0", 21 | "@fortawesome/react-fontawesome": "^0.2.0", 22 | "@mdx-js/react": "1.6.22", 23 | "@svgr/webpack": "^6.3.1", 24 | "clsx": "^1.1.1", 25 | "docusaurus-plugin-sass": "^0.2.2", 26 | "file-loader": "^6.2.0", 27 | "react": "^17.0.2", 28 | "react-dom": "^17.0.2", 29 | "react-icons": "^4.2.0", 30 | "react-live": "2.4.1", 31 | "reactive-button": "^1.3.13", 32 | "sass": "^1.54.5", 33 | "styled-components": "^5.3.0", 34 | "url-loader": "^4.1.1" 35 | }, 36 | "browserslist": { 37 | "production": [ 38 | ">0.5%", 39 | "not dead", 40 | "not op_mini all" 41 | ], 42 | "development": [ 43 | "last 1 chrome version", 44 | "last 1 firefox version", 45 | "last 1 safari version" 46 | ] 47 | }, 48 | "devDependencies": { 49 | "eslint": "^8.25.0", 50 | "eslint-config-prettier": "^8.5.0", 51 | "eslint-plugin-prettier": "^4.0.0", 52 | "eslint-plugin-react": "^7.29.4", 53 | "prettier": "^2.5.1", 54 | "prop-types": "^15.8.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | docs: ['introduction', 'installation', 'usage', 'playground', 'theming'], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/src/components/DemoCodes/AnchorTag.js: -------------------------------------------------------------------------------- 1 | const AnchorTag = ` 2 | 3 | 4 | 5 | `; 6 | 7 | export default AnchorTag; 8 | -------------------------------------------------------------------------------- /docs/src/components/DemoCodes/Colors.js: -------------------------------------------------------------------------------- 1 | const Colors = ` 2 |
3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 | `; 39 | 40 | export default Colors; 41 | -------------------------------------------------------------------------------- /docs/src/components/DemoCodes/Size.js: -------------------------------------------------------------------------------- 1 | const Size = ` 2 |
3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 | `; 19 | 20 | export default Size; 21 | -------------------------------------------------------------------------------- /docs/src/components/DemoCodes/Style.js: -------------------------------------------------------------------------------- 1 | const Style = ` 2 |
3 |
4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 |
14 |
15 | `; 16 | 17 | export default Style; 18 | -------------------------------------------------------------------------------- /docs/src/components/DemoCodes/Theming.js: -------------------------------------------------------------------------------- 1 | const Theming = ` 2 | 8 | `; 9 | 10 | export default Theming; 11 | -------------------------------------------------------------------------------- /docs/src/components/DemoCodes/UsingIcons.js: -------------------------------------------------------------------------------- 1 | const UsingIcons = ` 2 | 5 | Send 6 | 7 | } 8 | /> 9 | `; 10 | 11 | export default UsingIcons; 12 | -------------------------------------------------------------------------------- /docs/src/components/Example/Example.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Example1 from './Example1'; 3 | import Example2 from './Example2'; 4 | import Example3 from './Example3'; 5 | import Example4 from './Example4'; 6 | import './Example.scss'; 7 | 8 | const Example = () => { 9 | return ( 10 |
11 |
12 | 13 | 14 | 15 | 16 |
17 |
18 | ); 19 | }; 20 | 21 | export default Example; 22 | -------------------------------------------------------------------------------- /docs/src/components/Example/Example.scss: -------------------------------------------------------------------------------- 1 | .example-component-wrapper { 2 | .reactive-btn-wrapper, 3 | .reactive-btn { 4 | --reactive-button-font-size: 13px !important; 5 | } 6 | .flex-section { 7 | padding-bottom: 6px; 8 | padding-top: 6px; 9 | } 10 | .flex-section .item { 11 | padding: 10px; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docs/src/components/Example/Example1.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from 'react'; 2 | import ReactiveButton from 'reactive-button'; 3 | 4 | const Example1 = () => { 5 | const [button1, setButton1] = useState({ 6 | ref: useRef(), 7 | color: 'primary', 8 | idleText: 'Primary', 9 | loadingText: 'Loading', 10 | successText: 'Success', 11 | errorText: 'Error', 12 | buttonState: 'idle', 13 | endState: 'success', 14 | }); 15 | 16 | const [button2, setButton2] = useState({ 17 | ref: useRef(), 18 | color: 'secondary', 19 | idleText: 'Secondary', 20 | loadingText: 'Loading', 21 | successText: 'Success', 22 | errorText: 'Error', 23 | buttonState: 'idle', 24 | endState: 'success', 25 | }); 26 | 27 | const [button3, setButton3] = useState({ 28 | ref: useRef(), 29 | color: 'teal', 30 | idleText: 'Teal', 31 | loadingText: 'Loading', 32 | successText: 'Success', 33 | errorText: 'Error', 34 | buttonState: 'idle', 35 | endState: 'success', 36 | }); 37 | 38 | const [button4, setButton4] = useState({ 39 | ref: useRef(), 40 | color: 'green', 41 | idleText: 'Green', 42 | loadingText: 'Loading', 43 | successText: 'Success', 44 | errorText: 'Error', 45 | buttonState: 'idle', 46 | endState: 'success', 47 | }); 48 | 49 | const [button5, setButton5] = useState({ 50 | ref: useRef(), 51 | color: 'red', 52 | idleText: 'Red', 53 | loadingText: 'Loading', 54 | successText: 'Success', 55 | errorText: 'Error', 56 | buttonState: 'idle', 57 | endState: 'success', 58 | }); 59 | 60 | const buttons = [ 61 | [button1, setButton1], 62 | [button2, setButton2], 63 | [button3, setButton3], 64 | [button4, setButton4], 65 | [button5, setButton5], 66 | ]; 67 | 68 | const onClickHandler = (index) => { 69 | buttons[index][1]({ 70 | ...buttons[index][0], 71 | buttonState: 'loading', 72 | }); 73 | setTimeout(() => { 74 | buttons[index][1]({ 75 | ...buttons[index][0], 76 | buttonState: buttons[index][0].endState, 77 | }); 78 | }, 2000); 79 | }; 80 | 81 | return ( 82 | 83 |
84 | {buttons.map((button, index) => ( 85 |
86 | { 95 | onClickHandler(index); 96 | }} 97 | /> 98 |
99 | ))} 100 |
101 |
102 | ); 103 | }; 104 | 105 | export default Example1; 106 | -------------------------------------------------------------------------------- /docs/src/components/Example/Example2.js: -------------------------------------------------------------------------------- 1 | import { 2 | faCalendarCheck, 3 | faCheck, 4 | faCheckCircle, 5 | faSpinner, 6 | faThumbsUp, 7 | faTimes, 8 | faUserCheck, 9 | } from '@fortawesome/free-solid-svg-icons'; 10 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 11 | import React, { useEffect, useRef, useState } from 'react'; 12 | import ReactiveButton from 'reactive-button'; 13 | 14 | const Example2 = () => { 15 | const [button1, setButton1] = useState({ 16 | ref: useRef(), 17 | color: 'violet', 18 | idleText: 'Violet', 19 | loadingText: ( 20 | 21 | Loading 22 | 23 | ), 24 | successText: ( 25 | 26 | Success 27 | 28 | ), 29 | errorText: ( 30 | 31 | Error 32 | 33 | ), 34 | buttonState: 'idle', 35 | endState: 'success', 36 | }); 37 | 38 | const [button2, setButton2] = useState({ 39 | ref: useRef(), 40 | color: 'blue', 41 | idleText: 'Blue', 42 | loadingText: ( 43 | 44 | Loading 45 | 46 | ), 47 | successText: ( 48 | 49 | Success 50 | 51 | ), 52 | errorText: ( 53 | 54 | Error 55 | 56 | ), 57 | buttonState: 'idle', 58 | endState: 'success', 59 | }); 60 | 61 | const [button3, setButton3] = useState({ 62 | ref: useRef(), 63 | color: 'yellow', 64 | idleText: 'Yellow', 65 | loadingText: ( 66 | 67 | Loading 68 | 69 | ), 70 | successText: ( 71 | 72 | Success 73 | 74 | ), 75 | errorText: ( 76 | 77 | Error 78 | 79 | ), 80 | buttonState: 'idle', 81 | endState: 'success', 82 | }); 83 | 84 | const [button4, setButton4] = useState({ 85 | ref: useRef(), 86 | color: 'dark', 87 | idleText: 'Dark', 88 | loadingText: ( 89 | 90 | Loading 91 | 92 | ), 93 | successText: ( 94 | 95 | Success 96 | 97 | ), 98 | errorText: ( 99 | 100 | Error 101 | 102 | ), 103 | buttonState: 'idle', 104 | endState: 'success', 105 | }); 106 | 107 | const [button5, setButton5] = useState({ 108 | ref: useRef(), 109 | color: 'light', 110 | idleText: 'Light', 111 | loadingText: ( 112 | 113 | Loading 114 | 115 | ), 116 | successText: ( 117 | 118 | Success 119 | 120 | ), 121 | errorText: ( 122 | 123 | Error 124 | 125 | ), 126 | buttonState: 'idle', 127 | endState: 'success', 128 | }); 129 | 130 | const buttons = [ 131 | [button1, setButton1], 132 | [button2, setButton2], 133 | [button3, setButton3], 134 | [button4, setButton4], 135 | [button5, setButton5], 136 | ]; 137 | 138 | const onClickHandler = (index) => { 139 | buttons[index][1]({ 140 | ...buttons[index][0], 141 | buttonState: 'loading', 142 | }); 143 | setTimeout(() => { 144 | buttons[index][1]({ 145 | ...buttons[index][0], 146 | buttonState: buttons[index][0].endState, 147 | }); 148 | }, 2000); 149 | }; 150 | 151 | useEffect(() => { 152 | const timer = buttons.map((button) => { 153 | button[0].ref.current.click(); 154 | }); 155 | return () => { 156 | //un-mounting 157 | clearTimeout(timer); 158 | }; 159 | }, []); 160 | 161 | return ( 162 | 163 |
164 | {buttons.map((button, index) => ( 165 |
166 | onClickHandler(index)} 175 | /> 176 |
177 | ))} 178 |
179 |
180 | ); 181 | }; 182 | 183 | export default Example2; 184 | -------------------------------------------------------------------------------- /docs/src/components/Example/Example3.js: -------------------------------------------------------------------------------- 1 | import { faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 3 | import React, { useEffect, useRef, useState } from 'react'; 4 | import ReactiveButton from 'reactive-button'; 5 | import { BiLoader } from 'react-icons/bi'; 6 | import { ImSpinner8 } from 'react-icons/im'; 7 | import { SiSpinrilla } from 'react-icons/si'; 8 | import { ImSpinner9 } from 'react-icons/im'; 9 | 10 | const Example3 = () => { 11 | const [button1, setButton1] = useState({ 12 | ref: useRef(), 13 | color: 'violet', 14 | idleText: 'Violet', 15 | loadingText: 'Loading', 16 | successText: ( 17 | 18 | Success 19 | 20 | ), 21 | errorText: ( 22 | 23 | {' '} 24 | 25 | 26 | {' '} 27 | Error 28 | 29 | ), 30 | buttonState: 'loading', 31 | endState: 'success', 32 | }); 33 | 34 | const [button2, setButton2] = useState({ 35 | ref: useRef(), 36 | color: 'blue', 37 | idleText: 'Blue', 38 | loadingText: ( 39 | 40 | Loading 41 | 42 | ), 43 | successText: ( 44 | 45 | {' '} 46 | 47 | 48 | {' '} 49 | Success 50 | 51 | ), 52 | errorText: ( 53 | 54 | {' '} 55 | 56 | 57 | {' '} 58 | Error 59 | 60 | ), 61 | buttonState: 'loading', 62 | endState: 'success', 63 | }); 64 | 65 | const [button3, setButton3] = useState({ 66 | ref: useRef(), 67 | color: 'yellow', 68 | idleText: 'Yellow', 69 | loadingText: ( 70 | 71 | Loading 72 | 73 | ), 74 | successText: ( 75 | 76 | {' '} 77 | 78 | 79 | {' '} 80 | Success 81 | 82 | ), 83 | errorText: ( 84 | 85 | {' '} 86 | 87 | 88 | {' '} 89 | Error 90 | 91 | ), 92 | buttonState: 'loading', 93 | endState: 'success', 94 | }); 95 | 96 | const [button4, setButton4] = useState({ 97 | ref: useRef(), 98 | color: 'dark', 99 | idleText: 'Dark', 100 | loadingText: ( 101 | 102 | Loading 103 | 104 | ), 105 | successText: ( 106 | 107 | {' '} 108 | 109 | 110 | {' '} 111 | Success 112 | 113 | ), 114 | errorText: ( 115 | 116 | {' '} 117 | 118 | 119 | {' '} 120 | Error 121 | 122 | ), 123 | buttonState: 'loading', 124 | endState: 'success', 125 | }); 126 | 127 | const [button5, setButton5] = useState({ 128 | ref: useRef(), 129 | color: 'light', 130 | idleText: 'Light', 131 | loadingText: ( 132 | 133 | Loading 134 | 135 | ), 136 | successText: ( 137 | 138 | {' '} 139 | 140 | 141 | {' '} 142 | Success 143 | 144 | ), 145 | errorText: ( 146 | 147 | {' '} 148 | 149 | 150 | {' '} 151 | Error 152 | 153 | ), 154 | buttonState: 'loading', 155 | endState: 'success', 156 | }); 157 | 158 | const buttons = [ 159 | [button1, setButton1], 160 | [button2, setButton2], 161 | [button3, setButton3], 162 | [button4, setButton4], 163 | [button5, setButton5], 164 | ]; 165 | 166 | const onClickHandler = (index) => { 167 | buttons[index][1]({ 168 | ...buttons[index][0], 169 | buttonState: 'loading', 170 | }); 171 | setTimeout(() => { 172 | buttons[index][1]({ 173 | ...buttons[index][0], 174 | buttonState: buttons[index][0].endState, 175 | }); 176 | }, 2000); 177 | }; 178 | 179 | useEffect(() => { 180 | setTimeout(() => { 181 | buttons.map((button) => { 182 | if (button[0].autoStart) { 183 | button[0].ref.current.click(); 184 | } 185 | }); 186 | }, 1000); 187 | }, []); 188 | 189 | return ( 190 | 191 |
192 | {buttons.map((button, index) => ( 193 |
194 | onClickHandler(index)} 206 | /> 207 |
208 | ))} 209 |
210 |
211 | ); 212 | }; 213 | 214 | export default Example3; 215 | -------------------------------------------------------------------------------- /docs/src/components/Example/Example4.js: -------------------------------------------------------------------------------- 1 | import { 2 | faCheck, 3 | faExclamationCircle, 4 | faRadiation, 5 | faSpinner, 6 | faThumbsDown, 7 | faTimes, 8 | faUserTimes, 9 | } from '@fortawesome/free-solid-svg-icons'; 10 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 11 | import React, { useEffect, useRef, useState } from 'react'; 12 | import ReactiveButton from 'reactive-button'; 13 | 14 | const Example4 = () => { 15 | const [button1, setButton1] = useState({ 16 | ref: useRef(), 17 | color: 'primary', 18 | idleText: 'Primary', 19 | loadingText: ( 20 | 21 | Loading 22 | 23 | ), 24 | successText: ( 25 | 26 | Success 27 | 28 | ), 29 | errorText: ( 30 | 31 | Error 32 | 33 | ), 34 | buttonState: 'idle', 35 | endState: 'error', 36 | }); 37 | 38 | const [button2, setButton2] = useState({ 39 | ref: useRef(), 40 | color: 'secondary', 41 | idleText: 'Secondary', 42 | loadingText: ( 43 | 44 | Loading 45 | 46 | ), 47 | successText: ( 48 | 49 | Success 50 | 51 | ), 52 | errorText: ( 53 | 54 | Error 55 | 56 | ), 57 | buttonState: 'idle', 58 | endState: 'error', 59 | }); 60 | 61 | const [button3, setButton3] = useState({ 62 | ref: useRef(), 63 | color: 'teal', 64 | idleText: 'Teal', 65 | loadingText: ( 66 | 67 | Loading 68 | 69 | ), 70 | successText: ( 71 | 72 | Success 73 | 74 | ), 75 | errorText: ( 76 | 77 | Error 78 | 79 | ), 80 | buttonState: 'idle', 81 | endState: 'error', 82 | }); 83 | 84 | const [button4, setButton4] = useState({ 85 | ref: useRef(), 86 | color: 'green', 87 | idleText: 'Green', 88 | loadingText: ( 89 | 90 | {' '} 91 | 92 | 93 | {' '} 94 | Loading 95 | 96 | ), 97 | successText: ( 98 | 99 | {' '} 100 | 101 | 102 | {' '} 103 | Success 104 | 105 | ), 106 | errorText: ( 107 | 108 | {' '} 109 | 110 | 111 | {' '} 112 | Error 113 | 114 | ), 115 | buttonState: 'idle', 116 | endState: 'error', 117 | }); 118 | 119 | const [button5, setButton5] = useState({ 120 | ref: useRef(), 121 | color: 'red', 122 | idleText: 'Red', 123 | loadingText: ( 124 | 125 | {' '} 126 | 127 | 128 | {' '} 129 | Loading 130 | 131 | ), 132 | successText: ( 133 | 134 | {' '} 135 | 136 | 137 | {' '} 138 | Success 139 | 140 | ), 141 | errorText: ( 142 | 143 | {' '} 144 | 145 | 146 | {' '} 147 | Error 148 | 149 | ), 150 | buttonState: 'idle', 151 | endState: 'error', 152 | }); 153 | 154 | const buttons = [ 155 | [button1, setButton1], 156 | [button2, setButton2], 157 | [button3, setButton3], 158 | [button4, setButton4], 159 | [button5, setButton5], 160 | ]; 161 | 162 | const onClickHandler = (index) => { 163 | buttons[index][1]({ 164 | ...buttons[index][0], 165 | buttonState: 'loading', 166 | }); 167 | setTimeout(() => { 168 | buttons[index][1]({ 169 | ...buttons[index][0], 170 | buttonState: buttons[index][0].endState, 171 | }); 172 | }, 2000); 173 | }; 174 | 175 | useEffect(() => { 176 | const timer = buttons.map((button) => { 177 | button[0].ref.current.click(); 178 | }); 179 | 180 | return () => { 181 | //un-mounting 182 | clearTimeout(timer); 183 | }; 184 | }, []); 185 | 186 | return ( 187 | 188 |
189 | {buttons.map((button, index) => ( 190 |
191 | onClickHandler(index)} 203 | /> 204 |
205 | ))} 206 |
207 |
208 | ); 209 | }; 210 | 211 | export default Example4; 212 | -------------------------------------------------------------------------------- /docs/src/components/Playground/EditorPlayground.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useState } from 'react'; 2 | import ReactiveButton from 'reactive-button'; 3 | import './EditorPlayground.scss'; 4 | import palenight from 'prism-react-renderer/themes/palenight'; 5 | import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live'; 6 | import { 7 | faCheck, 8 | faSpinner, 9 | faTimes, 10 | faCircleNotch, 11 | faThumbsUp, 12 | faBomb, 13 | } from '@fortawesome/free-solid-svg-icons'; 14 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 15 | 16 | const EditorPlayground = () => { 17 | const code = ` 18 | function App() { 19 | const [state, setState] = useState('idle'); 20 | 21 | const onClickHandler = () => { 22 | setState('loading'); 23 | setTimeout(() => { 24 | setState('success'); 25 | }, 2000); 26 | }; 27 | 28 | return ( 29 | 36 | Loading 37 | 38 | } 39 | successText={ 40 | <> 41 | Success 42 | 43 | } 44 | errorText={ 45 | <> 46 | Error 47 | 48 | } 49 | type={'button'} 50 | className={'class1 class2'} 51 | style={{ 52 | borderRadius: '5px', 53 | }} 54 | outline={false} 55 | shadow={false} 56 | rounded={false} 57 | size={'normal'} 58 | block={false} 59 | messageDuration={2000} 60 | disabled={false} 61 | buttonRef={null} 62 | width={null} 63 | height={null} 64 | animation={true} 65 | /> 66 | ); 67 | } 68 | `; 69 | 70 | return ( 71 |
72 | 88 |
89 |
90 |
91 | 92 | 93 |
94 |
95 |
96 |
97 |
98 | 99 |
100 |
101 |
102 |
103 | ); 104 | }; 105 | 106 | export default EditorPlayground; 107 | -------------------------------------------------------------------------------- /docs/src/components/Playground/EditorPlayground.scss: -------------------------------------------------------------------------------- 1 | .editor-playground-component-wrapper { 2 | .playground__card { 3 | min-height: 120px; 4 | } 5 | .editor__card__body { 6 | padding: 0; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/src/components/Playground/InteractivePlayground.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import ReactiveButton from 'reactive-button'; 3 | import './InteractivePlayground.scss'; 4 | import '../../css/bootstrap.scss'; 5 | 6 | const InteractivePlayground = () => { 7 | const defaultValues = { 8 | buttonState: 'idle', 9 | color: 'primary', 10 | idleText: 'Click Me', 11 | loadingText: 'Loading', 12 | successText: 'Success', 13 | errorText: 'Error', 14 | className: '', 15 | outline: false, 16 | shadow: false, 17 | rounded: false, 18 | size: 'normal', 19 | messageDuration: 2000, 20 | block: false, 21 | disabled: false, 22 | width: '', 23 | height: '', 24 | animation: true, 25 | }; 26 | const [buttonState, setButtonState] = useState(defaultValues.buttonState); 27 | const [color, setColor] = useState(defaultValues.color); 28 | const [idleText, setIdleText] = useState(defaultValues.idleText); 29 | const [loadingText, setLoadingText] = useState(defaultValues.loadingText); 30 | const [successText, setSuccessText] = useState(defaultValues.successText); 31 | const [errorText, setErrorText] = useState(defaultValues.errorText); 32 | const [className, setClassName] = useState(defaultValues.className); 33 | const [outline, setOutline] = useState(defaultValues.outline); 34 | const [shadow, setShadow] = useState(defaultValues.shadow); 35 | const [rounded, setRounded] = useState(defaultValues.rounded); 36 | const [size, setSize] = useState(defaultValues.size); 37 | const [messageDuration, setMessageDuration] = useState( 38 | defaultValues.messageDuration 39 | ); 40 | const [block, setBlock] = useState(defaultValues.block); 41 | const [disabled, setDisabled] = useState(defaultValues.disabled); 42 | const [width, setWidth] = useState(defaultValues.width); 43 | const [height, setHeight] = useState(defaultValues.height); 44 | const [animation, setAnimation] = useState(defaultValues.animation); 45 | 46 | const [resetButtonState, setResetButtonState] = useState('idle'); 47 | const [disableButtonStateProp, setDisableButtonStateProp] = useState(false); 48 | 49 | const buttonOnClickHandler = () => { 50 | setDisableButtonStateProp(true); 51 | setButtonState('loading'); 52 | setTimeout(() => { 53 | setButtonState('success'); 54 | setDisableButtonStateProp(false); 55 | }, 2000); 56 | }; 57 | 58 | useEffect(() => { 59 | if (buttonState === 'success' || buttonState === 'error') { 60 | setDisableButtonStateProp(true); 61 | setTimeout(() => { 62 | setDisableButtonStateProp(false); 63 | setButtonState('idle'); 64 | }, messageDuration); 65 | } 66 | }, [buttonState]); 67 | 68 | const buttonStateOnChangeHandler = (e) => { 69 | setButtonState(e.target.value); 70 | }; 71 | 72 | const colorOnChangeHandler = (e) => { 73 | setColor(e.target.value); 74 | }; 75 | 76 | const idleTextOnChangeHandler = (e) => { 77 | setIdleText(e.target.value); 78 | }; 79 | 80 | const loadingTextOnChangeHandler = (e) => { 81 | setLoadingText(e.target.value); 82 | }; 83 | 84 | const successTextOnChangeHandler = (e) => { 85 | setSuccessText(e.target.value); 86 | }; 87 | 88 | const errorTextOnChangeHandler = (e) => { 89 | setErrorText(e.target.value); 90 | }; 91 | 92 | const classNameOnChangeHandler = (e) => { 93 | setClassName(e.target.value); 94 | }; 95 | 96 | const outlineOnChangeHandler = (e) => { 97 | setOutline(e.target.checked === true ? true : false); 98 | }; 99 | 100 | const shadowOnChangeHandler = (e) => { 101 | setShadow(e.target.checked === true ? true : false); 102 | }; 103 | 104 | const roundedOnChangeHandler = (e) => { 105 | setRounded(e.target.checked === true ? true : false); 106 | }; 107 | 108 | const sizeOnChangeHandler = (e) => { 109 | setSize(e.target.value); 110 | }; 111 | 112 | const blockOnChangeHandler = (e) => { 113 | setBlock(e.target.checked === true ? true : false); 114 | }; 115 | 116 | const messageDurationOnChangeHandler = (e) => { 117 | if (parseInt(e.target.value) >= 0) { 118 | setMessageDuration(e.target.value); 119 | } 120 | }; 121 | 122 | const disabledChangeHandler = (e) => { 123 | setDisabled(e.target.checked === true ? true : false); 124 | }; 125 | 126 | const widthOnChangeHandler = (e) => { 127 | setWidth(e.target.value); 128 | }; 129 | 130 | const heightOnChangeHandler = (e) => { 131 | setHeight(e.target.value); 132 | }; 133 | 134 | const animationOnChangeHandler = (e) => { 135 | setAnimation(e.target.checked === true ? true : false); 136 | }; 137 | 138 | const resetAll = () => { 139 | setResetButtonState('loading'); 140 | 141 | setTimeout(() => { 142 | setButtonState(defaultValues.buttonState); 143 | setColor(defaultValues.buttonState); 144 | setIdleText(defaultValues.idleText); 145 | setLoadingText(defaultValues.loadingText); 146 | setSuccessText(defaultValues.successText); 147 | setErrorText(defaultValues.errorText); 148 | setClassName(defaultValues.className); 149 | setSize(defaultValues.size); 150 | setShadow(defaultValues.shadow); 151 | setRounded(defaultValues.rounded); 152 | setOutline(defaultValues.outline); 153 | setBlock(defaultValues.block); 154 | setMessageDuration(defaultValues.messageDuration); 155 | setDisabled(defaultValues.disabled); 156 | setWidth(defaultValues.width); 157 | setHeight(defaultValues.height); 158 | setAnimation(defaultValues.animation); 159 | 160 | setResetButtonState('success'); 161 | }, 1000); 162 | }; 163 | 164 | return ( 165 |
166 |
167 |
168 |
169 | 196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 | 212 |
213 | 225 |
226 |
227 |
228 | 234 |
235 | 243 |
244 |
245 |
246 | 252 |
253 | 261 |
262 |
263 |
264 | 270 |
271 | 278 |
279 |
280 |
281 | 287 |
288 | 295 |
296 |
297 |
298 | 304 |
305 | 312 |
313 |
314 |
315 | 321 |
322 | 330 |
331 |
332 |
333 | 339 |
340 | 351 |
352 |
353 |
354 | 360 |
361 | 369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 | 382 |
383 | 400 |
401 |
402 |
403 | 409 |
410 | 418 |
419 |
420 |
421 | 427 |
428 | 436 |
437 |
438 |
439 | 445 |
446 | 453 |
454 |
455 |
456 | 462 |
463 | 470 |
471 |
472 |
473 | 479 |
480 | 487 |
488 |
489 |
490 | 496 |
497 |
498 | 506 |
507 | px 508 |
509 |
510 |
511 |
512 |
513 | 519 |
520 |
521 | 529 |
530 | px 531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 | 551 |
552 |
553 |
554 | ); 555 | }; 556 | 557 | export default InteractivePlayground; 558 | -------------------------------------------------------------------------------- /docs/src/components/Playground/InteractivePlayground.scss: -------------------------------------------------------------------------------- 1 | .interactive-playground-component-wrapper { 2 | .playground__card { 3 | min-height: 120px; 4 | } 5 | 6 | .props__container .flex-section { 7 | justify-content: left; 8 | } 9 | 10 | .form-control, 11 | .input-group-text { 12 | font-size: inherit !important; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/src/components/Playground/Playground.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled, { css } from 'styled-components'; 3 | import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live'; 4 | import palenight from 'prism-react-renderer/themes/palenight'; 5 | import PropTypes from 'prop-types'; 6 | 7 | const foreground = '#f8f8f2'; 8 | const red = '#ff5555'; 9 | 10 | const StyledProvider = styled(LiveProvider)` 11 | border-radius: 3px; 12 | box-shadow: 1px 1px 20px rgba(20, 20, 20, 0.27); 13 | overflow: hidden; 14 | margin-bottom: 100px; 15 | `; 16 | 17 | const LiveWrapper = styled.div` 18 | display: flex; 19 | flex-direction: row; 20 | justify-content: stretch; 21 | align-items: stretch; 22 | @media (max-width: 600px) { 23 | flex-direction: column; 24 | } 25 | `; 26 | 27 | const Column = css` 28 | flex-basis: ${(props) => (props.previewOnly ? '100%' : '50%')}; 29 | width: ${(props) => (props.previewOnly ? '100%' : '50%')}; 30 | max-width: ${(props) => (props.previewOnly ? '100%' : '50%')}; 31 | @media (max-width: 600px) { 32 | flex-basis: auto; 33 | width: 100%; 34 | max-width: 100%; 35 | height: ${(props) => props.height}; 36 | max-height: ${(props) => props.height}; 37 | } 38 | `; 39 | 40 | const StyledEditor = styled.div` 41 | font-family: 'Source Code Pro', monospace; 42 | font-size: 14px; 43 | height: ${(props) => props.height}; 44 | max-height: ${(props) => props.height}; 45 | overflow: auto; 46 | ${Column}; 47 | * > textarea:focus { 48 | outline: none; 49 | } 50 | `; 51 | 52 | const StyledPreview = styled(LivePreview)` 53 | position: relative; 54 | padding: 1.5rem; 55 | color: black; 56 | height: auto; 57 | overflow: hidden; 58 | display: flex; 59 | justify-content: center; 60 | flex-direction: row; 61 | align-items: center; 62 | flex-wrap: wrap; 63 | ${Column}; 64 | `; 65 | 66 | const StyledError = styled(LiveError)` 67 | display: block; 68 | padding: 0.5rem; 69 | background: ${red}; 70 | color: ${foreground}; 71 | white-space: pre-wrap; 72 | text-align: left; 73 | font-size: 0.9em; 74 | font-family: 'Source Code Pro', monospace; 75 | `; 76 | 77 | const Playground = ({ noInline, code, scope, height, previewOnly = false }) => { 78 | return ( 79 | 85 | 86 | {!previewOnly && ( 87 | 88 | 89 | 90 | )} 91 | 96 | 97 | 98 | 99 | ); 100 | }; 101 | 102 | Playground.propTypes = { 103 | noInline: PropTypes.bool, 104 | code: PropTypes.string, 105 | scope: PropTypes.object, 106 | height: PropTypes.string, 107 | previewOnly: PropTypes.bool, 108 | }; 109 | 110 | export default Playground; 111 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: #5867dd; 11 | --ifm-color-primary-dark: #3e50d8; 12 | --ifm-color-primary-darker: #3144d5; 13 | --ifm-color-primary-darkest: #2535b4; 14 | --ifm-color-primary-light: #727ee2; 15 | --ifm-color-primary-lighter: #7f8ae5; 16 | --ifm-color-primary-lightest: #a5aded; 17 | --ifm-code-font-size: 95%; 18 | --ifm-navbar-item-padding-horizontal: 16px; 19 | } 20 | 21 | ::-webkit-scrollbar-track { 22 | /* border-radius: 0px; */ 23 | box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 24 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 25 | -moz-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 26 | } 27 | 28 | * { 29 | scrollbar-width: thin; 30 | /* scrollbar-color: darkgrey #F5F5F5; */ 31 | } 32 | 33 | @media screen and (min-width: 966px) { 34 | ::-webkit-scrollbar, 35 | .scroller { 36 | width: 8px; 37 | height: 8px; 38 | background-color: #f1f1f1; 39 | } 40 | } 41 | 42 | ::-webkit-scrollbar-thumb { 43 | /* border-radius: 0px; 44 | box-shadow: inset 0 0 6px rgba(0,0,0,0.5); 45 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5); 46 | -moz-box-shadow: inset 0 0 6px rgba(0,0,0,0.5); */ 47 | background-color: #888; 48 | border-radius: 10px; 49 | } 50 | 51 | .navbar__title { 52 | color: var(--ifm-color-primary); 53 | } 54 | 55 | .header-github-link:before { 56 | background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") 57 | no-repeat; 58 | content: ''; 59 | display: flex; 60 | height: 24px; 61 | width: 24px; 62 | } 63 | 64 | html[data-theme='dark'] .header-github-link:before { 65 | background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") 66 | no-repeat; 67 | } 68 | 69 | .z-styled-preview { 70 | background-color: #e3eaea; 71 | } 72 | 73 | html[data-theme='dark'] .z-styled-preview { 74 | background-color: #232525; 75 | } 76 | 77 | .header-github-link:hover { 78 | opacity: 0.6; 79 | } 80 | 81 | .docusaurus-highlight-code-line { 82 | background-color: rgb(72, 77, 91); 83 | display: block; 84 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 85 | padding: 0 var(--ifm-pre-padding); 86 | } 87 | 88 | .flex-section { 89 | align-items: center; 90 | display: flex; 91 | justify-content: center; 92 | flex-direction: row; 93 | flex-wrap: wrap; 94 | } 95 | 96 | /* ----------- z-table starts ----------- */ 97 | .z-table-wrapper { 98 | border: 1px solid #e0e0e0; 99 | } 100 | 101 | .z-table-wrapper table { 102 | width: 100%; 103 | color: #8a8f94; 104 | margin-bottom: 0; 105 | border-collapse: unset; 106 | } 107 | 108 | .z-table-wrapper table tr { 109 | background: var(--ifm-table-background); 110 | line-height: 30px; 111 | } 112 | 113 | .z-table-wrapper table th { 114 | background-color: #e2e4e4; 115 | color: #adacac; 116 | text-align: center; 117 | text-transform: uppercase; 118 | letter-spacing: 0.05em; 119 | } 120 | 121 | html[data-theme='dark'] .z-table-wrapper table th { 122 | background-color: #1d1f1f; 123 | } 124 | 125 | .z-table-wrapper table th:nth-child(3) { 126 | width: 20px; 127 | } 128 | 129 | .z-table-wrapper table td:first-child { 130 | font-weight: 600; 131 | color: #777; 132 | } 133 | 134 | .z-table-wrapper table td:nth-child(2) { 135 | color: cadetblue; 136 | } 137 | 138 | .z-table-wrapper table tr:nth-child(2n) { 139 | background-color: var(--ifm-table-stripe-background); 140 | } 141 | 142 | .z-table-wrapper table td, 143 | .z-table-wrapper table th { 144 | border: none; 145 | font-size: 13px; 146 | white-space: nowrap !important; 147 | } 148 | 149 | /* ----------- z-table ends ----------- */ 150 | 151 | /* bootstrap custom starts */ 152 | 153 | .border { 154 | border: 1px solid #dee2e6 !important; 155 | } 156 | 157 | .w-25 { 158 | width: 25% !important; 159 | } 160 | 161 | .w-50 { 162 | width: 50% !important; 163 | } 164 | 165 | .w-75 { 166 | width: 75% !important; 167 | } 168 | 169 | .w-100 { 170 | width: 100% !important; 171 | } 172 | 173 | .w-auto { 174 | width: auto !important; 175 | } 176 | 177 | .h-25 { 178 | height: 25% !important; 179 | } 180 | 181 | .h-50 { 182 | height: 50% !important; 183 | } 184 | 185 | .h-75 { 186 | height: 75% !important; 187 | } 188 | 189 | .h-100 { 190 | height: 100% !important; 191 | } 192 | 193 | .h-auto { 194 | height: auto !important; 195 | } 196 | 197 | .float-left { 198 | float: left !important; 199 | } 200 | 201 | .float-right { 202 | float: right !important; 203 | } 204 | 205 | .float-none { 206 | float: none !important; 207 | } 208 | 209 | .justify-content-center { 210 | -ms-flex-pack: center !important; 211 | justify-content: center !important; 212 | } 213 | 214 | .m-auto { 215 | margin: auto !important; 216 | } 217 | 218 | .mt-auto, 219 | .my-auto { 220 | margin-top: auto !important; 221 | } 222 | 223 | .mr-auto, 224 | .mx-auto { 225 | margin-right: auto !important; 226 | } 227 | 228 | .mb-auto, 229 | .my-auto { 230 | margin-bottom: auto !important; 231 | } 232 | 233 | .ml-auto, 234 | .mx-auto { 235 | margin-left: auto !important; 236 | } 237 | 238 | .text-left { 239 | text-align: left !important; 240 | } 241 | 242 | .text-right { 243 | text-align: right !important; 244 | } 245 | 246 | .text-center { 247 | text-align: center !important; 248 | } 249 | 250 | .mt-0, 251 | .my-0 { 252 | margin-top: 0 !important; 253 | } 254 | 255 | .mr-0, 256 | .mx-0 { 257 | margin-right: 0 !important; 258 | } 259 | 260 | .mb-0, 261 | .my-0 { 262 | margin-bottom: 0 !important; 263 | } 264 | 265 | .ml-0, 266 | .mx-0 { 267 | margin-left: 0 !important; 268 | } 269 | 270 | .m-1 { 271 | margin: 0.25rem !important; 272 | } 273 | 274 | .mt-1, 275 | .my-1 { 276 | margin-top: 0.25rem !important; 277 | } 278 | 279 | .mr-1, 280 | .mx-1 { 281 | margin-right: 0.25rem !important; 282 | } 283 | 284 | .mb-1, 285 | .my-1 { 286 | margin-bottom: 0.25rem !important; 287 | } 288 | 289 | .ml-1, 290 | .mx-1 { 291 | margin-left: 0.25rem !important; 292 | } 293 | 294 | .m-2 { 295 | margin: 0.5rem !important; 296 | } 297 | 298 | .mt-2, 299 | .my-2 { 300 | margin-top: 0.5rem !important; 301 | } 302 | 303 | .mr-2, 304 | .mx-2 { 305 | margin-right: 0.5rem !important; 306 | } 307 | 308 | .mb-2, 309 | .my-2 { 310 | margin-bottom: 0.5rem !important; 311 | } 312 | 313 | .ml-2, 314 | .mx-2 { 315 | margin-left: 0.5rem !important; 316 | } 317 | 318 | .m-3 { 319 | margin: 1rem !important; 320 | } 321 | 322 | .mt-3, 323 | .my-3 { 324 | margin-top: 1rem !important; 325 | } 326 | 327 | .mr-3, 328 | .mx-3 { 329 | margin-right: 1rem !important; 330 | } 331 | 332 | .mb-3, 333 | .my-3 { 334 | margin-bottom: 1rem !important; 335 | } 336 | 337 | .ml-3, 338 | .mx-3 { 339 | margin-left: 1rem !important; 340 | } 341 | 342 | .m-4 { 343 | margin: 1.5rem !important; 344 | } 345 | 346 | .mt-4, 347 | .my-4 { 348 | margin-top: 1.5rem !important; 349 | } 350 | 351 | .mr-4, 352 | .mx-4 { 353 | margin-right: 1.5rem !important; 354 | } 355 | 356 | .mb-4, 357 | .my-4 { 358 | margin-bottom: 1.5rem !important; 359 | } 360 | 361 | .ml-4, 362 | .mx-4 { 363 | margin-left: 1.5rem !important; 364 | } 365 | 366 | .m-5 { 367 | margin: 3rem !important; 368 | } 369 | 370 | .mt-5, 371 | .my-5 { 372 | margin-top: 3rem !important; 373 | } 374 | 375 | .mr-5, 376 | .mx-5 { 377 | margin-right: 3rem !important; 378 | } 379 | 380 | .mb-5, 381 | .my-5 { 382 | margin-bottom: 3rem !important; 383 | } 384 | 385 | .ml-5, 386 | .mx-5 { 387 | margin-left: 3rem !important; 388 | } 389 | 390 | .p-0 { 391 | padding: 0 !important; 392 | } 393 | 394 | .pt-0, 395 | .py-0 { 396 | padding-top: 0 !important; 397 | } 398 | 399 | .pr-0, 400 | .px-0 { 401 | padding-right: 0 !important; 402 | } 403 | 404 | .pb-0, 405 | .py-0 { 406 | padding-bottom: 0 !important; 407 | } 408 | 409 | .pl-0, 410 | .px-0 { 411 | padding-left: 0 !important; 412 | } 413 | 414 | .p-1 { 415 | padding: 0.25rem !important; 416 | } 417 | 418 | .pt-1, 419 | .py-1 { 420 | padding-top: 0.25rem !important; 421 | } 422 | 423 | .pr-1, 424 | .px-1 { 425 | padding-right: 0.25rem !important; 426 | } 427 | 428 | .pb-1, 429 | .py-1 { 430 | padding-bottom: 0.25rem !important; 431 | } 432 | 433 | .pl-1, 434 | .px-1 { 435 | padding-left: 0.25rem !important; 436 | } 437 | 438 | .p-2 { 439 | padding: 0.5rem !important; 440 | } 441 | 442 | .pt-2, 443 | .py-2 { 444 | padding-top: 0.5rem !important; 445 | } 446 | 447 | .pr-2, 448 | .px-2 { 449 | padding-right: 0.5rem !important; 450 | } 451 | 452 | .pb-2, 453 | .py-2 { 454 | padding-bottom: 0.5rem !important; 455 | } 456 | 457 | .pl-2, 458 | .px-2 { 459 | padding-left: 0.5rem !important; 460 | } 461 | 462 | .p-3 { 463 | padding: 1rem !important; 464 | } 465 | 466 | .pt-3, 467 | .py-3 { 468 | padding-top: 1rem !important; 469 | } 470 | 471 | .pr-3, 472 | .px-3 { 473 | padding-right: 1rem !important; 474 | } 475 | 476 | .pb-3, 477 | .py-3 { 478 | padding-bottom: 1rem !important; 479 | } 480 | 481 | .pl-3, 482 | .px-3 { 483 | padding-left: 1rem !important; 484 | } 485 | 486 | .p-4 { 487 | padding: 1.5rem !important; 488 | } 489 | 490 | .pt-4, 491 | .py-4 { 492 | padding-top: 1.5rem !important; 493 | } 494 | 495 | .pr-4, 496 | .px-4 { 497 | padding-right: 1.5rem !important; 498 | } 499 | 500 | .pb-4, 501 | .py-4 { 502 | padding-bottom: 1.5rem !important; 503 | } 504 | 505 | .pl-4, 506 | .px-4 { 507 | padding-left: 1.5rem !important; 508 | } 509 | 510 | .p-5 { 511 | padding: 3rem !important; 512 | } 513 | 514 | .pt-5, 515 | .py-5 { 516 | padding-top: 3rem !important; 517 | } 518 | 519 | .pr-5, 520 | .px-5 { 521 | padding-right: 3rem !important; 522 | } 523 | 524 | .pb-5, 525 | .py-5 { 526 | padding-bottom: 3rem !important; 527 | } 528 | 529 | .pl-5, 530 | .px-5 { 531 | padding-left: 3rem !important; 532 | } 533 | 534 | /* bootstrap custom ends */ 535 | 536 | /* **************************** z-switch starts **************************** */ 537 | 538 | .z-switch { 539 | --z-switch-ball-bg: var(--ifm-color-primary); 540 | --z-switch-checked-bg: var(--ifm-color-primary); 541 | --z-switch-border-color: var(--ifm-color-primary); 542 | --z-switch-hover-border-color: transparent; 543 | --z-switch-disabled-checked-bg: transparent; 544 | --z-switch-width: 41px; 545 | --z-switch-height: 21px; 546 | --z-switch-bg: var(--ifm-background-color); 547 | --z-switch-radius: 12px; 548 | --z-switch-ball-width: 15px; 549 | --z-switch-ball-bg: #d7d7f4; 550 | --z-switch-checked-ball-bg: white; 551 | --z-switch-border-color: #d7d7f4; 552 | --z-switch-hover-border-color: #d7d7f4; 553 | --z-switch-checked-bg: var(--ifm-color-primary); 554 | --z-switch-disabled-bg: var(--ifm-color-primary-light); 555 | --z-switch-disabled-checked-bg: var(--ifm-color-primary-dark); 556 | position: relative; 557 | width: var(--z-switch-width); 558 | height: var(--z-switch-height); 559 | background: var(--z-switch-bg); 560 | border: 1px solid var(--z-switch-border-color); 561 | border-radius: var(--z-switch-radius); 562 | cursor: pointer; 563 | transition: 0.3s; 564 | appearance: none; 565 | -moz-appearance: none; 566 | -webkit-appearance: none; 567 | } 568 | 569 | .z-switch:focus { 570 | outline: none; 571 | } 572 | 573 | .z-switch:checked { 574 | --z-switch-border-color: var(--ifm-color-primary); 575 | background: var(--ifm-color-primary); 576 | } 577 | 578 | .z-switch:checked::before { 579 | --z-switch-ball-bg: var(--z-switch-checked-ball-bg); 580 | transform: translateX(20px); 581 | } 582 | 583 | .z-switch::before { 584 | position: absolute; 585 | content: ''; 586 | top: 2px; 587 | left: 2px; 588 | width: var(--z-switch-ball-width); 589 | height: var(--z-switch-ball-width); 590 | background: var(--z-switch-ball-bg); 591 | border-radius: 50%; 592 | transition: 0.3s; 593 | } 594 | 595 | .z-switch:disabled { 596 | --z-switch-bg: var(--z-switch-disabled-bg); 597 | --z-switch-checked-bg: var(--z-switch-disabled-checked-bg); 598 | } 599 | 600 | .z-switch:disabled:checked { 601 | --z-switch-border-color: #d7d7f4; 602 | background: #d7d7f4; 603 | } 604 | /* **************************** z-switch ends **************************** */ 605 | .z-center { 606 | align-items: center; 607 | -webkit-box-pack: center; 608 | justify-content: center; 609 | -webkit-box-align: center; 610 | display: flex; 611 | } 612 | 613 | .vertical-align { 614 | vertical-align: middle; 615 | } 616 | 617 | .keyword { 618 | color: var(--ifm-color-primary); 619 | } 620 | 621 | .reactive-button-badges a, 622 | .reactive-button-badges img { 623 | padding-left: 2px; 624 | padding-right: 2px; 625 | } 626 | 627 | .shadow-dim { 628 | box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; 629 | } 630 | 631 | svg.react-icon { 632 | vertical-align: middle; 633 | } 634 | 635 | .footer__logo { 636 | max-width: 80px; 637 | } 638 | 639 | .fadeIn { 640 | opacity: 1; 641 | animation-name: fadeIn; 642 | animation-iteration-count: 1; 643 | animation-timing-function: ease-in; 644 | animation-duration: 1s; 645 | } 646 | 647 | @keyframes fadeIn { 648 | 0% { 649 | opacity: 0; 650 | } 651 | 100% { 652 | opacity: 1; 653 | } 654 | } 655 | 656 | @-webkit-keyframes fadeIn { 657 | from { 658 | opacity: 0; 659 | } 660 | to { 661 | opacity: 1; 662 | } 663 | } 664 | 665 | .icon-spin { 666 | -webkit-animation: icon-spin 2s infinite linear; 667 | animation: icon-spin 2s infinite linear; 668 | } 669 | 670 | @-webkit-keyframes icon-spin { 671 | 0% { 672 | -webkit-transform: rotate(0deg); 673 | transform: rotate(0deg); 674 | } 675 | 100% { 676 | -webkit-transform: rotate(359deg); 677 | transform: rotate(359deg); 678 | } 679 | } 680 | 681 | @keyframes icon-spin { 682 | 0% { 683 | -webkit-transform: rotate(0deg); 684 | transform: rotate(0deg); 685 | } 686 | 100% { 687 | -webkit-transform: rotate(359deg); 688 | transform: rotate(359deg); 689 | } 690 | } 691 | -------------------------------------------------------------------------------- /docs/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import clsx from 'clsx'; 3 | import Layout from '@theme/Layout'; 4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 5 | import useBaseUrl from '@docusaurus/useBaseUrl'; 6 | import styles from './index.module.css'; 7 | import ReactiveButton from 'reactive-button'; 8 | import { FaArrowRight } from 'react-icons/fa'; 9 | import { useHistory } from '@docusaurus/router'; 10 | import Playground from '../components/Playground/Playground'; 11 | import PropTypes from 'prop-types'; 12 | 13 | const showcaseCode = ` 14 | function App() { 15 | const [state, setState] = useState('idle'); 16 | 17 | return ( 18 | { 21 | setState('loading'); 22 | setTimeout(() => { 23 | setState('success'); 24 | }, 2000); 25 | }} 26 | /> 27 | ); 28 | } 29 | `.trim(); 30 | 31 | const showcaseScope = { ReactiveButton, useState }; 32 | 33 | const features = [ 34 | { 35 | title: 'Progress Indicator', 36 | description: ( 37 | <> 38 | Don't just click button. See what is happening behind your button 39 | click. 40 | 41 | ), 42 | }, 43 | { 44 | title: 'Animated', 45 | description: ( 46 | <> 47 | The 3D animated buttons replace the traditional buttons with reactive 48 | behavior. 49 | 50 | ), 51 | }, 52 | { 53 | title: 'Customizable', 54 | description: ( 55 | <> 56 | Comes with proper customization. Use the beautiful default themes or 57 | create your own. 58 | 59 | ), 60 | }, 61 | { 62 | title: 'Simple', 63 | description: ( 64 | <> 65 | You know button? Use it just like a button. It's super easy and 66 | simple. 67 | 68 | ), 69 | }, 70 | { 71 | title: 'Lightweight', 72 | description: ( 73 | <>Extremely small in size. Less than 20 KB with zero dependency. 74 | ), 75 | }, 76 | { 77 | title: 'Flexible', 78 | description: ( 79 | <>Reactive button is an isolated component. Use it with any UI library. 80 | ), 81 | }, 82 | ]; 83 | 84 | function Feature({ title, description }) { 85 | return ( 86 |
87 |

{title}

88 |

{description}

89 |
90 | ); 91 | } 92 | 93 | function Home() { 94 | const { 95 | siteConfig: { title, customFields, tagline }, 96 | } = useDocusaurusContext(); 97 | 98 | const { description } = customFields; 99 | 100 | const [showGetStartedButton, setShowGetStartedButton] = useState(false); 101 | const docLink = useBaseUrl('docs/introduction'); 102 | const history = useHistory(); 103 | 104 | useEffect(() => { 105 | setShowGetStartedButton(true); 106 | }, []); 107 | 108 | const buttonOnClickHandler = () => { 109 | history.push(docLink); 110 | }; 111 | 112 | return ( 113 | 114 |
115 |
116 |
117 |
118 | {title} 123 |
124 |

125 | React 126 | 127 | ive 128 | {' '} 129 | Button 130 |

131 |

{tagline}

132 |
133 | 139 | Get Started   140 | 141 | 142 | } 143 | width={'170px'} 144 | height={'50px'} 145 | className={'fadeIn'} 146 | /> 147 |
148 |
149 |
150 | {showGetStartedButton && ( 151 |
152 |
153 |
154 |
155 |
156 |
157 | 162 |
163 |
164 |
165 |
166 |
167 |
168 | )} 169 |
170 | {features && features.length > 0 && ( 171 |
172 |
173 |
174 | {features.map((props, idx) => ( 175 | 176 | ))} 177 |
178 |
179 |
180 | )} 181 |
182 |
183 |
184 | ); 185 | } 186 | 187 | Feature.propTypes = { 188 | title: PropTypes.string, 189 | description: PropTypes.node, 190 | }; 191 | 192 | export default Home; 193 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | /** 4 | * CSS files with the .module.css suffix will be treated as CSS modules 5 | * and scoped locally. 6 | */ 7 | 8 | .heroBanner { 9 | padding: 4rem 0; 10 | text-align: center; 11 | position: relative; 12 | overflow: hidden; 13 | } 14 | 15 | @media screen and (max-width: 966px) { 16 | .heroBanner { 17 | padding: 2rem; 18 | } 19 | } 20 | 21 | .buttons { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | } 26 | 27 | .features { 28 | display: flex; 29 | align-items: center; 30 | padding: 2rem 0; 31 | width: 100%; 32 | } 33 | 34 | .featureImage { 35 | height: 200px; 36 | width: 200px; 37 | padding-bottom: 20px; 38 | } 39 | 40 | html[data-theme='dark'] .features { 41 | background-color: #33363b; 42 | } 43 | 44 | .exampleComponent { 45 | display: flex; 46 | align-items: center; 47 | padding: 3rem 0; 48 | width: 100%; 49 | background-color: #f7f7f7; 50 | } 51 | 52 | html[data-theme='dark'] .exampleComponent { 53 | background-color: #18191a; 54 | } 55 | 56 | .exampleComponent__container { 57 | margin-left: auto; 58 | margin-right: auto; 59 | padding-left: var(--ifm-spacing-horizontal); 60 | padding-right: var(--ifm-spacing-horizontal); 61 | width: 100%; 62 | } 63 | 64 | .exampleComponent__item { 65 | padding: 50px 100px; 66 | } 67 | 68 | @media screen and (max-width: 966px) { 69 | .exampleComponent__item { 70 | padding: unset; 71 | } 72 | } 73 | 74 | .logo { 75 | animation-duration: 2s; 76 | animation-name: jackInTheBox; 77 | width: 80px; 78 | } 79 | 80 | .heroProjectKeywords { 81 | color: var(--ifm-color-primary); 82 | } 83 | 84 | .heroProjectKeywordsSecondary { 85 | color: var(--ifm-color-primary-lighter); 86 | } 87 | 88 | .root { 89 | opacity: 1; 90 | animation-name: fadeIn; 91 | animation-iteration-count: 1; 92 | animation-timing-function: ease-in; 93 | animation-duration: 1s; 94 | } 95 | 96 | /* animations */ 97 | @keyframes jackInTheBox { 98 | from { 99 | opacity: 0; 100 | transform: scale(0.1) rotate(30deg); 101 | transform-origin: center bottom; 102 | } 103 | 104 | 50% { 105 | transform: rotate(-10deg); 106 | } 107 | 108 | 70% { 109 | transform: rotate(3deg); 110 | } 111 | 112 | to { 113 | opacity: 1; 114 | transform: scale(1); 115 | } 116 | } 117 | 118 | @keyframes fadeIn { 119 | 0% { 120 | opacity: 0; 121 | } 122 | 100% { 123 | opacity: 1; 124 | } 125 | } 126 | 127 | @-webkit-keyframes fadeIn { 128 | from { 129 | opacity: 0; 130 | } 131 | to { 132 | opacity: 1; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/dependencies.svg: -------------------------------------------------------------------------------- 1 | dependenciesdependenciesnonenone -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/logo/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/logo/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/static/img/logo/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/logo/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/static/img/logo/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/logo/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/static/img/logo/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/logo/favicon-16x16.png -------------------------------------------------------------------------------- /docs/static/img/logo/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/logo/favicon-32x32.png -------------------------------------------------------------------------------- /docs/static/img/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/logo/logo.png -------------------------------------------------------------------------------- /docs/static/img/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/preview.gif -------------------------------------------------------------------------------- /docs/static/img/social_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arifszn/reactive-button/b1c129b39c3ddbba0cf6f2399c18964046f4cfaf/docs/static/img/social_preview.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactive-button", 3 | "version": "1.3.15", 4 | "description": "3D animated react button component with progress bar", 5 | "private": false, 6 | "homepage": "https://github.com/arifszn/reactive-button#readme", 7 | "main": "./dist/index.cjs.js", 8 | "module": "./dist/index.esm.js", 9 | "typings": "./types/index.d.ts", 10 | "scripts": { 11 | "build": "rollup -c --bundleConfigAsCjs", 12 | "lint": "eslint --ext .js,.jsx .", 13 | "lint:fix": "eslint --ext .js,.jsx --fix .", 14 | "prettier": "prettier --check './**/*.{js,jsx,ts,tsx,css,md,json}'", 15 | "prettier:fix": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}'" 16 | }, 17 | "peerDependencies": { 18 | "react": ">=16.8.0", 19 | "react-dom": ">=16.8.0" 20 | }, 21 | "devDependencies": { 22 | "@babel/cli": "^7.11.6", 23 | "@babel/core": "^7.11.6", 24 | "@babel/preset-env": "^7.11.5", 25 | "@babel/preset-react": "^7.10.4", 26 | "@rollup/plugin-babel": "^6.0.3", 27 | "@rollup/plugin-commonjs": "^25.0.2", 28 | "@rollup/plugin-image": "^3.0.2", 29 | "@rollup/plugin-node-resolve": "^15.1.0", 30 | "@rollup/plugin-terser": "^0.4.3", 31 | "@types/react": "^18.0.17", 32 | "eslint": "^8.11.0", 33 | "eslint-config-prettier": "^8.5.0", 34 | "eslint-plugin-prettier": "^4.0.0", 35 | "eslint-plugin-react": "^7.29.4", 36 | "prettier": "^2.5.1", 37 | "prop-types": "^15.8.1", 38 | "rollup": "^3.26.0", 39 | "rollup-plugin-peer-deps-external": "^2.2.3", 40 | "rollup-plugin-postcss": "^4.0.2" 41 | }, 42 | "author": "arifszn", 43 | "license": "MIT", 44 | "repository": { 45 | "type": "git", 46 | "url": "https://github.com/arifszn/reactive-button.git" 47 | }, 48 | "bugs": { 49 | "url": "https://github.com/arifszn/reactive-button/issues" 50 | }, 51 | "keywords": [ 52 | "react", 53 | "button", 54 | "react-button", 55 | "react-animated-button", 56 | "react-3d-button", 57 | "react-button-progress-bar", 58 | "react-component", 59 | "button-component", 60 | "button-progress", 61 | "button-ui", 62 | "simple-button", 63 | "progressbar", 64 | "animation", 65 | "reactjs", 66 | "reactive", 67 | "3d-button", 68 | "button-progress", 69 | "progress-bar", 70 | "component", 71 | "beautiful-button" 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from '@rollup/plugin-babel'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import external from 'rollup-plugin-peer-deps-external'; 4 | import postcss from 'rollup-plugin-postcss'; 5 | import resolve from '@rollup/plugin-node-resolve'; 6 | import image from '@rollup/plugin-image'; 7 | import pkg from './package.json'; 8 | import terser from '@rollup/plugin-terser'; 9 | 10 | export default { 11 | input: './src/index.js', 12 | output: [ 13 | { 14 | file: pkg.main, 15 | format: 'cjs', 16 | exports: 'auto', 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'esm', 21 | }, 22 | ], 23 | plugins: [ 24 | external(), 25 | terser(), 26 | postcss(), 27 | babel({ 28 | exclude: 'node_modules/**', 29 | }), 30 | resolve(), 31 | commonjs(), 32 | image(), 33 | ], 34 | }; 35 | -------------------------------------------------------------------------------- /src/css/index.css: -------------------------------------------------------------------------------- 1 | .reactive-btn-wrapper, 2 | .reactive-btn { 3 | --reactive-button-min-width: 100px; 4 | --reactive-button-min-height: 35px; 5 | --reactive-button-font-size: 14px; 6 | --reactive-button-font-weight: 400; 7 | --reactive-button-border-radius: 0px; 8 | --reactive-button-text-color: #ffffff; 9 | --reactive-progress-color: rgba(0, 0, 0, 0.15); 10 | 11 | --reactive-button-primary-color: rgba(88, 103, 221, 1); 12 | --reactive-button-secondary-color: rgba(108, 117, 125, 1); 13 | --reactive-button-dark-color: rgba(66, 78, 106, 1); 14 | --reactive-button-light-color: rgba(183, 186, 191, 1); 15 | --reactive-button-green-color: rgba(37, 162, 51, 1); 16 | --reactive-button-red-color: rgba(244, 81, 108, 1); 17 | --reactive-button-yellow-color: rgba(255, 193, 7, 1); 18 | --reactive-button-teal-color: rgba(0, 181, 173, 1); 19 | --reactive-button-violet-color: rgba(100, 53, 201, 1); 20 | --reactive-button-blue-color: rgba(66, 153, 225, 1); 21 | 22 | --reactive-progress-outline-primary-color: rgba(88, 103, 221, 0.3); 23 | --reactive-progress-outline-secondary-color: rgba(108, 117, 125, 0.3); 24 | --reactive-progress-outline-dark-color: rgba(66, 78, 106, 0.3); 25 | --reactive-progress-outline-light-color: rgba(214, 212, 212, 0.3); 26 | --reactive-progress-outline-green-color: rgba(37, 162, 51, 0.3); 27 | --reactive-progress-outline-red-color: rgba(244, 81, 108, 0.3); 28 | --reactive-progress-outline-yellow-color: rgba(255, 193, 7, 0.3); 29 | --reactive-progress-outline-teal-color: rgba(0, 181, 173, 0.3); 30 | --reactive-progress-outline-violet-color: rgba(100, 53, 201, 0.3); 31 | --reactive-progress-outline-blue-color: rgba(66, 153, 225, 0.3); 32 | } 33 | 34 | .reactive-btn-wrapper { 35 | display: inline-block; 36 | min-width: var(--reactive-button-min-width); 37 | height: var(--reactive-button-min-height); 38 | } 39 | 40 | .reactive-btn-wrapper.tiny { 41 | min-width: calc(var(--reactive-button-min-width) - 35px); 42 | height: calc(var(--reactive-button-min-height) - 11px); 43 | } 44 | 45 | .reactive-btn-wrapper.small { 46 | min-width: calc(var(--reactive-button-min-width) - 25px); 47 | height: calc(var(--reactive-button-min-height) - 5px); 48 | } 49 | 50 | .reactive-btn-wrapper.large { 51 | min-width: calc(var(--reactive-button-min-width) + 25px); 52 | height: calc(var(--reactive-button-min-height) + 5px); 53 | } 54 | 55 | .reactive-btn-wrapper.block { 56 | width: 100%; 57 | } 58 | 59 | .reactive-btn { 60 | margin-bottom: 0; 61 | padding: 6px 14px 6px; 62 | font-size: var(--reactive-button-font-size); 63 | font-weight: var(--reactive-button-font-weight); 64 | width: 100%; 65 | min-height: 100%; 66 | color: var(--reactive-button-text-color); 67 | text-align: center; 68 | line-height: 1.5; 69 | text-decoration: none; 70 | user-select: none; 71 | -webkit-user-select: none; 72 | -moz-user-select: none; 73 | -ms-user-select: none; 74 | vertical-align: middle; 75 | cursor: pointer; 76 | background-color: var(--reactive-button-primary-color); 77 | border: none; 78 | -webkit-border-radius: var(--reactive-button-border-radius); 79 | -moz-border-radius: var(--reactive-button-border-radius); 80 | border-radius: var(--reactive-button-border-radius); 81 | -webkit-box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.2); 82 | -moz-box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.2); 83 | box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.2); 84 | -webkit-transition: 0.1s; 85 | -moz-transition: 0.1s; 86 | transition: 0.1s; 87 | -webkit-box-sizing: border-box; 88 | -moz-box-sizing: border-box; 89 | box-sizing: border-box; 90 | position: relative; 91 | overflow: hidden; 92 | outline: none !important; 93 | align-items: center; 94 | -webkit-box-pack: center; 95 | -webkit-box-align: center; 96 | } 97 | 98 | .reactive-btn.outline { 99 | background-color: transparent !important; 100 | } 101 | 102 | .reactive-btn:disabled { 103 | cursor: default; 104 | } 105 | 106 | .reactive-btn:hover:not(:disabled):not(.disabled) { 107 | opacity: 0.85; 108 | } 109 | 110 | .reactive-btn:not(.no-animation):hover:not(:disabled):not(.disabled) { 111 | opacity: 0.85; 112 | -webkit-box-shadow: inset 0 -4px 0 rgba(0, 0, 0, 0.2); 113 | -moz-box-shadow: inset 0 -4px 0 rgba(0, 0, 0, 0.2); 114 | box-shadow: inset 0 -4px 0 rgba(0, 0, 0, 0.2); 115 | margin-top: -1px; 116 | padding: 6px 14px 8px; 117 | } 118 | 119 | .reactive-btn:not(.no-animation):active:not(:disabled):not(.disabled) { 120 | -webkit-box-shadow: none; 121 | -moz-box-shadow: none; 122 | box-shadow: none; 123 | margin-top: 1px; 124 | padding: 6px 14px 4px; 125 | } 126 | 127 | .reactive-btn:focus { 128 | outline: none !important; 129 | } 130 | 131 | .reactive-btn.rounded { 132 | border-radius: 50rem !important; 133 | -webkit-border-radius: 50rem !important; 134 | -moz-border-radius: 50rem !important; 135 | } 136 | 137 | .reactive-btn.disabled { 138 | opacity: 0.7; 139 | } 140 | 141 | .reactive-btn .content { 142 | position: relative; 143 | } 144 | 145 | .reactive-btn .progress { 146 | position: absolute; 147 | top: 0; 148 | left: 0; 149 | right: 0; 150 | bottom: 0; 151 | height: 100%; 152 | width: 100%; 153 | background-color: var(--reactive-progress-color); 154 | transform: translateX(-100%); 155 | transition: transform 0.2s ease; 156 | } 157 | 158 | .reactive-btn:not([data-button-state='idle']) .progress { 159 | transform: translateX(0%); 160 | transition: transform 3s cubic-bezier(0.59, 0.01, 0.41, 0.99); 161 | } 162 | 163 | .reactive-btn .drbll1:after { 164 | content: '.'; 165 | animation: dots 1s steps(5, end) infinite; 166 | } 167 | 168 | .reactive-btn-wrapper.tiny .reactive-btn { 169 | font-size: calc(var(--reactive-button-font-size) - 4px); 170 | line-height: 1; 171 | } 172 | 173 | .reactive-btn-wrapper.small .reactive-btn { 174 | font-size: calc(var(--reactive-button-font-size) - 2px); 175 | } 176 | 177 | .reactive-btn-wrapper.large .reactive-btn { 178 | font-size: calc(var(--reactive-button-font-size) + 2px); 179 | } 180 | 181 | /* primary button starts */ 182 | .reactive-btn.primary.outline { 183 | border-color: var(--reactive-button-primary-color); 184 | color: var(--reactive-button-primary-color); 185 | border: 1px solid var(--reactive-button-primary-color); 186 | box-shadow: inset 0 -1px 0 var(--reactive-button-primary-color); 187 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-primary-color); 188 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-primary-color); 189 | } 190 | 191 | .reactive-btn.primary.outline .progress { 192 | background-color: var(--reactive-progress-outline-primary-color) !important; 193 | } 194 | 195 | .reactive-btn.primary.outline:hover:not(:disabled):not(.disabled) { 196 | box-shadow: inset 0 -3px 0 var(--reactive-button-primary-color); 197 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-primary-color); 198 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-primary-color); 199 | } 200 | 201 | .reactive-btn.primary.shadow { 202 | box-shadow: 0px 5px 16px -3px var(--reactive-button-primary-color) !important; 203 | } 204 | 205 | /* primary button ends */ 206 | 207 | /* secondary button starts */ 208 | .reactive-btn.secondary { 209 | background: var(--reactive-button-secondary-color); 210 | } 211 | 212 | .reactive-btn.secondary.outline { 213 | border-color: var(--reactive-button-secondary-color); 214 | color: var(--reactive-button-secondary-color); 215 | border: 1px solid var(--reactive-button-secondary-color); 216 | box-shadow: inset 0 -1px 0 var(--reactive-button-secondary-color); 217 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-secondary-color); 218 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-secondary-color); 219 | } 220 | 221 | .reactive-btn.secondary.outline .progress { 222 | background-color: var(--reactive-progress-outline-secondary-color) !important; 223 | } 224 | 225 | .reactive-btn.secondary.outline:hover:not(:disabled):not(.disabled) { 226 | box-shadow: inset 0 -3px 0 var(--reactive-button-secondary-color); 227 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-secondary-color); 228 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-secondary-color); 229 | } 230 | 231 | .reactive-btn.secondary.shadow { 232 | box-shadow: 0px 5px 16px -3px var(--reactive-button-secondary-color) !important; 233 | } 234 | 235 | /* secondary button ends */ 236 | 237 | /* dark button starts */ 238 | 239 | .reactive-btn.dark { 240 | background: var(--reactive-button-dark-color); 241 | } 242 | 243 | .reactive-btn.dark.outline { 244 | border-color: var(--reactive-button-dark-color); 245 | color: var(--reactive-button-dark-color); 246 | border: 1px solid var(--reactive-button-dark-color); 247 | box-shadow: inset 0 -1px 0 var(--reactive-button-dark-color); 248 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-dark-color); 249 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-dark-color); 250 | } 251 | 252 | .reactive-btn.dark.outline .progress { 253 | background-color: var(--reactive-progress-outline-dark-color) !important; 254 | } 255 | 256 | .reactive-btn.dark.outline:hover:not(:disabled):not(.disabled) { 257 | box-shadow: inset 0 -3px 0 var(--reactive-button-dark-color); 258 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-dark-color); 259 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-dark-color); 260 | } 261 | 262 | .reactive-btn.dark.shadow { 263 | box-shadow: 0px 5px 16px -3px var(--reactive-button-dark-color) !important; 264 | } 265 | 266 | /* dark button ends */ 267 | 268 | /* light button starts */ 269 | 270 | .reactive-btn.light { 271 | background: var(--reactive-button-light-color); 272 | color: #000000; 273 | } 274 | 275 | .reactive-btn.light.outline { 276 | border-color: var(--reactive-button-light-color); 277 | color: var(--reactive-button-light-color); 278 | border: 1px solid var(--reactive-button-light-color); 279 | box-shadow: inset 0 -1px 0 var(--reactive-button-light-color); 280 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-light-color); 281 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-light-color); 282 | } 283 | 284 | .reactive-btn.light.outline .progress { 285 | background-color: var(--reactive-progress-outline-light-color) !important; 286 | } 287 | 288 | .reactive-btn.light.outline:hover:not(:disabled):not(.disabled) { 289 | box-shadow: inset 0 -3px 0 var(--reactive-button-light-color); 290 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-light-color); 291 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-light-color); 292 | } 293 | 294 | .reactive-btn.light.shadow { 295 | box-shadow: 0px 5px 16px -3px var(--reactive-button-light-color) !important; 296 | } 297 | 298 | /* light button ends */ 299 | 300 | /* green button starts */ 301 | .reactive-btn.green { 302 | background: var(--reactive-button-green-color); 303 | } 304 | 305 | .reactive-btn.green.outline { 306 | border-color: var(--reactive-button-green-color); 307 | color: var(--reactive-button-green-color); 308 | border: 1px solid var(--reactive-button-green-color); 309 | box-shadow: inset 0 -1px 0 var(--reactive-button-green-color); 310 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-green-color); 311 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-green-color); 312 | } 313 | 314 | .reactive-btn.green.outline .progress { 315 | background-color: var(--reactive-progress-outline-green-color) !important; 316 | } 317 | 318 | .reactive-btn.green.outline:hover:not(:disabled):not(.disabled) { 319 | box-shadow: inset 0 -3px 0 var(--reactive-button-green-color); 320 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-green-color); 321 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-green-color); 322 | } 323 | 324 | .reactive-btn.green.shadow { 325 | box-shadow: 0px 5px 16px -3px var(--reactive-button-green-color) !important; 326 | } 327 | 328 | /* green button ends */ 329 | 330 | /* red button starts */ 331 | .reactive-btn.red { 332 | background: var(--reactive-button-red-color); 333 | } 334 | 335 | .reactive-btn.red.outline { 336 | border-color: var(--reactive-button-red-color); 337 | color: var(--reactive-button-red-color); 338 | border: 1px solid var(--reactive-button-red-color); 339 | box-shadow: inset 0 -1px 0 var(--reactive-button-red-color); 340 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-red-color); 341 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-red-color); 342 | } 343 | 344 | .reactive-btn.red.outline .progress { 345 | background-color: var(--reactive-progress-outline-red-color) !important; 346 | } 347 | 348 | .reactive-btn.red.outline:hover:not(:disabled):not(.disabled) { 349 | box-shadow: inset 0 -3px 0 var(--reactive-button-red-color); 350 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-red-color); 351 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-red-color); 352 | } 353 | 354 | .reactive-btn.red.shadow { 355 | box-shadow: 0px 5px 16px -3px var(--reactive-button-red-color) !important; 356 | } 357 | 358 | /* red button ends */ 359 | 360 | /* yellow button starts */ 361 | .reactive-btn.yellow { 362 | background: var(--reactive-button-yellow-color); 363 | color: #000000; 364 | } 365 | 366 | .reactive-btn.yellow.outline { 367 | border-color: var(--reactive-button-yellow-color); 368 | color: var(--reactive-button-yellow-color); 369 | border: 1px solid var(--reactive-button-yellow-color); 370 | box-shadow: inset 0 -1px 0 var(--reactive-button-yellow-color); 371 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-yellow-color); 372 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-yellow-color); 373 | } 374 | 375 | .reactive-btn.yellow.outline .progress { 376 | background-color: var(--reactive-progress-outline-yellow-color) !important; 377 | } 378 | 379 | .reactive-btn.yellow.outline:hover:not(:disabled):not(.disabled) { 380 | box-shadow: inset 0 -3px 0 var(--reactive-button-yellow-color); 381 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-yellow-color); 382 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-yellow-color); 383 | } 384 | 385 | .reactive-btn.yellow.shadow { 386 | box-shadow: 0px 5px 16px -3px var(--reactive-button-yellow-color) !important; 387 | } 388 | 389 | /* yellow button ends */ 390 | 391 | /* teal button starts */ 392 | .reactive-btn.teal { 393 | background: var(--reactive-button-teal-color); 394 | } 395 | 396 | .reactive-btn.teal.outline { 397 | border-color: var(--reactive-button-teal-color); 398 | color: var(--reactive-button-teal-color); 399 | border: 1px solid var(--reactive-button-teal-color); 400 | box-shadow: inset 0 -1px 0 var(--reactive-button-teal-color); 401 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-teal-color); 402 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-teal-color); 403 | } 404 | 405 | .reactive-btn.teal.outline .progress { 406 | background-color: var(--reactive-progress-outline-teal-color) !important; 407 | } 408 | 409 | .reactive-btn.teal.outline:hover:not(:disabled):not(.disabled) { 410 | box-shadow: inset 0 -3px 0 var(--reactive-button-teal-color); 411 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-teal-color); 412 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-teal-color); 413 | } 414 | 415 | .reactive-btn.teal.shadow { 416 | box-shadow: 0px 5px 16px -3px var(--reactive-button-teal-color) !important; 417 | } 418 | 419 | /* teal button ends */ 420 | 421 | /* violet button starts */ 422 | .reactive-btn.violet { 423 | background: var(--reactive-button-violet-color); 424 | } 425 | 426 | .reactive-btn.violet.outline { 427 | border-color: var(--reactive-button-violet-color); 428 | color: var(--reactive-button-violet-color); 429 | border: 1px solid var(--reactive-button-violet-color); 430 | box-shadow: inset 0 -1px 0 var(--reactive-button-violet-color); 431 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-violet-color); 432 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-violet-color); 433 | } 434 | 435 | .reactive-btn.violet.outline .progress { 436 | background-color: var(--reactive-progress-outline-violet-color) !important; 437 | } 438 | 439 | .reactive-btn.violet.outline:hover:not(:disabled):not(.disabled) { 440 | box-shadow: inset 0 -3px 0 var(--reactive-button-violet-color); 441 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-violet-color); 442 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-violet-color); 443 | } 444 | 445 | .reactive-btn.violet.shadow { 446 | box-shadow: 0px 5px 16px -3px var(--reactive-button-violet-color) !important; 447 | } 448 | 449 | /* violet button ends */ 450 | 451 | /* blue button starts */ 452 | .reactive-btn.blue { 453 | background: var(--reactive-button-blue-color); 454 | } 455 | 456 | .reactive-btn.blue.outline { 457 | border-color: var(--reactive-button-blue-color); 458 | color: var(--reactive-button-blue-color); 459 | border: 1px solid var(--reactive-button-blue-color); 460 | box-shadow: inset 0 -1px 0 var(--reactive-button-blue-color); 461 | -webkit-box-shadow: inset 0 -1px 0 var(--reactive-button-blue-color); 462 | -moz-box-shadow: inset 0 -1px 0 var(--reactive-button-blue-color); 463 | } 464 | 465 | .reactive-btn.blue.outline .progress { 466 | background-color: var(--reactive-progress-outline-blue-color) !important; 467 | } 468 | 469 | .reactive-btn.blue.outline:hover:not(:disabled):not(.disabled) { 470 | box-shadow: inset 0 -3px 0 var(--reactive-button-blue-color); 471 | -webkit-box-shadow: inset 0 -3px 0 var(--reactive-button-blue-color); 472 | -moz-box-shadow: inset 0 -3px 0 var(--reactive-button-blue-color); 473 | } 474 | 475 | .reactive-btn.blue.shadow { 476 | box-shadow: 0px 5px 16px -3px var(--reactive-button-blue-color) !important; 477 | } 478 | 479 | /* blue button ends */ 480 | 481 | .reactive-spin { 482 | animation: reactive-spin 2s infinite linear; 483 | } 484 | .reactive-btn-loading-svg, 485 | .reactive-btn-success-svg, 486 | .reactive-btn-error-svg { 487 | display: inline-block; 488 | font-size: inherit; 489 | height: 1em; 490 | overflow: visible; 491 | vertical-align: -0.125em; 492 | } 493 | 494 | @keyframes dots { 495 | 0%, 496 | 20% { 497 | color: rgba(0, 0, 0, 0); 498 | text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0); 499 | } 500 | 501 | 40% { 502 | color: white; 503 | text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0); 504 | } 505 | 506 | 60% { 507 | text-shadow: 0.25em 0 0 white, 0.5em 0 0 rgba(0, 0, 0, 0); 508 | } 509 | 510 | 80%, 511 | 100% { 512 | text-shadow: 0.25em 0 0 white, 0.5em 0 0 white; 513 | } 514 | } 515 | 516 | @keyframes reactive-spin { 517 | 0% { 518 | -webkit-transform: rotate(0deg); 519 | transform: rotate(0deg); 520 | } 521 | 100% { 522 | -webkit-transform: rotate(360deg); 523 | transform: rotate(360deg); 524 | } 525 | } 526 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import './css/index.css'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const loadingIcon = ( 6 | 21 | ); 22 | 23 | const successIcon = ( 24 | 39 | ); 40 | 41 | const errorIcon = ( 42 | 57 | ); 58 | 59 | const ReactiveButton = (props) => { 60 | const color = props.color ? props.color : 'primary'; 61 | 62 | const idleText = props.idleText ? props.idleText : 'Click Me'; 63 | 64 | const loadingText = 65 | props.loadingText && props.loadingText !== '' 66 | ? props.loadingText 67 | : 'Loading'; 68 | 69 | const successText = 70 | props.successText && props.successText !== '' 71 | ? props.successText 72 | : 'Success'; 73 | 74 | const errorText = 75 | props.errorText && props.errorText !== '' ? props.errorText : 'Error'; 76 | 77 | const type = props.type ? props.type : 'button'; 78 | 79 | const className = `reactive-btn${ 80 | props.className ? ' ' + props.className : '' 81 | }`; 82 | 83 | const outline = props.outline ? true : false; 84 | 85 | const shadow = props.shadow ? true : false; 86 | 87 | const style = props.style ? props.style : {}; 88 | 89 | const rounded = props.rounded ? true : false; 90 | 91 | const size = props.size ? props.size : 'normal'; 92 | 93 | const animation = 94 | typeof props.animation !== 'undefined' && props.animation === false 95 | ? false 96 | : true; 97 | 98 | const [buttonState, setButtonState] = useState( 99 | props.buttonState ? props.buttonState : 'idle' 100 | ); 101 | 102 | const onClickHandler = () => { 103 | if (typeof props.onClick !== 'undefined') { 104 | props.onClick(); 105 | } 106 | }; 107 | 108 | useEffect(() => { 109 | if (typeof props.buttonState !== 'undefined') { 110 | setButtonState(props.buttonState); 111 | if (props.buttonState === 'success' || props.buttonState === 'error') { 112 | setTimeout( 113 | () => { 114 | setButtonState('idle'); 115 | }, 116 | props.messageDuration ? props.messageDuration : 2000 117 | ); 118 | } 119 | } 120 | }, [props.buttonState, props.messageDuration]); 121 | 122 | const getButtonText = (currentButtonState) => { 123 | if (currentButtonState === 'idle') { 124 | return idleText; 125 | } else if (currentButtonState === 'loading') { 126 | return loadingText === 'Loading' ? ( 127 | 128 | {loadingIcon} {loadingText} 129 | 130 | ) : ( 131 | loadingText 132 | ); 133 | } else if (currentButtonState === 'success') { 134 | return successText === 'Success' ? ( 135 | 136 | {successIcon} {successText} 137 | 138 | ) : ( 139 | successText 140 | ); 141 | } else if (currentButtonState === 'error') { 142 | return errorText === 'Error' ? ( 143 | 144 | {errorIcon} {errorText} 145 | 146 | ) : ( 147 | errorText 148 | ); 149 | } 150 | }; 151 | 152 | return ( 153 | 154 | 158 | 176 | 177 | 178 | ); 179 | }; 180 | 181 | ReactiveButton.propTypes = { 182 | color: PropTypes.string, 183 | idleText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), 184 | loadingText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), 185 | successText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), 186 | errorText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), 187 | type: PropTypes.string, 188 | className: PropTypes.string, 189 | outline: PropTypes.bool, 190 | shadow: PropTypes.bool, 191 | style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), 192 | rounded: PropTypes.bool, 193 | size: PropTypes.string, 194 | animation: PropTypes.bool, 195 | buttonState: PropTypes.string, 196 | onClick: PropTypes.func, 197 | messageDuration: PropTypes.number, 198 | block: PropTypes.bool, 199 | width: PropTypes.string, 200 | height: PropTypes.string, 201 | buttonRef: PropTypes.oneOfType([ 202 | PropTypes.func, 203 | PropTypes.shape({ current: PropTypes.any }), 204 | ]), 205 | disabled: PropTypes.bool, 206 | }; 207 | 208 | export default ReactiveButton; 209 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Reactive Button 2 | // Project https://github.com/arifszn/reactive-button 3 | // Author: Ariful Alam 4 | 5 | import { 6 | CSSProperties, 7 | Component, 8 | MouseEvent, 9 | MutableRefObject, 10 | ReactNode, 11 | } from 'react'; 12 | 13 | export interface ReactiveButtonProps { 14 | /** 15 | * Current button state. Values: 'idle' | 'loading' | 'success' | 'error' 16 | * 17 | * Default: 'idle'. 18 | */ 19 | buttonState?: string; 20 | 21 | /** 22 | * Callback function when clicking button. 23 | * 24 | * Default: () => {} 25 | */ 26 | onClick?: (event: MouseEvent) => void; 27 | 28 | /** 29 | * Button color. values: 'primary' | 'secondary' | 'dark' | 'light' | 'green' | 'red' | 'yellow' | 'teal' | 'violet' | 'blue' 30 | * 31 | * Default: 'primary' 32 | */ 33 | color?: string; 34 | 35 | /** 36 | * Button text when idle. 37 | * 38 | * Default: 'Click Me' 39 | */ 40 | idleText?: string | ReactNode; 41 | 42 | /** 43 | * Button text when loading. 44 | * 45 | * Default: 'Loading' 46 | */ 47 | loadingText?: string | ReactNode; 48 | 49 | /** 50 | * Button text when loading successful. 51 | * 52 | * Default: 'Success' 53 | */ 54 | successText?: string | ReactNode; 55 | 56 | /** 57 | * Button text when loading failed. 58 | * 59 | * Default: 'Error' 60 | */ 61 | errorText?: string | ReactNode; 62 | 63 | /** 64 | * Button type attribute. Values: 'button' | 'submit' | 'reset' 65 | * 66 | * Default: 'button' 67 | */ 68 | type?: string; 69 | 70 | /** 71 | * Button classnames. 72 | * 73 | * Default: '' 74 | */ 75 | className?: string; 76 | 77 | /** 78 | * Custom style. 79 | * 80 | * Default: {} 81 | */ 82 | style?: CSSProperties; 83 | 84 | /** 85 | * Enable outline effect. 86 | * 87 | * Default: false 88 | */ 89 | outline?: boolean; 90 | 91 | /** 92 | * Enable shadow effect. 93 | * 94 | * Default: false 95 | */ 96 | shadow?: boolean; 97 | 98 | /** 99 | * Enable rounded button. 100 | * 101 | * Default: false 102 | */ 103 | rounded?: boolean; 104 | 105 | /** 106 | * Button size. Values: 'tiny' | 'small' | 'normal' | 'large' 107 | * 108 | * Default: 'normal' 109 | */ 110 | size?: string; 111 | 112 | /** 113 | * Block button. 114 | * 115 | * Default: false 116 | */ 117 | block?: boolean; 118 | 119 | /** 120 | * Success/Error message duration in millisecond. 121 | * 122 | * Default: 2000 123 | */ 124 | messageDuration?: number; 125 | 126 | /** 127 | * Disable button. 128 | * 129 | * Default: false 130 | */ 131 | disabled?: boolean; 132 | 133 | /** 134 | * Button reference. 135 | * 136 | * Default null 137 | */ 138 | buttonRef?: MutableRefObject | null; 139 | 140 | /** 141 | * Override button width. 142 | * 143 | * Default null 144 | */ 145 | width?: string | null; 146 | 147 | /** 148 | * Override button height. 149 | * 150 | * Default null 151 | */ 152 | height?: string | null; 153 | 154 | /** 155 | * Button hover and click animation. 156 | * 157 | * Default true 158 | */ 159 | animation?: boolean; 160 | } 161 | 162 | declare class ReactiveButton extends Component {} 163 | 164 | export default ReactiveButton; 165 | --------------------------------------------------------------------------------