├── .circleci └── config.yml ├── .gitignore ├── .storybook ├── config.js └── webpack.config.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── jest.config.js ├── package.json ├── prettier.config.js ├── src ├── __tests__ │ ├── setup.js │ └── test.spec.ts ├── components │ ├── Player │ │ ├── __story__ │ │ │ └── story.tsx │ │ ├── createColorManager.ts │ │ ├── formatTime.ts │ │ └── index.tsx │ ├── Rail │ │ ├── __story__ │ │ │ └── story.tsx │ │ └── index.tsx │ ├── RailWrap │ │ └── index.tsx │ └── Volume │ │ └── index.tsx ├── icons │ ├── Muted │ │ ├── __story__ │ │ │ └── story.tsx │ │ └── index.tsx │ ├── Pause │ │ ├── __story__ │ │ │ └── story.tsx │ │ └── index.tsx │ ├── Play │ │ ├── __story__ │ │ │ └── story.tsx │ │ └── index.tsx │ └── Volume │ │ ├── __story__ │ │ └── story.tsx │ │ └── index.tsx └── index.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | refs: 4 | container: &container 5 | docker: 6 | - image: node:10.20 7 | working_directory: ~/repo 8 | steps: 9 | - &Versions 10 | run: 11 | name: Versions 12 | command: node -v && npm -v && yarn -v 13 | - &Install 14 | run: 15 | name: Install Dependencies 16 | command: yarn install --pure-lockfile 17 | - &Build 18 | run: 19 | name: Build 20 | command: yarn build 21 | - &Test 22 | run: 23 | name: Test 24 | command: yarn test 25 | - &Post_to_dev_null 26 | run: 27 | name: 'Post to Slack #dev-null' 28 | command: npx ci-scripts slack --channel="dev-null" 29 | 30 | jobs: 31 | all: 32 | <<: *container 33 | steps: 34 | - checkout 35 | - *Versions 36 | - *Install 37 | - *Build 38 | - *Test 39 | - *Post_to_dev_null 40 | 41 | master: 42 | <<: *container 43 | steps: 44 | - checkout 45 | - *Versions 46 | - *Install 47 | - *Build 48 | - *Test 49 | - *Post_to_dev_null 50 | - run: 51 | name: Release 52 | command: yarn release 53 | - *Post_to_dev_null 54 | 55 | nightly: 56 | <<: *container 57 | steps: 58 | - checkout 59 | - *Versions 60 | - *Install 61 | - *Build 62 | - *Test 63 | - *Post_to_dev_null 64 | - run: 65 | name: Post to Slack on FAILURE 66 | command: npx ci slack --channel="dev" --text="** nightly build failed :scream:" --icon_emoji=tired_face 67 | when: on_fail 68 | 69 | workflows: 70 | version: 2 71 | all: 72 | jobs: 73 | - all: 74 | context: common-env-vars 75 | filters: 76 | branches: 77 | ignore: 78 | - master 79 | - gh-pages 80 | master: 81 | jobs: 82 | - master: 83 | context: common-env-vars 84 | filters: 85 | branches: 86 | only: master 87 | nightly: 88 | triggers: 89 | - schedule: 90 | cron: '0 1 * * *' 91 | filters: 92 | branches: 93 | only: master 94 | jobs: 95 | - nightly: 96 | context: common-env-vars 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and not Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | 107 | # Stores VSCode versions used for testing VSCode extensions 108 | .vscode-test 109 | 110 | # yarn v2 111 | 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .pnp.* 116 | 117 | # Build folder 118 | lib/ 119 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {configure} from '@storybook/react'; 3 | 4 | const req = require.context('../src/', true, /.*(stories|story)\.(js|jsx|ts|tsx)?$/); 5 | 6 | const loadStories = () => { 7 | const nonMd = []; 8 | const md = []; 9 | 10 | for (const filename of req.keys()) { 11 | if (filename.indexOf('/markdown/') > -1) { 12 | md.push(filename); 13 | } else { 14 | nonMd.push(filename); 15 | } 16 | } 17 | 18 | nonMd.forEach((filename) => req(filename)); 19 | md.forEach((filename) => req(filename)); 20 | }; 21 | 22 | configure(loadStories, module); 23 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const {compilerOptions} = require('../tsconfig.json'); 3 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 4 | 5 | const SRC_PATH = path.join(__dirname, '../src'); 6 | 7 | module.exports = { 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.tsx?$/, 12 | loader: 'ts-loader', 13 | include: [SRC_PATH], 14 | options: { 15 | transpileOnly: true, // use transpileOnly mode to speed-up compilation 16 | compilerOptions: { 17 | ...compilerOptions, 18 | declaration: false, 19 | }, 20 | }, 21 | } 22 | ], 23 | }, 24 | resolve: { 25 | extensions: ['.ts', '.tsx', '.js', '.jsx', '.gif', '.jpg', '.png'], 26 | enforceExtension: false, 27 | }, 28 | plugins: [new ForkTsCheckerWebpackPlugin()], 29 | }; 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [1.1.0](https://github.com/streamich/react-simple-player/compare/v1.0.3...v1.1.0) (2022-04-19) 2 | 3 | 4 | ### Features 5 | 6 | * 🎸 add hover effects to buttons ([d3f143f](https://github.com/streamich/react-simple-player/commit/d3f143fd625e4b972f9243dd8751f53bc5681a33)) 7 | * 🎸 add hover effects to rails and change cursor style ([96a3c20](https://github.com/streamich/react-simple-player/commit/96a3c208b8e86cf8af1c8af67b3fb1e342999205)) 8 | 9 | ## [1.0.3](https://github.com/streamich/react-simple-player/compare/v1.0.2...v1.0.3) (2020-04-25) 10 | 11 | 12 | ### Bug Fixes 13 | 14 | * 🐛 stretch full width ([82d4b71](https://github.com/streamich/react-simple-player/commit/82d4b710788989140620af242b7114fbcc23df30)) 15 | 16 | ## [1.0.2](https://github.com/streamich/react-simple-player/compare/v1.0.1...v1.0.2) (2020-04-25) 17 | 18 | 19 | ### Bug Fixes 20 | 21 | * 🐛 move React to peer dependencies ([a0f0501](https://github.com/streamich/react-simple-player/commit/a0f050151e16a2d9424e2725a342bb11fa1792e2)) 22 | 23 | ## [1.0.1](https://github.com/streamich/react-simple-player/compare/v1.0.0...v1.0.1) (2020-04-24) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * 🐛 add index.js ([e3c0262](https://github.com/streamich/react-simple-player/commit/e3c02625adbd37885dd49387b06d4739ca701705)) 29 | 30 | # 1.0.0 (2020-04-24) 31 | 32 | 33 | ### Features 34 | 35 | * 🎸 add and icons ([1297a78](https://github.com/streamich/react-simple-player/commit/1297a783946c9f0810f747a7173c305b659598a0)) 36 | * 🎸 add icon ([a181bf0](https://github.com/streamich/react-simple-player/commit/a181bf056b07dd8e476007c12c7309406cb0be0d)) 37 | * 🎸 add component ([5b9201f](https://github.com/streamich/react-simple-player/commit/5b9201fe12bb304f5cea89d0d71061486faf65df)) 38 | * 🎸 add Storybook demo ([d097320](https://github.com/streamich/react-simple-player/commit/d097320cf4bfba6da51194319a69d24d631a2f07)) 39 | * 🎸 add componentn ([09bb6de](https://github.com/streamich/react-simple-player/commit/09bb6de919e204a98b3d5e2c7ceda193cb2640e5)) 40 | * 🎸 add ability to set [height] ([ffdfa22](https://github.com/streamich/react-simple-player/commit/ffdfa22f66838de9174f7989a8680b1ee77c51d1)) 41 | * 🎸 add mute/unmute button to ([d152f0f](https://github.com/streamich/react-simple-player/commit/d152f0ff42853928f396a94079d952ef0281ad97)) 42 | * 🎸 add progress rail to player ([cc1d724](https://github.com/streamich/react-simple-player/commit/cc1d7240b9f2423112ff25a11b08366416c34e86)) 43 | * 🎸 add timer to player ([234a0b5](https://github.com/streamich/react-simple-player/commit/234a0b5dd16cb49bade82fa50afacbb4f3cfb278)) 44 | * 🎸 add tooltip with time ([433ec14](https://github.com/streamich/react-simple-player/commit/433ec144a559e269a9d04064433b348c062631f9)) 45 | * 🎸 display visually seeking ([238d864](https://github.com/streamich/react-simple-player/commit/238d8646a08fcd1c5cfc07c805cafd94868291a5)) 46 | * 🎸 improve control ([b1d5c36](https://github.com/streamich/react-simple-player/commit/b1d5c368b448a094f151e08f7503e92949f87f92)) 47 | * 🎸 improve color handling ([ceb4bfd](https://github.com/streamich/react-simple-player/commit/ceb4bfd5f5bc73a928fa7adc3a895d56980246de)) 48 | * 🎸 improve colors, add dark mode ([d8097a1](https://github.com/streamich/react-simple-player/commit/d8097a17e054bb7e59efb37d891aff3dcd13115a)) 49 | * 🎸 integrate button ([135c2c9](https://github.com/streamich/react-simple-player/commit/135c2c978e2c0d10929bb83516490ab65d2480b1)) 50 | * 🎸 introduce colors ([107aa7a](https://github.com/streamich/react-simple-player/commit/107aa7a44dbdd1f9de09c7486db0b3ee25ca2d37)) 51 | * 🎸 render buffered ranges ([544353c](https://github.com/streamich/react-simple-player/commit/544353cb66c2f7302754c755f67b0956858c4164)) 52 | * 🎸 show progress bar grey when paused ([7057790](https://github.com/streamich/react-simple-player/commit/7057790fc6f87bb46c5ebd6d6882f16e31565a84)) 53 | 54 | # 1.0.0 (2020-04-24) 55 | 56 | 57 | ### Features 58 | 59 | * 🎸 add Storybook and improve project ([8ec3b7e](https://github.com/streamich/template-react-library/commit/8ec3b7e798104c01924c9a158b515f9a10308888)) 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-simple-player 2 | 3 | 4 | [![](https://user-images.githubusercontent.com/9773803/164186454-340639d7-52c7-4f7e-a45d-9eb9adc80fc9.gif)](https://streamich.github.io/react-simple-player) 5 | 6 | ```ts 7 | import {Player} from 'react-simple-player'; 8 | 9 | 10 | ``` 11 | 12 | 13 | 14 | ## License 15 | 16 | [Unlicense](LICENSE) — public domain. 17 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'subject-case': [1, 'always', 'lower-case'], 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | testURL: 'http://localhost/', 4 | setupFiles: ['/src/__tests__/setup.js'], 5 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 6 | transformIgnorePatterns: [], 7 | testRegex: '.*/__tests__/.*\.(test|spec)\.(jsx?|tsx?)$', 8 | }; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-simple-player", 3 | "version": "1.1.0", 4 | "description": "", 5 | "author": { 6 | "name": "streamich", 7 | "url": "https://github.com/streamich" 8 | }, 9 | "homepage": "https://github.com/streamich/react-simple-player", 10 | "repository": "streamich/react-simple-player", 11 | "license": "Unlicense", 12 | "main": "lib/index.js", 13 | "types": "lib/index.d.ts", 14 | "typings": "lib/index.d.ts", 15 | "sideEffects": false, 16 | "files": [ 17 | "lib/" 18 | ], 19 | "scripts": { 20 | "clean": "rimraf public/dist && yarn storybook:clean", 21 | "build": "tsc", 22 | "test": "jest --no-cache --config='jest.config.js'", 23 | "start": "yarn storybook", 24 | "prettier": "prettier --ignore-path .gitignore --write 'src/**/*.{ts,tsx,js,jsx}'", 25 | "prettier:diff": "prettier -l 'src/**/*.{ts,tsx,js,jsx}'", 26 | "prepush": "yarn prettier:diff", 27 | "precommit": "pretty-quick --staged && yarn tslint", 28 | "tslint": "tslint 'src/**/*.{js,jsx,ts,tsx}' -t verbose", 29 | "commitmsg": "commitlint -E GIT_PARAMS", 30 | "release": "semantic-release", 31 | "storybook": "start-storybook -p 6010", 32 | "storybook:build": "build-storybook", 33 | "storybook:deploy": "gh-pages -d storybook-static", 34 | "storybook:clean": "rimraf storybook-static" 35 | }, 36 | "keywords": [], 37 | "dependencies": { 38 | "p4-css": "*", 39 | "react-use": "*" 40 | }, 41 | "peerDependencies": { 42 | "react": "*", 43 | "react-dom": "*" 44 | }, 45 | "devDependencies": { 46 | "@babel/core": "^7.9.0", 47 | "@commitlint/cli": "^8.3.5", 48 | "@commitlint/config-conventional": "^8.3.4", 49 | "@semantic-release/changelog": "^5.0.1", 50 | "@semantic-release/git": "^9.0.0", 51 | "@semantic-release/npm": "^7.0.5", 52 | "@storybook/react": "^5.3.18", 53 | "@types/jest": "^25.2.1", 54 | "@types/react": "^16.9.34", 55 | "@types/react-dom": "^16.9.6", 56 | "babel-loader": "^8.1.0", 57 | "fork-ts-checker-webpack-plugin": "^4.1.3", 58 | "gh-pages": "^3.2.3", 59 | "husky": "^4.2.5", 60 | "jest": "^25.4.0", 61 | "prettier": "^2.0.5", 62 | "pretty-quick": "^2.0.1", 63 | "react": "^16.13.1", 64 | "react-dom": "^16.13.1", 65 | "rimraf": "^3.0.2", 66 | "semantic-release": "^17.0.7", 67 | "storybook-readme": "^5.0.8", 68 | "ts-loader": "^7.0.1", 69 | "ts-node": "^8.9.0", 70 | "tslint": "^6.1.1", 71 | "tslint-config-common": "^1.6.0", 72 | "typescript": "^3.8.3" 73 | }, 74 | "release": { 75 | "verifyConditions": [ 76 | "@semantic-release/changelog", 77 | "@semantic-release/npm", 78 | "@semantic-release/git" 79 | ], 80 | "prepare": [ 81 | "@semantic-release/changelog", 82 | "@semantic-release/npm", 83 | "@semantic-release/git" 84 | ] 85 | }, 86 | "config": { 87 | "commitizen": { 88 | "path": "git-cz" 89 | } 90 | }, 91 | "husky": { 92 | "hooks": { 93 | "pre-commit": "yarn tslint", 94 | "pre-push": "yarn tslint && yarn clean && yarn build && yarn test" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'always', 3 | printWidth: 120, 4 | tabWidth: 2, 5 | useTabs: false, 6 | semi: true, 7 | singleQuote: true, 8 | trailingComma: 'all', 9 | bracketSpacing: false, 10 | jsxBracketSameLine: false, 11 | }; 12 | -------------------------------------------------------------------------------- /src/__tests__/setup.js: -------------------------------------------------------------------------------- 1 | // Jest setup. 2 | process.env.JEST = true; 3 | -------------------------------------------------------------------------------- /src/__tests__/test.spec.ts: -------------------------------------------------------------------------------- 1 | test('Jest is set up', () => {}); 2 | -------------------------------------------------------------------------------- /src/components/Player/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Player, PlayerState} from '..'; 4 | 5 | const mp3 = [ 6 | 'https://files.freemusicarchive.org/storage-freemusicarchive-org/music/blocSonic/Flex_Vector/Born_Ready/Flex_Vector_-_Born_Ready.mp3', 7 | 'https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_700KB.mp3', 8 | 'https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_5MG.mp3', 9 | ]; 10 | 11 | const Demo = () => { 12 | const [state, setState] = React.useState(null); 13 | 14 | return ( 15 |
16 |
17 | 18 |
19 |
20 |
{JSON.stringify(state, null, 4)}
21 |
22 |
23 | ); 24 | }; 25 | 26 | storiesOf('components/Player', module) 27 | .add('Default', () => { 28 | return ; 29 | }) 30 | .add('Demo', () => { 31 | return ; 32 | }) 33 | .add('Hight scale', () => { 34 | return ( 35 |
36 |
37 | 38 |
39 |
40 | 41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 | ); 50 | }) 51 | .add('Dark mode', () => { 52 | return ( 53 |
54 |
55 | 56 |
57 |
58 | ); 59 | }) 60 | .add('Grey scale', () => { 61 | return ( 62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 | 71 |
72 |
73 | 74 |
75 |
76 | 77 |
78 |
79 | 80 |
81 |
82 | ); 83 | }) 84 | .add('in flex', () => { 85 | return ( 86 |
94 | 95 |
96 | ); 97 | }); 98 | -------------------------------------------------------------------------------- /src/components/Player/createColorManager.ts: -------------------------------------------------------------------------------- 1 | const createColorManager = (grey: [number, number, number], accent: [number, number, number]) => { 2 | const [r, g, b] = grey; 3 | const avg = Math.round((r + g + b) / 3); 4 | const isLight = avg > 127; 5 | const contrast = isLight ? [0, 0, 0] : [255, 255, 255]; 6 | 7 | return { 8 | isLight, 9 | avg, 10 | grey, 11 | accent, 12 | shade: (value: number) => { 13 | const base = isLight ? '0' : '255'; 14 | return `rgba(${base},${base},${base},${value})`; 15 | }, 16 | shift: (value: number, opacity = 1) => { 17 | const r1 = Math.max(0, Math.min(255, r + (isLight ? value : -value))); 18 | const g1 = Math.max(0, Math.min(255, g + (isLight ? value : -value))); 19 | const b1 = Math.max(0, Math.min(255, b + (isLight ? value : -value))); 20 | return opacity === 1 ? `rgba(${r1},${g1},${b1},${opacity})` : `rgb(${r1},${g1},${b1})`; 21 | }, 22 | contrast: (opacity = 1) => `rgba(${contrast[0]},${contrast[1]},${contrast[2]},${opacity})`, 23 | }; 24 | }; 25 | 26 | export default createColorManager; 27 | -------------------------------------------------------------------------------- /src/components/Player/formatTime.ts: -------------------------------------------------------------------------------- 1 | const formatTime = (seconds: number) => { 2 | const round = Math.round(seconds); 3 | const s = round % 60; 4 | const minutes = Math.round((round - s) / 60); 5 | const m = minutes % 60; 6 | const h = Math.round((minutes - m) / 60); 7 | 8 | return `${h ? h + ':' : ''}${m > 9 ? m : '0' + m}:${s > 9 ? s : '0' + s}`; 9 | }; 10 | 11 | export default formatTime; 12 | -------------------------------------------------------------------------------- /src/components/Player/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {rule, nano} from 'p4-css'; 3 | import useAudio from 'react-use/lib/useAudio'; 4 | import useSlider from 'react-use/lib/useSlider'; 5 | import {HTMLMediaState, HTMLMediaControls} from 'react-use/lib/factory/createHTMLMediaHook'; 6 | import IconPlay from '../../icons/Play'; 7 | import IconPause from '../../icons/Pause'; 8 | import IconVolume from '../../icons/Volume'; 9 | import IconMuted from '../../icons/Muted'; 10 | import {Rail} from '../Rail'; 11 | import {RailWrap} from '../RailWrap'; 12 | import {Volume} from '../Volume'; 13 | import createColorManager from './createColorManager'; 14 | import formatTime from './formatTime'; 15 | 16 | const {useRef, useEffect, useState, useMemo} = React; 17 | 18 | const ff = '"Open Sans",Roboto,sans-serif'; 19 | export const defaultHeight = 64; 20 | 21 | export type PlayerState = HTMLMediaState; 22 | export type PlayerControls = HTMLMediaControls; 23 | 24 | const blockClass = rule({ 25 | d: 'flex', 26 | alignItems: 'center', 27 | bg: '#eee', 28 | h: defaultHeight + 'px', 29 | bdrad: '4px', 30 | w: '100%', 31 | }); 32 | 33 | const playButtonClass = rule({ 34 | d: 'flex', 35 | alignItems: 'center', 36 | h: '100%', 37 | pad: '0 16px', 38 | cur: 'pointer', 39 | mar: 0, 40 | bd: 0, 41 | bg: 'transparent', 42 | bdrad: '4px', 43 | '& svg': { 44 | w: '18px', 45 | h: '18px', 46 | }, 47 | }); 48 | 49 | const seekAreaClass = rule({ 50 | pos: 'relative', 51 | d: 'flex', 52 | flex: '1 1 100%', 53 | alignItems: 'center', 54 | h: '100%', 55 | cur: 'pointer', 56 | cursor: 'ew-resize', 57 | }); 58 | 59 | const tooltipClass = rule({ 60 | pos: 'absolute', 61 | op: 0.9, 62 | }); 63 | 64 | const tooltipInnerClass = rule({ 65 | d: 'inline-block', 66 | mar: '0 0 0 -50%', 67 | pad: '4px 8px', 68 | fz: '12px', 69 | ff, 70 | bdrad: '3px', 71 | whiteSpace: 'nowrap', 72 | }); 73 | 74 | const timeClass = rule({ 75 | whiteSpace: 'nowrap', 76 | userSelect: 'none', 77 | pad: '0 0 0 16px', 78 | ff, 79 | fz: '12px', 80 | }); 81 | 82 | const volumeButtonClass = rule({ 83 | d: 'flex', 84 | alignItems: 'center', 85 | h: '100%', 86 | pad: '0 16px', 87 | cur: 'pointer', 88 | mar: 0, 89 | bd: 0, 90 | bg: 'transparent', 91 | bdrad: '4px', 92 | '& svg': { 93 | w: '18px', 94 | h: '18px', 95 | }, 96 | }); 97 | 98 | export interface PlayerProps { 99 | /** 100 | * URL of .mp3 or other audio media. 101 | */ 102 | src: string; 103 | 104 | /** 105 | * Shade of grey to use as base color for player. 3-tuple of numbers in 106 | * 0 to 255 range, representing an RGB color. 107 | */ 108 | grey?: [number, number, number]; 109 | 110 | /** 111 | * Accent color 3-tuple of numbers from 0 to 255, representing an RGB color. 112 | */ 113 | accent?: [number, number, number]; 114 | 115 | /** 116 | * Player height in px. 117 | */ 118 | height?: number; 119 | 120 | /** 121 | * Whether to autoplay the audio on mount. 122 | */ 123 | autoPlay?: boolean; 124 | 125 | /** 126 | * Whether to hide volume slider. 127 | */ 128 | hideVolume?: boolean; 129 | 130 | /** 131 | * React mutable ref which will be set to contain audio player controls. 132 | */ 133 | controls?: React.MutableRefObject; 134 | 135 | /** 136 | * React mutable ref which will be set to contain latest state of the player. 137 | */ 138 | state?: React.MutableRefObject; 139 | 140 | /** 141 | * Get reference to HTML