diff --git a/.eslintrc b/.eslintrc index 8e1d97d3..fc128474 100644 --- a/.eslintrc +++ b/.eslintrc @@ -11,7 +11,10 @@ "jest": true }, "rules": { + "no-unused-expressions": "error", "no-trailing-spaces": "error", + "no-nested-ternary": "error", + "space-infix-ops": "error", "indent": ["warn", 2], "arrow-spacing": ["error", { "before": true, "after": true }], "space-in-parens": [ 0, "never" ], diff --git a/README.md b/README.md index 9ce746f2..7ab27559 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,18 @@ npm install @bbc/react-transcript-editor ```js import { TranscriptEditor } from '@bbc/react-transcript-editor'; - + ``` +See [`./src/index.js` demo](./src/index.js) as a more detailed example usage of the component. _Note: `fileName` it is optional but it's needed if working with user uploaded local media in the browser, to be able to save and retrieve from local storage. For instance if you are passing a blob url to `mediaUrl` using `createObjectURL` this url is randomly re-generated on every page refresh so you wouldn't be able to restore a session, as `mediaUrl` is used as the local storage key. See demo app for more detail example of this[`./src/index.js`](./src/index.js)_ diff --git a/package-lock.json b/package-lock.json index 6af22584..52fd4ac2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bbc/react-transcript-editor", - "version": "0.2.9", + "version": "0.2.11", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -9199,7 +9199,8 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true }, "lodash.escape": { "version": "4.0.1", diff --git a/src/index.js b/src/index.js index 616d6638..681db3b2 100644 --- a/src/index.js +++ b/src/index.js @@ -19,7 +19,8 @@ class App extends React.Component { isTextEditable: true, sttType: 'bbckaldi', analyticsEvents: [], - fileName: 'Kate Darling Ted Talk' + title: 'Ted Talk Kate Kate Darling', + fileName: '' }; this.transcriptEditorRef = React.createRef(); @@ -67,7 +68,7 @@ class App extends React.Component { handleChangeLoadTranscriptJson(files) { const file = files[0]; - if (file.type ==='application/json') { + if (file.type === 'application/json') { const fr = new FileReader(); fr.onload = (evt) => { @@ -125,11 +126,18 @@ class App extends React.Component { this.setState({ analyticsEvents: [ ...this.state.analyticsEvents, event ] }); } + handleChangeTranscriptTitle = (newTitle) => { + this.setState({ + title: newTitle + }); + } + handleChangeTranscriptName = (value) => { this.setState({ fileName: value }); } render() { + return (
@@ -184,13 +192,14 @@ class App extends React.Component { /> -
- +
+ this.handleChangeTranscriptName(e.target.value) } - value={ this.state.fileName } + value={ this.state.title } + onChange={ e => this.handleChangeTranscriptTitle(e.target.value) } /> +

@@ -202,6 +211,7 @@ class App extends React.Component { isEditable={ this.state.isTextEditable } sttJsonType={ this.state.sttType } handleAnalyticsEvents={ this.handleAnalyticsEvents } + title={ this.state.title } ref={ this.transcriptEditorRef } />
diff --git a/src/index.module.css b/src/index.module.css index a2383559..dc2f5903 100644 --- a/src/index.module.css +++ b/src/index.module.css @@ -1,3 +1,8 @@ +body { + margin: 0px; + padding: 10px; +} + .container { height: 100vh; font-family: ReithSerif, Fallback, sans-serif; diff --git a/src/lib/TranscriptEditor/MediaPlayer/PlayerControls.js b/src/lib/TranscriptEditor/MediaPlayer/PlayerControls.js index abdff384..5fb96f61 100644 --- a/src/lib/TranscriptEditor/MediaPlayer/PlayerControls.js +++ b/src/lib/TranscriptEditor/MediaPlayer/PlayerControls.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import VolumeControl from './VolumeControl'; + import Select from './Select'; import style from './PlayerControls.module.css'; @@ -14,7 +14,9 @@ import { faPause, faBackward, faForward, - faUndo + faUndo, + faVolumeUp, + faVolumeOff } from '@fortawesome/free-solid-svg-icons'; class PlayerControls extends React.Component { @@ -23,14 +25,14 @@ class PlayerControls extends React.Component { // backward or forward function // on mouseUp the interval is cleared setIntervalHelperBackward = () => { - this.props.skipBackward(); + // this.props.skipBackward(); this.interval = setInterval(() => { this.props.skipBackward(); }, 300); } setIntervalHelperForward = () => { - this.props.skipForward(); + // this.props.skipForward(); this.interval = setInterval(() => { this.props.skipForward(); }, 300); @@ -43,78 +45,98 @@ class PlayerControls extends React.Component { render() { return (
- - - - - - - - - - + + + + + + + +
); } } PlayerControls.propTypes = { + playMedia: PropTypes.func, currentTime: PropTypes.string, timecodeOffset: PropTypes.string, @@ -123,12 +145,14 @@ PlayerControls.propTypes = { handleMuteVolume: PropTypes.func, duration: PropTypes.string, isPlaying: PropTypes.bool, + isMute: PropTypes.bool, skipBackward: PropTypes.func, skipForward: PropTypes.func, playbackRate: PropTypes.number, playbackRateOptions: PropTypes.array, setPlayBackRate: PropTypes.func, - pictureInPicture: PropTypes.func + pictureInPicture: PropTypes.func, + handleSaveTranscript: PropTypes.func }; export default PlayerControls; diff --git a/src/lib/TranscriptEditor/MediaPlayer/PlayerControls.module.css b/src/lib/TranscriptEditor/MediaPlayer/PlayerControls.module.css index c151a5ca..91cf267d 100644 --- a/src/lib/TranscriptEditor/MediaPlayer/PlayerControls.module.css +++ b/src/lib/TranscriptEditor/MediaPlayer/PlayerControls.module.css @@ -1,9 +1,5 @@ -@value color-darkest-grey, color-light-grey, color-labs-red from '../colours.module.css'; +@value color-light-shilo, color-darkest-grey, color-light-grey, color-labs-red from '../colours.module.css'; -.playerControls { - margin: 1em; - display: flex; -} .playerControls > * { display: inline-block; @@ -11,7 +7,6 @@ .playerControls > *:not(:last-child) { margin-right: 0.5em; - background: color-darkest-grey; } .playerButton { @@ -20,24 +15,28 @@ padding: 0.5em; border: 0; color: white; + background: color-darkest-grey; font-size: 1em; cursor: pointer; + margin-right: 0.3rem; + margin-top: 0.3rem; } .playBackRate{ + width: 70px; border: 0; color: white; font-size: 1em; cursor: pointer; position: relative; - padding-left: 0.8em; + margin-right: 0.3rem; } .playBackRate::before{ content: '×'; position: absolute; - bottom: 11px; - left: 12px; + bottom: -2px; + left: 21px; } .playBackRate > select { @@ -47,6 +46,8 @@ outline: none; width: auto; width: 100%; + color:white; + background-color: color-darkest-grey; } .timeBox { @@ -54,10 +55,13 @@ text-align: center; line-height: 48px; padding: 0 1em; + background-color: color-darkest-grey; } + + .currentTime { - color: color-labs-red; + color: color-light-shilo; cursor: pointer; } @@ -69,3 +73,88 @@ .duration { color: white; } + +@media (max-width: 760px) { + .playerControls { + margin:0 0 10px 0; + display: grid; + grid-template-rows: 10% 48px 48px; + grid-row-gap: 5px; + + } + + .playerControls > *:not(:last-child) { + margin:0; + } + + .btnsGroup { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-column-gap: 0.2em; + } + + .playerButton { + width: 100%; + height: 48px; + margin:0; + } + + .playBackRate{ + width: 100%; + border: 0; + color: white; + font-size: 1em; + cursor: pointer; + position: relative; + grid-column: 1 / span 2; + } + + .playBackRate::before{ + content: '×'; + position: absolute; + bottom: 30%; + left: 21px; + } + + .playBackRate > select { + padding-top: 0.5em; + padding-bottom: 0.5em; + height: 100%; + outline: none; + width: auto; + width: 100%; + color:white; + background-color: color-darkest-grey; + } + + .timeBox { + background-color:transparent; + align-self: start; + padding:0; + } + + .currentTime { + color: color-light-shilo; + cursor: pointer; + float:left; + font-size: 0.7em; + line-height: 1em; + } + + .separator { + color: color-light-grey; + margin: 0 1em; + display:none; + } + + .duration { + color: white; + font-size: 0.7em; + float:right; + line-height: 1em; + } + + .pip { + display:none; + } +} \ No newline at end of file diff --git a/src/lib/TranscriptEditor/MediaPlayer/ProgressBar.module.css b/src/lib/TranscriptEditor/MediaPlayer/ProgressBar.module.css index 6e0f0487..60427cb5 100644 --- a/src/lib/TranscriptEditor/MediaPlayer/ProgressBar.module.css +++ b/src/lib/TranscriptEditor/MediaPlayer/ProgressBar.module.css @@ -1,41 +1,95 @@ @value color-light-grey, color-labs-red from '../colours.module.css'; -.bar { - position: relative; - overflow: hidden; - height: 20px; +.progress { + margin-bottom:1em; +} + +input[type=range] { + /*removes default webkit styles*/ -webkit-appearance: none; - margin: 10px 0; + background-color:transparent; + + /*required for proper track sizing in FF*/ + width: 100%; + cursor: pointer; +} +input[type=range]::-webkit-slider-runnable-track { width: 100%; + height: 3px; + background: #ccc; + border: none; + border-radius: 0px; +} +input[type=range]::-webkit-slider-thumb { + -webkit-appearance: none; + border: none; + height: 22px; + width: 22px; + border-radius: 22px; + background:rgb(255, 255, 255); + margin-top: -10px; } -.bar:focus { +input[type=range]:focus { outline: none; } +input[type=range]:focus::-webkit-slider-runnable-track { + background: #ccc; +} -.bar::-webkit-slider-runnable-track { +input[type=range]::-moz-range-track { width: 100%; - cursor: pointer; - background: color-light-grey; + height: 3px; + background: #ccc; + border: none; +} +input[type=range]::-moz-range-thumb { + border: none; + height: 22px; + width: 22px; + border-radius: 22px;; + background:rgb(255, 255, 255); } - -.bar::-webkit-slider-thumb { - background: color-labs-red; - cursor: pointer; - -webkit-appearance: none; - width: 20px; - height: 20px; - background: #fff; - box-shadow: -100vw 0 0 100vw color-labs-red; - border: 2px solid #999; +/*hide the outline behind the border*/ +input[type=range]:-moz-focusring{ + outline: 1px solid white; + outline-offset: -1px; } -/* - The ms-upper/low only works on IE: box-shadow solution is cross browser - TODO: make slider thumb bypass overflow:hidden (make taller than bar) -*/ +input[type=range]::-ms-track { + width: 100%; + height: 3px; + + /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */ + background: transparent; + + /*leave room for the larger thumb to overflow with a transparent border */ + border-color: transparent; + border-width: 6px 0; -.bar:focus::-webkit-slider-runnable-track { - background: color-light-grey; + /*remove default tick marks*/ + color: transparent; +} +input[type=range]::-ms-fill-lower { + background: #777; + border-radius: 10px; +} +input[type=range]::-ms-fill-upper { + background: #ddd; + border-radius: 10px; +} +input[type=range]::-ms-thumb { + border: none; + height: 16px; + width: 16px; + border-radius: 50%; + background:rgb(255, 255, 255); + margin-top: 0px; +} +input[type=range]:focus::-ms-fill-lower { + background: #888; } +input[type=range]:focus::-ms-fill-upper { + background: #eee; +} \ No newline at end of file diff --git a/src/lib/TranscriptEditor/MediaPlayer/Select.module.css b/src/lib/TranscriptEditor/MediaPlayer/Select.module.css index 049660c6..a21c79d4 100644 --- a/src/lib/TranscriptEditor/MediaPlayer/Select.module.css +++ b/src/lib/TranscriptEditor/MediaPlayer/Select.module.css @@ -1,17 +1,21 @@ -.selectPlayerControl { +.selectPlayerControl[name="playbackRate"] { font-size: 1em; -webkit-appearance: none; -moz-appearance: none; appearance: none; - height: 20px; - width: 60px; + height: 48px; + width: 97px; border-radius: 0; - padding-left: 8px; background-image: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMS4xLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEyNS4zMDQgMTI1LjMwNCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTI1LjMwNCAxMjUuMzA0OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCI+CjxnPgoJPGc+CgkJPHBvbHlnb24gcG9pbnRzPSI2Mi42NTIsMTAzLjg5NSAwLDIxLjQwOSAxMjUuMzA0LDIxLjQwOSAgICIgZmlsbD0iI0ZGRkZGRiIvPgoJPC9nPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=); background-repeat: no-repeat; background-position: 85% center; - background-color: transparent; color: white; border-color: transparent; - padding-right: 30px; + padding-left: 32px; } + +@media (max-width: 760px) { + .selectPlayerControl[name="playbackRate"] { + width: 100%; + } +} \ No newline at end of file diff --git a/src/lib/TranscriptEditor/MediaPlayer/VideoPlayer.js b/src/lib/TranscriptEditor/MediaPlayer/VideoPlayer.js index 53680d58..e7fe4e19 100644 --- a/src/lib/TranscriptEditor/MediaPlayer/VideoPlayer.js +++ b/src/lib/TranscriptEditor/MediaPlayer/VideoPlayer.js @@ -1,8 +1,17 @@ import React from 'react'; import PropTypes from 'prop-types'; +import styles from './VideoPlayer.module.css'; class VideoPlayer extends React.Component { + + handlePlayMedia = () => { + if (this.props.videoRef.current !== null) { + return this.props.videoRef.current.paused ? this.props.videoRef.current.play() : this.props.videoRef.current.pause(); + } + }; render() { + const isDisplayed = this.props.previewIsDisplayed ? 'inline' : 'none'; + return (