├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs
├── .babelrc
├── assets
│ ├── app.js
│ ├── favicon.png
│ ├── logo-icon.svg
│ ├── prism.css
│ └── styles.css
├── index.html
├── package-lock.json
├── package.json
└── src
│ └── demos.js
├── package-lock.json
├── package.json
├── resources
└── icons
│ ├── next.svg
│ ├── pause.svg
│ ├── play.svg
│ ├── prev.svg
│ ├── sound-off.svg
│ └── sound-on.svg
├── src
├── components
│ ├── FormattedTime.js
│ ├── Slider.js
│ └── icons.js
├── constants.js
└── index.js
└── tests
├── FormattedTime-test.js
└── helpers
└── configure-enzyme.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"],
3 | "plugins": [
4 | "transform-class-properties",
5 | ["@babel/plugin-proposal-decorators", { "legacy": true }],
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | # editorconfig.org
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 |
13 | [*.md]
14 | trim_trailing_whitespace = true
15 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["standard", "plugin:react/recommended"],
3 | "parser": "babel-eslint",
4 | "plugins": ["react", "import", "jsx-a11y"],
5 | "rules": {
6 | "brace-style": [2, "stroustrup"],
7 | "comma-dangle": [2, "only-multiline"],
8 | "react/prop-types": [2, { "ignore": ["className", "extraClasses", "childClasses", "childrenStyles"] }],
9 | },
10 | "settings": {
11 | "react": {
12 | "version": "detect",
13 | },
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | # ------------------------
40 | # Project specific ignores
41 | # ------------------------
42 |
43 | dist
44 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "10"
4 | - "8"
5 | before_install:
6 | - if [[ `npm -v` != 6* ]]; then npm i -g npm@6; fi
7 | install:
8 | - npm install
9 | script:
10 | - npm test
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2016-present, Alexander Wallin
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 | PERFORMANCE OF THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | react-player-controls
5 |
6 |
7 |
8 |
9 | [](https://npmjs.com/package/react-player-controls)
10 | [](https://travis-ci.org/alexanderwallin/react-player-controls)
11 | [](https://david-dm.org/alexanderwallin/react-player-controls)
12 | [](https://david-dm.org/alexanderwallin/react-player-controls#info=devDependencies)
13 |
14 | This is a minimal set of modular and hopefully useful React components for composing media player interfaces. It is designed for you to compose media player controls yourself using a [small and easy-to-learn API](#api).
15 |
16 | Instead of shipping default but customisable styles, there are [implementation recipies](#recipes) to help you get going quickly. Also check out [the demo site](http://alexanderwallin.github.io/react-player-controls/) to try the components out.
17 |
18 | ⚠️ **NOTE:** This library does not deal with actual media in any way, only the UI. ⚠️
19 |
20 | ## Table of contents
21 |
22 | * [Installation](#installation)
23 | * [Usage](#usage)
24 | * [API](#api)
25 | * [Recipies](#recipies)
26 | * [Contribute](#contribute)
27 |
28 |
29 | ## Installation
30 |
31 | ```sh
32 | npm i react-player-controls
33 | ```
34 |
35 | ## Usage
36 |
37 | ```js
38 | // ES2015+ import
39 | import { Slider, Direction } from 'react-player-controls'
40 |
41 | // Using CommonJS
42 | const { Slider, Direction } = require('react-player-controls')
43 | ```
44 |
45 | ## API
46 |
47 | * [`Direction`](#direction)
48 | * [``](#formattedtime-)
49 | * [``](#playericon-)
50 | * [``](#slider-)
51 |
52 | ### `Direction`
53 |
54 | An enum describing a slider's active axis.
55 |
56 | | Key | Value |
57 | |-----|-------|
58 | | `HORIZONTAL` | `"HORIZONTAL"` |
59 | | `VERTICAL` | `"VERTICAL"` |
60 |
61 | ### ``
62 |
63 | `` translates a number of seconds into the player-friendly format of `m:ss`, or `h:mm:ss` if the total time is one hour or higher.
64 |
65 | ```js
66 | // This will render -1:01:02
67 |
68 | ```
69 |
70 | | Prop name | Default value | Description |
71 | |-----------|---------------|-------------|
72 | | `numSeconds` | `0` | A number of seconds, positive or negative |
73 | | `className` | `null` | A string to set as the HTML `class` attribute |
74 | | `style` | `{}` | Styles to set on the wrapping `span` element. |
75 |
76 | ### ``
77 |
78 | `` is not really a component in itself, but a container of a number of icon components.
79 |
80 | ```js
81 |
82 |
83 |
84 |
85 |
86 |
87 | ```
88 |
89 | Any props passed to a `` component will be passed onto the underlying `svg` element.
90 |
91 | ### ``
92 |
93 | The `` helps you build things like volume controls and progress bars. **It does not take a `value` prop**, but expects you to keep track of this yourself and render whatever you want inside it.
94 |
95 | What this component actually does is that it renders an element inside itself, on top of its children, which listens to mouse events and invokes change and intent callbacks with relative, normalised values based on those events.
96 |
97 | ```js
98 | console.log(`hovered at ${intent}`)}
101 | onIntentStart={intent => console.log(`entered with mouse at ${intent}`)}
102 | onIntentEnd={() => console.log('left with mouse')}
103 | onChange={newValue => console.log(`clicked at ${newValue}`)}
104 | onChangeStart={startValue => console.log(`started dragging at ${startValue}`)}
105 | onChangeEnd={endValue => console.log(`stopped dragging at ${endValue}`)}
106 | >
107 | {/* Here we render whatever we want. Nothings is rendered by default. */}
108 |
109 | ```
110 |
111 | | Prop name | Default value | Description |
112 | |-----------|---------------|-------------|
113 | | `direction` | `Direction.HORIZONTAL` | The slider's direction |
114 | | `onIntent` | `(intent) => {}` | A function that is invoked with the relative, normalised value at which the user is hovering (when not dragging). |
115 | | `onIntentStart` | `(intent) => {}` | A function this is invoked with the relative, normalised value at which the user started hovering the slider (when not dragging). |
116 | | `onIntentEnd` | `() => {}` | A function this is invoked when the mouse left the slider area (when not dragging). |
117 | | `onChange` | `(value) => {}` | A function that is invoked with the latest relative, normalised value that the user has set by either clicking or dragging. |
118 | | `onChangeStart` | `(value) => {}` | A function that is invoked with the relative, normalised value at which the user started changing the slider's value. |
119 | | `onChangeEnd` | `(value) => {}` | A function that is invoked with the relative, normalised value at which the user stopped changing the slider's value. When the component unmounts, this function will be invoked with a value of `null`. |
120 | | `children` | `null` | Child elements. |
121 | | `className` | `null` | A string to set as the HTML `class` attribute. |
122 | | `style` | `{}` | Styles to set on the wrapping `div` element. |
123 | | `overlayZIndex` | 10 | The `z-index` of the invisible overlay that captures mouse events |
124 |
125 |
126 | ## Recipies
127 |
128 |
129 | Styled buttons with icons
130 |
131 | ```js
132 | import { PlayerIcon } from 'react-player-controls'
133 |
134 | // A base component that has base styles applied to it
135 | const PlayerButton = ({ style, children, ...props }) => (
136 |
153 | )
154 |
155 | // Compose buttons with matching icons. Use whatever icon library
156 | // you want. If you don't have any particular logic for each of the
157 | // buttons, you might not need this abstraction.
158 | const PlayButton = props =>
159 | const PauseButton = props =>
160 | const PreviousButton = props =>
161 | const NextButton = props =>
162 | ```
163 |
164 |
165 |
166 | Styled slider
167 |
168 | ```js
169 | import { Direction, Slider } from 'react-player-controls'
170 |
171 | const WHITE_SMOKE = '#eee'
172 | const GRAY = '#878c88'
173 | const GREEN = '#72d687'
174 |
175 | // A colored bar that will represent the current value
176 | const SliderBar = ({ direction, value, style }) => (
177 |
194 | )
195 |
196 | // A handle to indicate the current value
197 | const SliderHandle = ({ direction, value, style }) => (
198 |
222 | )
223 |
224 | // A composite progress bar component
225 | const ProgressBar = ({ isEnabled, direction, value, ...props }) => (
226 |
239 |
240 |
241 |
242 | )
243 |
244 | // Now use somewhere
245 | seek(value * currentSong.duration)}
250 | />
251 | ```
252 |
253 |
254 |
255 | Playback controls
256 |
257 | ```js
258 | import Icon from 'some-icon-library'
259 |
260 | const PlaybackControls = ({
261 | isPlaying,
262 | onPlaybackChange,
263 | hasPrevious,
264 | onPrevious,
265 | hasNext,
266 | onNext,
267 | }) => (
268 |