├── .babelrc ├── .editorconfig ├── .github └── dependabot.yml ├── .gitignore ├── .npmrc ├── .storybook ├── addons.js └── config.js ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── demo.gif ├── package.json ├── rollup.config.js ├── src ├── components │ ├── ClapButton.js │ ├── ClapCount.js │ ├── ClapCountTotal.js │ ├── ClapIcon.js │ ├── ClapWrap.js │ └── ClearClaps.js ├── index.js └── utils.js └── stories └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "production": { 4 | "plugins": [ 5 | ["transform-react-remove-prop-types", {"removeImport": true}], 6 | "transform-react-pure-class-to-function", 7 | "@babel/plugin-transform-react-constant-elements" 8 | ] 9 | } 10 | }, 11 | "presets": [ 12 | ["@babel/preset-env", { 13 | "targets": { "browsers": [ 14 | ">0.25%", 15 | "not ie 11", 16 | "not op_mini all" 17 | ] } 18 | }], 19 | "@babel/preset-react" 20 | ], 21 | "plugins": [ 22 | "@babel/plugin-proposal-class-properties", 23 | "@babel/plugin-proposal-object-rest-spread" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | max_line_length = 100 13 | indent_brace_style = 1TBS 14 | spaces_around_operators = true 15 | quote_type = auto 16 | 17 | [package.json] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [*.md] -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | # Check for updates to GitHub Actions every weekday 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | storybook-static 4 | dist 5 | .envrc 6 | stats.html 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | unsafe-perm=true 2 | save-prefix=~ 3 | shrinkwrap=false 4 | save=false -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-options/register' 2 | import '@storybook/addon-viewport/register' 3 | import 'storybook-addon-jsx/register' 4 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { setAddon, configure } from '@storybook/react' 2 | import { setOptions } from '@storybook/addon-options' 3 | import JSXAddon from 'storybook-addon-jsx' 4 | 5 | function loadStories () { 6 | require('../stories') 7 | } 8 | 9 | setAddon(JSXAddon) 10 | 11 | setOptions({ 12 | name: 'react-clap-button', 13 | url: 'https://github.com/Kikobeats/react-clap-button', 14 | goFullScreen: false, 15 | showStoriesPanel: true, 16 | showAddonPanel: true, 17 | showSearchBox: false, 18 | addonPanelInRight: true, 19 | sortStoriesByKind: false 20 | }) 21 | 22 | configure(loadStories, module) 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - lts/* 5 | 6 | stages: 7 | - Test 8 | - name: Release 9 | if: branch = master AND commit_message !~ /(release|no-release)/ 10 | 11 | jobs: 12 | include: 13 | - stage: Release 14 | node_js: lts/* 15 | install: npm install --no-package-lock 16 | before_deploy: 17 | - git config user.email ${GITHUB_EMAIL:-"travis@travis-ci.org"} 18 | - git config user.name ${GITHUB_USER:-"Travis CI"} 19 | - git remote set-url origin https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git 20 | - git checkout master 21 | deploy: 22 | skip_cleanup: true 23 | provider: script 24 | script: npm run release 25 | on: 26 | branch: master 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.2.12](https://github.com/kikobeats/react-clap-button/compare/v1.2.11...v1.2.12) (2020-10-24) 6 | 7 | ### [1.2.11](https://github.com/kikobeats/react-clap-button/compare/v1.2.10...v1.2.11) (2020-10-05) 8 | 9 | ### [1.2.10](https://github.com/kikobeats/react-clap-button/compare/v1.2.9...v1.2.10) (2020-09-07) 10 | 11 | ### [1.2.9](https://github.com/kikobeats/react-clap-button/compare/v1.2.8...v1.2.9) (2020-05-04) 12 | 13 | ### [1.2.8](https://github.com/kikobeats/react-clap-button/compare/v1.2.7...v1.2.8) (2020-04-07) 14 | 15 | ### [1.2.7](https://github.com/kikobeats/react-clap-button/compare/v1.2.6...v1.2.7) (2020-03-31) 16 | 17 | ### [1.2.6](https://github.com/kikobeats/react-clap-button/compare/v1.2.5...v1.2.6) (2020-03-26) 18 | 19 | ### [1.2.5](https://github.com/kikobeats/react-clap-button/compare/v1.2.4...v1.2.5) (2020-02-12) 20 | 21 | ### [1.2.4](https://github.com/kikobeats/react-clap-button/compare/v1.2.3...v1.2.4) (2020-02-09) 22 | 23 | ### [1.2.3](https://github.com/kikobeats/react-clap-button/compare/v1.2.2...v1.2.3) (2020-02-09) 24 | 25 | ### [1.2.2](https://github.com/kikobeats/react-clap-button/compare/v1.2.1...v1.2.2) (2020-02-09) 26 | 27 | ### [1.2.1](https://github.com/kikobeats/react-clap-button/compare/v1.2.0...v1.2.1) (2020-02-09) 28 | 29 | ## [1.2.0](https://github.com/kikobeats/react-clap-button/compare/v1.1.5...v1.2.0) (2020-02-09) 30 | 31 | 32 | ### Features 33 | 34 | * fix SSR ([deceadd](https://github.com/kikobeats/react-clap-button/commit/deceadd2b7490ab93b023ab4243dc46e27c98127)) 35 | 36 | ### [1.1.5](https://github.com/kikobeats/react-clap-button/compare/v1.1.4...v1.1.5) (2019-10-23) 37 | 38 | ### [1.1.4](https://github.com/kikobeats/react-clap-button/compare/v1.1.3...v1.1.4) (2019-10-23) 39 | 40 | 41 | ### Bug Fixes 42 | 43 | * add postinstall script ([688dcd7](https://github.com/kikobeats/react-clap-button/commit/688dcd744ff7209658c5e2d3483b143a421155c6)), closes [#45](https://github.com/kikobeats/react-clap-button/issues/45) 44 | 45 | ### 1.1.3 (2019-10-11) 46 | 47 | ### 1.1.2 (2019-10-11) 48 | 49 | ### 1.1.1 (2019-10-10) 50 | 51 | ## 1.1.0 (2019-10-10) 52 | 53 | 54 | ### Bug Fixes 55 | 56 | * **package:** update polished to version 2.3.0 ([212adce](https://github.com/kikobeats/react-clap-button/commit/212adce17c2c026cdaaf1a6795f55b2e14961987)) 57 | * **package:** update polished to version 3.0.0 ([3a6348c](https://github.com/kikobeats/react-clap-button/commit/3a6348cab2c7c2f04e98624506b354d02f25ef2e)) 58 | * **package:** update polished to version 3.1.0 ([275c5e6](https://github.com/kikobeats/react-clap-button/commit/275c5e6ce5bc3e6a9b151c10f880e33ef2c59aae)) 59 | * **package:** update react-transition-group to version 3.0.0 ([8759f29](https://github.com/kikobeats/react-clap-button/commit/8759f294282f4c1c1aabe540fa8aa0a806a8b446)) 60 | * **package:** update react-transition-group to version 4.0.0 ([e76d863](https://github.com/kikobeats/react-clap-button/commit/e76d863a08f4036d81e8f0dbb3674a9abae66219)) 61 | * **package:** update styled-components to version 4.1.0 ([dda01e0](https://github.com/kikobeats/react-clap-button/commit/dda01e03887adb55ba9364661f7840d23a8cd44a)) 62 | * **package:** update styled-components to version 4.2.0 ([372d473](https://github.com/kikobeats/react-clap-button/commit/372d4738e35f2604cb18814945965363f57f85eb)) 63 | * **package:** update styled-components to version 4.3.0 ([d5bb878](https://github.com/kikobeats/react-clap-button/commit/d5bb87861a76a7f1582533af64e6eaa7a6a43b9c)) 64 | 65 | ### 1.0.8 (2018-10-18) 66 | 67 | ### 1.0.7 (2018-10-18) 68 | 69 | 70 | ### Bug Fixes 71 | 72 | * **package:** update polished to version 2.0.1 ([228c8ee](https://github.com/kikobeats/react-clap-button/commit/228c8eeb4117230dcd3d6eccff8367f2f9c8ef79)) 73 | * **package:** update polished to version 2.1.0 ([9e649a6](https://github.com/kikobeats/react-clap-button/commit/9e649a62a513894def705912c152c5cb50ecb5ad)) 74 | * **package:** update styled-components to version 3.3.0 ([b4df56f](https://github.com/kikobeats/react-clap-button/commit/b4df56fbd3af5d894d6efbdf3b1b23ce742aa0c0)) 75 | * **package:** update styled-components to version 3.4.0 ([eb0574e](https://github.com/kikobeats/react-clap-button/commit/eb0574e60452435e51717f5ceb73018bb1313440)) 76 | * **package:** update styled-components to version 4.0.0 ([849c0d5](https://github.com/kikobeats/react-clap-button/commit/849c0d5dc466fc320ad11ddd50aef9c571aed1fa)) 77 | 78 | ### 1.0.6 (2018-04-26) 79 | 80 | ### 1.0.5 (2018-03-17) 81 | 82 | ### 1.0.4 (2018-03-07) 83 | 84 | 85 | ### Bug Fixes 86 | 87 | * **package:** update styled-components to version 3.2.0 ([a076193](https://github.com/kikobeats/react-clap-button/commit/a0761936ec55b3bfbaa902f9a202a76a1ffec393)) 88 | 89 | ### 1.0.3 (2018-03-04) 90 | 91 | 92 | ## 1.1.2 (2019-10-11) 93 | 94 | * Using Ref based element selection instead of ids ([c3c1165](https://github.com/kikobeats/react-clap-button/commit/c3c1165)) 95 | 96 | 97 | 98 | 99 | ## 1.1.1 (2019-10-10) 100 | 101 | * Nano bug fix. ([adeb5a9](https://github.com/kikobeats/react-clap-button/commit/adeb5a9)) 102 | 103 | 104 | 105 | 106 | # 1.1.0 (2019-10-10) 107 | 108 | * build: add contributors ([18600cd](https://github.com/kikobeats/react-clap-button/commit/18600cd)) 109 | * build: update dependencies ([c2e4b43](https://github.com/kikobeats/react-clap-button/commit/c2e4b43)) 110 | * callback for count change ([2d1b7f7](https://github.com/kikobeats/react-clap-button/commit/2d1b7f7)) 111 | * Update README.md ([e111441](https://github.com/kikobeats/react-clap-button/commit/e111441)) 112 | * fix(package): update polished to version 2.3.0 ([212adce](https://github.com/kikobeats/react-clap-button/commit/212adce)) 113 | * fix(package): update polished to version 3.0.0 ([3a6348c](https://github.com/kikobeats/react-clap-button/commit/3a6348c)) 114 | * fix(package): update polished to version 3.1.0 ([275c5e6](https://github.com/kikobeats/react-clap-button/commit/275c5e6)) 115 | * fix(package): update react-transition-group to version 3.0.0 ([8759f29](https://github.com/kikobeats/react-clap-button/commit/8759f29)) 116 | * fix(package): update react-transition-group to version 4.0.0 ([e76d863](https://github.com/kikobeats/react-clap-button/commit/e76d863)) 117 | * fix(package): update styled-components to version 4.1.0 ([dda01e0](https://github.com/kikobeats/react-clap-button/commit/dda01e0)) 118 | * fix(package): update styled-components to version 4.2.0 ([372d473](https://github.com/kikobeats/react-clap-button/commit/372d473)) 119 | * fix(package): update styled-components to version 4.3.0 ([d5bb878](https://github.com/kikobeats/react-clap-button/commit/d5bb878)) 120 | 121 | 122 | 123 | 124 | ## 1.0.8 (2018-10-18) 125 | 126 | * Migrate husky ([ad1301a](https://github.com/kikobeats/react-clap-button/commit/ad1301a)) 127 | * Update polished ([61b58cd](https://github.com/kikobeats/react-clap-button/commit/61b58cd)) 128 | 129 | 130 | 131 | 132 | ## 1.0.7 (2018-10-18) 133 | 134 | * fix(package): update polished to version 2.0.1 ([228c8ee](https://github.com/kikobeats/react-clap-button/commit/228c8ee)) 135 | * fix(package): update polished to version 2.1.0 ([9e649a6](https://github.com/kikobeats/react-clap-button/commit/9e649a6)) 136 | * fix(package): update styled-components to version 3.3.0 ([b4df56f](https://github.com/kikobeats/react-clap-button/commit/b4df56f)) 137 | * fix(package): update styled-components to version 3.4.0 ([eb0574e](https://github.com/kikobeats/react-clap-button/commit/eb0574e)) 138 | * fix(package): update styled-components to version 4.0.0 ([849c0d5](https://github.com/kikobeats/react-clap-button/commit/849c0d5)) 139 | * Update package.json ([90c5481](https://github.com/kikobeats/react-clap-button/commit/90c5481)) 140 | 141 | 142 | 143 | 144 | ## 1.0.6 (2018-04-26) 145 | 146 | * Add better browser targets ([512614b](https://github.com/kikobeats/react-clap-button/commit/512614b)) 147 | * Remove final bundle from repository ([debb47f](https://github.com/kikobeats/react-clap-button/commit/debb47f)) 148 | 149 | 150 | 151 | 152 | ## 1.0.5 (2018-03-17) 153 | 154 | * Add `onClickClear` method ([a2acecf](https://github.com/kikobeats/react-clap-button/commit/a2acecf)) 155 | * Add count story ([7d159aa](https://github.com/kikobeats/react-clap-button/commit/7d159aa)) 156 | * Add different timing after click ([9e4741a](https://github.com/kikobeats/react-clap-button/commit/9e4741a)) 157 | * Add missing precommit script ([c0c4136](https://github.com/kikobeats/react-clap-button/commit/c0c4136)) 158 | * Adjust space ([7846a1f](https://github.com/kikobeats/react-clap-button/commit/7846a1f)) 159 | * Adjust timing ([1115c40](https://github.com/kikobeats/react-clap-button/commit/1115c40)) 160 | * Create initial components for “clear results” ([cc19dd1](https://github.com/kikobeats/react-clap-button/commit/cc19dd1)) 161 | * Fix `onClickClear` method ([3610e96](https://github.com/kikobeats/react-clap-button/commit/3610e96)) 162 | * Hide `ClapCountTotal` when total === 0 ([e855c54](https://github.com/kikobeats/react-clap-button/commit/e855c54)) 163 | * Improve animation ([e241bb9](https://github.com/kikobeats/react-clap-button/commit/e241bb9)) 164 | * Improve hover effect ([74ec2e3](https://github.com/kikobeats/react-clap-button/commit/74ec2e3)) 165 | * Modify timing ([67b676e](https://github.com/kikobeats/react-clap-button/commit/67b676e)) 166 | * Remove size-limit ([a8caf29](https://github.com/kikobeats/react-clap-button/commit/a8caf29)) 167 | * Update “color” stories primaryColor ([ef83aaa](https://github.com/kikobeats/react-clap-button/commit/ef83aaa)) 168 | * Update ClearClaps ([585f45e](https://github.com/kikobeats/react-clap-button/commit/585f45e)) 169 | * Update ClearClaps transition ([78d83d9](https://github.com/kikobeats/react-clap-button/commit/78d83d9)) 170 | * Update demo ([ee19daf](https://github.com/kikobeats/react-clap-button/commit/ee19daf)) 171 | 172 | 173 | 174 | 175 | ## 1.0.4 (2018-03-07) 176 | 177 | * Fix storybook custom icon size ([1f83242](https://github.com/kikobeats/react-clap-button/commit/1f83242)) 178 | * Fix storybook ecamples ([543dc61](https://github.com/kikobeats/react-clap-button/commit/543dc61)) 179 | * Fix theme prop ([54bade6](https://github.com/kikobeats/react-clap-button/commit/54bade6)) 180 | * Fixes ([2bd6713](https://github.com/kikobeats/react-clap-button/commit/2bd6713)) 181 | * Misc. formatting updates ([7172e03](https://github.com/kikobeats/react-clap-button/commit/7172e03)) 182 | * Misc. refactor icon ([ce32b0d](https://github.com/kikobeats/react-clap-button/commit/ce32b0d)) 183 | * Refactoring ([e996106](https://github.com/kikobeats/react-clap-button/commit/e996106)) 184 | * Smoother glow animation ([a937f9d](https://github.com/kikobeats/react-clap-button/commit/a937f9d)) 185 | * Update `getTheme` method ([f284a40](https://github.com/kikobeats/react-clap-button/commit/f284a40)) 186 | * Update icon example in storybook to `ThumbsUp` icon ([d00ab66](https://github.com/kikobeats/react-clap-button/commit/d00ab66)) 187 | * fix(package): update styled-components to version 3.2.0 ([a076193](https://github.com/kikobeats/react-clap-button/commit/a076193)) 188 | 189 | 190 | 191 | 192 | ## 1.0.3 (2018-03-04) 193 | 194 | * Adjust border transition ([46cf183](https://github.com/kikobeats/react-clap-button/commit/46cf183)) 195 | * Disable clicks on count numbers ([ee2b9dd](https://github.com/kikobeats/react-clap-button/commit/ee2b9dd)) 196 | * Disable text-highlighting ([a427e08](https://github.com/kikobeats/react-clap-button/commit/a427e08)) 197 | * Update index.js ([bdaaaab](https://github.com/kikobeats/react-clap-button/commit/bdaaaab)) 198 | * Update README ([23f7c8f](https://github.com/kikobeats/react-clap-button/commit/23f7c8f)) 199 | * Update release scripts ([bd18d70](https://github.com/kikobeats/react-clap-button/commit/bd18d70)) 200 | 201 | 202 | 203 | 204 | ## 1.0.2 (2018-02-23) 205 | 206 | * Fix warning ([7730f4a](https://github.com/kikobeats/react-clap-button/commit/7730f4a)) 207 | * Improve storybook ([cb50e81](https://github.com/kikobeats/react-clap-button/commit/cb50e81)) 208 | * Increment count if clicks is under total ([a135f19](https://github.com/kikobeats/react-clap-button/commit/a135f19)), closes [#5](https://github.com/kikobeats/react-clap-button/issues/5) 209 | * Prop destructuring ([6108b09](https://github.com/kikobeats/react-clap-button/commit/6108b09)) 210 | 211 | 212 | 213 | 214 | ## 1.0.1 (2018-02-20) 215 | 216 | * Add `backface-visibility` css property ([d48aa20](https://github.com/kikobeats/react-clap-button/commit/d48aa20)) 217 | * Rename ([e827541](https://github.com/kikobeats/react-clap-button/commit/e827541)) 218 | * Update demo.gif ([de3a62c](https://github.com/kikobeats/react-clap-button/commit/de3a62c)) 219 | * Update README.md ([223e448](https://github.com/kikobeats/react-clap-button/commit/223e448)) 220 | 221 | 222 | 223 | 224 | # 1.0.0 (2018-02-20) 225 | 226 | * First commit ([083709e](https://github.com/kikobeats/react-clap-button/commit/083709e)) 227 | * Lint package ([4e40edc](https://github.com/kikobeats/react-clap-button/commit/4e40edc)) 228 | * Remove ncu ([c255631](https://github.com/kikobeats/react-clap-button/commit/c255631)) 229 | * Remove unnecessary deps ([c48ed02](https://github.com/kikobeats/react-clap-button/commit/c48ed02)) 230 | * Setup build ([f4be01e](https://github.com/kikobeats/react-clap-button/commit/f4be01e)) 231 | * So-rry ([fc33fba](https://github.com/kikobeats/react-clap-button/commit/fc33fba)) 232 | * Update ([a454f12](https://github.com/kikobeats/react-clap-button/commit/a454f12)) 233 | * Update README.md ([180fc13](https://github.com/kikobeats/react-clap-button/commit/180fc13)) 234 | * Update README.md ([3142bc9](https://github.com/kikobeats/react-clap-button/commit/3142bc9)) 235 | * Update README.md ([feca2d3](https://github.com/kikobeats/react-clap-button/commit/feca2d3)) 236 | * Update README.md ([8e9d06b](https://github.com/kikobeats/react-clap-button/commit/8e9d06b)) 237 | * Update README.md ([2c8ba2e](https://github.com/kikobeats/react-clap-button/commit/2c8ba2e)) 238 | * Update README.md ([cb15e90](https://github.com/kikobeats/react-clap-button/commit/cb15e90)) 239 | * Update README.md ([ba12336](https://github.com/kikobeats/react-clap-button/commit/ba12336)) 240 | * Update scripts ([4aefe23](https://github.com/kikobeats/react-clap-button/commit/4aefe23)) 241 | * docs(readme): add Greenkeeper badge ([3c18b9b](https://github.com/kikobeats/react-clap-button/commit/3c18b9b)) 242 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | See demo 5 |
6 |
7 |

8 | 9 | ![Last version](https://img.shields.io/github/tag/Kikobeats/react-clap-button.svg?style=flat-square) 10 | [![Build Status](https://img.shields.io/travis/Kikobeats/react-clap-button/master.svg?style=flat-square)](https://travis-ci.org/Kikobeats/react-clap-button) 11 | [![Dependency status](https://img.shields.io/david/Kikobeats/react-clap-button.svg?style=flat-square)](https://david-dm.org/Kikobeats/react-clap-button) 12 | [![Dev Dependencies Status](https://img.shields.io/david/dev/Kikobeats/react-clap-button.svg?style=flat-square)](https://david-dm.org/Kikobeats/react-clap-button#info=devDependencies) 13 | [![NPM Status](https://img.shields.io/npm/dm/react-clap-button.svg?style=flat-square)](https://www.npmjs.org/package/react-clap-button) 14 | [![Donate](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square)](https://paypal.me/Kikobeats) 15 | 16 | > A Medium like clap button. Based on [Ohans Emmanuel](https://codepen.io/ohansemmanuel/full/zEJpYy/) CodePen, icon by [Luis Durazo](https://thenounproject.com/luisdurazo/). 17 | 18 | ## Install 19 | 20 | ```bash 21 | $ npm install react-clap-button --save 22 | ``` 23 | 24 | ## Usage 25 | 26 | ``` 27 | import React from 'react'; 28 | import ClapButton from 'react-clap-button'; 29 | 30 | export default () => { 31 | 32 | const onCountChange = ({ count, countTotal }) => { 33 | 34 | }; 35 | // All Props are Optional 36 | return ( 37 | } 44 | /> 45 | ); 46 | } 47 | ``` 48 | 49 | ## Related 50 | 51 | - [vue-clap-button](https://github.com/AJLoveChina/vue-clap-button) – A Medium like clap button written on Vue.js 52 | 53 | ## License 54 | 55 | **react-clap-button** © [Kiko Beats](https://kikobeats.com), released under the [MIT](https://github.com/Kikobeats/react-clap-button/blob/master/LICENSE.md) License.
56 | Authored and maintained by Kiko Beats with help from [contributors](https://github.com/Kikobeats/react-clap-button/contributors). 57 | 58 | > [kikobeats.com](https://kikobeats.com) · GitHub [@Kiko Beats](https://github.com/Kikobeats) · Twitter [@Kikobeats](https://twitter.com/Kikobeats) 59 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kikobeats/react-clap-button/52c6a48006f38eae15c500570e98a3d064a6a6a0/demo.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-clap-button", 3 | "description": "A Medium like clap button", 4 | "homepage": "https://github.com/kikobeats/react-clap-button", 5 | "version": "1.2.12", 6 | "main": "dist/react-clap-button.js", 7 | "module": "dist/react-clap-button.m.js", 8 | "author": { 9 | "email": "josefrancisco.verdu@gmail.com", 10 | "name": "Kiko Beats", 11 | "url": "https://github.com/Kikobeats" 12 | }, 13 | "contributors": [ 14 | { 15 | "name": "santosh898", 16 | "email": "santoshkottakota007@gmail.com" 17 | }, 18 | { 19 | "name": "Prashanth Reddy", 20 | "email": "prashanth726@gmail.com" 21 | }, 22 | { 23 | "name": "Thomas Maximini", 24 | "email": "tmaximini@gmail.com" 25 | } 26 | ], 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/kikobeats/react-clap-button.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/kikobeats/react-clap-button/issues" 33 | }, 34 | "keywords": [ 35 | "button", 36 | "clap", 37 | "medium", 38 | "reaction" 39 | ], 40 | "dependencies": { 41 | "mo-js": "~0.288.2", 42 | "polished": "~4.2.1", 43 | "react-transition-group": "^4.0.0", 44 | "styled-components": "~5.3.0" 45 | }, 46 | "devDependencies": { 47 | "@babel/plugin-proposal-class-properties": "latest", 48 | "@babel/plugin-proposal-object-rest-spread": "latest", 49 | "@babel/plugin-transform-react-constant-elements": "latest", 50 | "@babel/preset-env": "latest", 51 | "@babel/preset-react": "latest", 52 | "@commitlint/cli": "latest", 53 | "@commitlint/config-conventional": "latest", 54 | "@storybook/addon-centered": "latest", 55 | "@storybook/addon-options": "latest", 56 | "@storybook/addon-viewport": "latest", 57 | "@storybook/react": "latest", 58 | "babel-eslint": "latest", 59 | "babel-loader": "latest", 60 | "babel-plugin-transform-react-pure-class-to-function": "latest", 61 | "babel-plugin-transform-react-remove-prop-types": "latest", 62 | "ci-publish": "latest", 63 | "conventional-github-releaser": "latest", 64 | "eslint-plugin-jsx-a11y": "latest", 65 | "finepack": "latest", 66 | "git-authors-cli": "latest", 67 | "husky": "latest", 68 | "lint-staged": "latest", 69 | "npm-check-updates": "latest", 70 | "prettier-standard": "latest", 71 | "prop-types": "latest", 72 | "react": "^18", 73 | "react-dom": "^18", 74 | "react-feather": "latest", 75 | "rollup": "latest", 76 | "rollup-plugin-babel": "latest", 77 | "rollup-plugin-filesize": "latest", 78 | "rollup-plugin-peer-deps-external": "latest", 79 | "rollup-plugin-terser": "latest", 80 | "rollup-plugin-visualizer": "latest", 81 | "standard": "latest", 82 | "standard-markdown": "latest", 83 | "standard-version": "latest", 84 | "storybook-addon-jsx": "latest" 85 | }, 86 | "engines": { 87 | "node": ">= 8" 88 | }, 89 | "files": [ 90 | "dist" 91 | ], 92 | "scripts": { 93 | "build": "NODE_ENV=production rollup -c rollup.config.js", 94 | "build-storybook": "NODE_ENV=production build-storybook", 95 | "contributors": "(git-authors-cli && finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true", 96 | "dev": "start-storybook -p 6006", 97 | "lint": "standard-markdown README.md && standard src stories", 98 | "postrelease": "npm run release:tags && npm run release:github && ci-publish", 99 | "prepublishOnly": "npm run build", 100 | "prerelease": "npm run update:check && npm run contributors", 101 | "pretest": "npm run lint", 102 | "release": "standard-version -a", 103 | "release:github": "conventional-github-releaser -p angular", 104 | "release:tags": "git push --follow-tags origin HEAD:master", 105 | "test": "exit 0", 106 | "update": "ncu -u", 107 | "update:check": "ncu -- --error-level 2" 108 | }, 109 | "license": "MIT", 110 | "commitlint": { 111 | "extends": [ 112 | "@commitlint/config-conventional" 113 | ] 114 | }, 115 | "husky": { 116 | "hooks": { 117 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 118 | "pre-commit": "lint-staged" 119 | } 120 | }, 121 | "lint-staged": { 122 | "*.js": [ 123 | "prettier-standard" 124 | ], 125 | "package.json": [ 126 | "finepack" 127 | ] 128 | }, 129 | "peerDependencies": { 130 | "react": ">= 16 < 17" 131 | }, 132 | "standard": { 133 | "globals": [ 134 | "React", 135 | "fetch" 136 | ], 137 | "ignore": [ 138 | "/dist/" 139 | ], 140 | "parser": "babel-eslint", 141 | "plugins": [ 142 | "jsx-a11y" 143 | ] 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import external from 'rollup-plugin-peer-deps-external' 2 | import visualizer from 'rollup-plugin-visualizer' 3 | import { terser } from 'rollup-plugin-terser' 4 | import filesize from 'rollup-plugin-filesize' 5 | import babel from 'rollup-plugin-babel' 6 | import fs from 'fs' 7 | 8 | import pkg from './package.json' 9 | 10 | const babelRc = JSON.parse(fs.readFileSync('./.babelrc')) 11 | 12 | export default { 13 | input: 'src/index.js', 14 | output: [ 15 | { 16 | file: pkg.main, 17 | format: 'cjs', 18 | sourcemap: true 19 | }, 20 | { 21 | file: pkg.module, 22 | format: 'es', 23 | sourcemap: true 24 | } 25 | ], 26 | plugins: [ 27 | external({ 28 | includeDependencies: true 29 | }), 30 | babel({ 31 | babelrc: false, 32 | externalHelpers: false, 33 | ...babelRc 34 | }), 35 | terser(), 36 | filesize(), 37 | visualizer({ template: 'treemap' }) 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /src/components/ClapButton.js: -------------------------------------------------------------------------------- 1 | import styled, {css, keyframes} from 'styled-components' 2 | 3 | const shockwave = ({theme: {secondaryColor}}) => keyframes` 4 | 0%{ 5 | box-shadow:0 0 6 | } 7 | 70%{ 8 | box-shadow:0 0 5px 10px rgba(255,255,255,0) 9 | } 10 | 100%{ 11 | box-shadow:0 0 0 0 rgba(255,255,255,0) 12 | } 13 | ` 14 | 15 | const ClapButton = styled.button` 16 | position: relative; 17 | outline: 1px solid transparent; 18 | border-radius: 50%; 19 | background: #fff; 20 | transition: border 0.1s ease-in; 21 | 22 | &::after { 23 | content: ''; 24 | position: absolute; 25 | top: 0; 26 | left: 0; 27 | display: block; 28 | border-radius: 50%; 29 | } 30 | 31 | &:hover { 32 | cursor: pointer; 33 | } 34 | 35 | ${props => props.isHover && css` 36 | &::after { 37 | animation: ${shockwave} 2s infinite; 38 | } 39 | `} 40 | 41 | ${({theme: {primaryColor, secondaryColor, size}}) => css` 42 | width: ${size}px; 43 | height: ${size}px; 44 | border: 1px solid ${primaryColor}; 45 | 46 | &::after { 47 | width: ${size - 1}px; 48 | height: ${size - 1}px; 49 | border-color: ${secondaryColor}; 50 | color: ${secondaryColor}; 51 | fill: ${secondaryColor}; 52 | } 53 | 54 | &:hover, &:focus { 55 | border: 1px solid ${secondaryColor}; 56 | } 57 | `} 58 | ` 59 | 60 | export default ClapButton 61 | -------------------------------------------------------------------------------- /src/components/ClapCount.js: -------------------------------------------------------------------------------- 1 | import styled, {css} from 'styled-components' 2 | import {textStyles} from '../utils' 3 | 4 | const ClapCount = styled.span` 5 | top: -${({size}) => size / 1.6}px; 6 | left: ${({size}) => size / 4}px; 7 | color: white; 8 | border-radius: 50%; 9 | backface-visibility: hidden; 10 | ${textStyles} 11 | 12 | ${({theme: {secondaryColor, size}}) => { 13 | const half = `${size / 2}px` 14 | return css` 15 | height: ${half}; 16 | width: ${half}; 17 | line-height: ${half}; 18 | top: -${size / 2}px; 19 | left: ${size / 4}px; 20 | background: ${secondaryColor}; 21 | ` 22 | }} 23 | ` 24 | 25 | export default ClapCount 26 | -------------------------------------------------------------------------------- /src/components/ClapCountTotal.js: -------------------------------------------------------------------------------- 1 | import styled, {css} from 'styled-components' 2 | import {textStyles} from '../utils' 3 | 4 | const ClapCountTotal = styled.span` 5 | transform: scale(1); 6 | text-align: center; 7 | left: 0; 8 | ${textStyles} 9 | 10 | ${({theme: {primaryColor, size}}) => css` 11 | top: -${size / 3}px; 12 | color: ${primaryColor}; 13 | width: ${size}px; 14 | `} 15 | ` 16 | 17 | export default ClapCountTotal 18 | -------------------------------------------------------------------------------- /src/components/ClapIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, {css} from 'styled-components' 3 | 4 | const Icon = styled.svg` 5 | width: 33px; 6 | 7 | ${({isClicked, theme: {secondaryColor}}) => css` 8 | fill: ${!isClicked ? 'none' : secondaryColor}; 9 | stroke: ${!isClicked ? secondaryColor : 'white'}; 10 | stroke-width: ${!isClicked ? '2px' : '1px'}; 11 | `}; 12 | ` 13 | 14 | export default props => ( 15 | 20 | 23 | 26 | 27 | ) 28 | -------------------------------------------------------------------------------- /src/components/ClapWrap.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import ClearClaps from './ClearClaps' 4 | 5 | const Wrap = styled.div` 6 | display: inline-block; 7 | position: relative; 8 | z-index: 1; 9 | ` 10 | 11 | const ClapWrapChildren = styled.div` 12 | position: relative; 13 | z-index: 2; 14 | ` 15 | 16 | export default class ClapWrap extends React.Component { 17 | state = { 18 | displayClear: false 19 | } 20 | 21 | onClick = () => { 22 | if (this.state.displayClear) { 23 | this.setState({displayClear: false}) 24 | } 25 | } 26 | 27 | mouseMove = () => { 28 | if (!this.state.displayClear) { 29 | this.setState({displayClear: true}) 30 | } 31 | } 32 | 33 | mouseLeave = () => { 34 | if (this.state.displayClear) { 35 | this.setState({displayClear: false}) 36 | } 37 | } 38 | 39 | render() { 40 | const {children, isClicked, onClickClear} = this.props 41 | const {displayClear} = this.state 42 | return ( 43 | { 46 | this.state.isClicked = true 47 | setTimeout(() => { 48 | this.state.isClicked = false 49 | }, 100) 50 | }}> 51 | 52 | {children} 53 | 54 | 59 | 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/components/ClearClaps.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Transition} from 'react-transition-group' 3 | import styled, {css} from 'styled-components' 4 | import {rgba, darken} from 'polished' 5 | 6 | const ClearClaps = styled.button` 7 | border: 0; 8 | padding: 0; 9 | appearance: none; 10 | position: absolute; 11 | right: 0; 12 | top: calc(50% - 15px); 13 | transform: translateX(0); 14 | height: 30px; 15 | width: 36px; 16 | border-radius: 0 9999px 9999px 0; 17 | backface-visibility: hidden; 18 | cursor: pointer; 19 | outline: none; 20 | transition: transform .3s cubic-bezier(.25,0,.6,1.4) 1s,-webkit-transform .3s cubic-bezier(.25,0,.6,1.4) 1s; 21 | 22 | &::before, 23 | &::after { 24 | content: ''; 25 | position: absolute; 26 | right: calc(50% - 3.5px); 27 | top: calc(50% - 8px); 28 | height: 16px; 29 | width: 2px; 30 | border-radius: 9999px; 31 | transition: background 0.3s ease-in-out; 32 | } 33 | 34 | &::before { 35 | transform: rotate(45deg); 36 | } 37 | 38 | &::after { 39 | transform: rotate(-45deg); 40 | } 41 | 42 | ${({theme: {primaryColor, secondaryColor}}) => css` 43 | background: ${rgba(secondaryColor, 0.1)}; 44 | 45 | &::before, 46 | &::after { 47 | background: ${secondaryColor}; 48 | } 49 | 50 | &:hover { 51 | &::before, 52 | &::after { 53 | background: ${darken(0.1, secondaryColor)}; 54 | } 55 | } 56 | `} 57 | ` 58 | 59 | const transitionStyles = { 60 | entered: () => ({ 61 | transform: 'translateX(calc(100% - 5px))', 62 | transitionDelay: '0s' 63 | }), 64 | exiting: ({isClicked}) => ({ 65 | transitionDuration: isClicked ? '0.25s' : '0.3s', 66 | transitionDelay: isClicked ? '0s' : '0.7s' 67 | }) 68 | } 69 | 70 | export default ({in: inProp, ...props}) => { 71 | return ( 72 | 73 | {state => } 74 | 75 | ) 76 | } 77 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ThemeProvider } from 'styled-components' 3 | 4 | import ClapWrap from './components/ClapWrap' 5 | import ClapIcon from './components/ClapIcon' 6 | import ClapButton from './components/ClapButton' 7 | import ClapCount from './components/ClapCount' 8 | import ClapCountTotal from './components/ClapCountTotal' 9 | 10 | const defaultTheme = { 11 | primaryColor: 'rgb(189, 195, 199)', 12 | secondaryColor: 'rgb(39, 174, 96)', 13 | size: 70 14 | } 15 | 16 | const Clap = class extends React.Component { 17 | constructor (props) { 18 | super(props) 19 | this.state = { 20 | unclicked: true, 21 | count: this.props.count, 22 | countTotal: this.props.countTotal, 23 | isClicked: props.count > 0, 24 | isHover: false 25 | } 26 | this.onClick = this.onClick.bind(this) 27 | this.onClickClear = this.onClickClear.bind(this) 28 | this.clapButtonRef = React.createRef() 29 | this.clapIconRef = React.createRef() 30 | this.clapCountRef = React.createRef() 31 | this.clapCountTotalRef = React.createRef() 32 | } 33 | 34 | componentDidMount () { 35 | // require mo-js on mount so it does not break SSR 36 | const mojs = require('mo-js') 37 | 38 | const tlDuration = 300 39 | const triangleBurst = new mojs.Burst({ 40 | parent: this.clapButtonRef.current, 41 | radius: { 50: 95 }, 42 | count: 5, 43 | angle: 30, 44 | children: { 45 | shape: 'polygon', 46 | radius: { 6: 0 }, 47 | scale: 1, 48 | stroke: 'rgba(211,84,0 ,0.5)', 49 | strokeWidth: 2, 50 | angle: 210, 51 | delay: 30, 52 | speed: 0.2, 53 | easing: mojs.easing.bezier(0.1, 1, 0.3, 1), 54 | duration: tlDuration 55 | } 56 | }) 57 | 58 | const circleBurst = new mojs.Burst({ 59 | parent: this.clapButtonRef.current, 60 | radius: { 50: 75 }, 61 | angle: 25, 62 | duration: tlDuration, 63 | children: { 64 | shape: 'circle', 65 | fill: 'rgba(149,165,166 ,0.5)', 66 | delay: 30, 67 | speed: 0.2, 68 | radius: { 3: 0 }, 69 | easing: mojs.easing.bezier(0.1, 1, 0.3, 1) 70 | } 71 | }) 72 | 73 | const countAnimation = new mojs.Html({ 74 | el: this.clapCountRef.current, 75 | isShowStart: false, 76 | isShowEnd: true, 77 | y: { 0: -30 }, 78 | opacity: { 0: 1 }, 79 | duration: tlDuration 80 | }).then({ 81 | opacity: { 1: 0 }, 82 | y: -80, 83 | delay: tlDuration / 2 84 | }) 85 | 86 | const opacityStart = this.props.count > 0 && this.state.unclicked ? 1 : 0 87 | 88 | const countTotalAnimation = new mojs.Html({ 89 | el: this.clapCountTotalRef.current, 90 | isShowStart: false, 91 | isShowEnd: true, 92 | opacity: { [opacityStart]: 1 }, 93 | delay: (3 * tlDuration) / 2, 94 | duration: tlDuration, 95 | y: { 0: -3 } 96 | }) 97 | 98 | const scaleButton = new mojs.Html({ 99 | el: this.clapButtonRef.current, 100 | duration: tlDuration, 101 | scale: { 1.3: 1 }, 102 | easing: mojs.easing.out 103 | }) 104 | 105 | const clap = this.clapButtonRef.current 106 | clap.style.transform = 'scale(1, 1)' 107 | this.animationTimeline = new mojs.Timeline() 108 | this.animationTimeline.add([ 109 | countAnimation, 110 | countTotalAnimation, 111 | scaleButton, 112 | circleBurst, 113 | triangleBurst 114 | ]) 115 | } 116 | 117 | getTheme () { 118 | const { theme = {} } = this.props 119 | return Object.assign({}, defaultTheme, theme) 120 | } 121 | 122 | onClick () { 123 | const { maxCount, onCountChange } = this.props 124 | this.animationTimeline.replay() 125 | 126 | this.setState(({ count, countTotal }) => { 127 | if (count < maxCount) { 128 | onCountChange({ count: count + 1, countTotal: countTotal + 1 }) 129 | return { 130 | unclicked: false, 131 | count: count + 1, 132 | countTotal: countTotal + 1, 133 | isClicked: true 134 | } 135 | } 136 | }) 137 | } 138 | 139 | onClickClear () { 140 | const { onCountChange } = this.props 141 | this.setState(({ count, countTotal }) => { 142 | onCountChange({ count: 0, countTotal: countTotal - count }) 143 | return { 144 | isClicked: false, 145 | countTotal: countTotal - count, 146 | count: 0 147 | } 148 | }) 149 | } 150 | 151 | render () { 152 | const { count, countTotal, isClicked, isHover } = this.state 153 | const { iconComponent: ClapIcon } = this.props 154 | 155 | return ( 156 | 157 | 158 | this.setState({ isHover: true })} 163 | onMouseLeave={e => this.setState({ isHover: false })} 164 | isHover={isHover && count === 0} 165 | > 166 | 167 | 168 | +{count} 169 | 170 | 171 | {Number(countTotal).toLocaleString()} 172 | 173 | 174 | 175 | 176 | ) 177 | } 178 | } 179 | 180 | Clap.defaultProps = { 181 | countTotal: 0, 182 | count: 0, 183 | maxCount: 50, 184 | isClicked: false, 185 | onCountChange: () => {}, 186 | iconComponent: ClapIcon 187 | } 188 | 189 | export default Clap 190 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import {css} from 'styled-components' 2 | 3 | export const textStyles = css` 4 | font-size: 0.8rem; 5 | user-select: none; 6 | pointer-events: none; 7 | position: absolute; 8 | ` 9 | -------------------------------------------------------------------------------- /stories/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, {css} from 'styled-components' 3 | import {storiesOf} from '@storybook/react' 4 | import {ThumbsUp} from 'react-feather' 5 | 6 | import centered from '@storybook/addon-centered' 7 | 8 | import ClapButton from '../src' 9 | 10 | const CustomIcon = styled(ThumbsUp)` 11 | stroke: ${({ theme: { secondaryColor } }) => secondaryColor}; 12 | stroke-width: 1px; 13 | fill: none; 14 | ${({isClicked, theme: {secondaryColor}}) => isClicked && css` 15 | fill: ${secondaryColor}; 16 | stroke: white; 17 | `}; 18 | ` 19 | 20 | storiesOf('ClapButton', module) 21 | .addDecorator(centered) 22 | .addWithJSX('default', () => ) 23 | .addWithJSX('maxCount', () => ) 24 | .addWithJSX('count', () => ) 25 | .addWithJSX('icon', () => ( 26 | } 28 | /> 29 | )) 30 | .addWithJSX('color', () => ( 31 | 34 | )) 35 | --------------------------------------------------------------------------------