diff --git a/.eslintrc b/.eslintrc index c4673ab2..c62b046f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,7 +10,8 @@ ], "env": { "browser": true, - "jest": true + "jest": true, + "node": true, }, "rules": { "prefer-const": 1, @@ -43,7 +44,7 @@ "no-param-reassign": [ 1 ], "no-shadow": [ 1 ], "camelcase": [ 1 ], - "no-underscore-dangle" : [0, "always"], + "no-underscore-dangle": [0, "always"], "keyword-spacing": ["error", { "before": true, "after": true }], "key-spacing": ["error", { "afterColon": true }], "newline-before-return": "error", diff --git a/.storybook/config.js b/.storybook/config.js index e357274d..9415e7b3 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -17,7 +17,8 @@ addDecorator(withA11y); addParameters({ options: { - panelPosition: 'right', + // showPanel: false, + panelPosition: 'bottom', sidebarAnimations: true }, }); diff --git a/.storybook/styles/main.scss b/.storybook/styles/main.scss index 18db8bce..56f3ef51 100644 --- a/.storybook/styles/main.scss +++ b/.storybook/styles/main.scss @@ -1,4 +1,13 @@ -body * { +body { font-family: ReithSerif, Fallback, sans-serif; - // font-family: ReithSans, Helvetica, sans-serif !important; + +} + +p, span { + font-family: ReithSans, Helvetica, sans-serif; + +} + +h1, h2, h3, h4, h5, h6, label * { + font-family: ReithSerif; } diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index ea93098a..f95b7d04 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -6,36 +6,28 @@ module.exports = { module: { rules: [ { - test: /\.module.css$/, + test: /\.module.(sa|sc|c)ss$/, use: [ + "style-loader", { - loader: "style-loader" + loader: "css-loader", + options: { modules: true } }, { - loader: "css-loader", - options: { - modules: true - } + loader: "sass-loader", + options: { sourcemap: true } } ] }, { - test: /\.scss$/, + test: /\.s(a|c)ss$/, + exclude: /\.module.(s(a|c)ss)$/, use: [ - { - loader: "style-loader" - }, - { - loader: "css-loader", - options: { - sourceMap: true - } - }, + "style-loader", + "css-loader", { loader: "sass-loader", - options: { - sourcemap: true - } + options: { sourcemap: true } } ] } diff --git a/demo/app.js b/demo/app.js index ce05b32c..2330f850 100644 --- a/demo/app.js +++ b/demo/app.js @@ -1,20 +1,16 @@ import React from 'react'; -// NOTE: This slows down performance, even during development -// if (process.env.NODE_ENV !== 'production') { -// const { whyDidYouUpdate } = require('why-did-you-update'); -// whyDidYouUpdate(React, { exclude: [ /^HotKeysWrapper/ ] } ); -// } + import TranscriptEditor from '../packages/components/transcript-editor'; import SttTypeSelect from './select-stt-json-type'; import ExportFormatSelect from './select-export-format'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faGithub } from '@fortawesome/free-brands-svg-icons'; -import demoTranscript from './sample-data/KateDarling-bbcKaldiTranscriptWithSpeakerSegments.json'; -const demoMediaUrl = 'https://download.ted.com/talks/KateDarling_2018S-950k.mp4'; -const demoTitle = 'Ted Talk - Kate Darling'; +import DEMO_TRANSCRIPT from './sample-data/KateDarling-bbcKaldiTranscriptWithSpeakerSegments.json'; +const DEMO_MEDIA_URL = 'https://download.ted.com/talks/KateDarling_2018S-950k.mp4'; +const DEMO_TITLE = 'TED Talk | Kate Darling - Why we have an emotional connection to robots'; -import style from './index.module.css'; +import style from './index.module.scss'; class App extends React.Component { constructor(props) { @@ -36,9 +32,9 @@ class App extends React.Component { loadDemo = () => { this.setState({ - transcriptData: demoTranscript, - mediaUrl: demoMediaUrl, - title: demoTitle, + transcriptData: DEMO_TRANSCRIPT, + mediaUrl: DEMO_MEDIA_URL, + title: DEMO_TITLE, sttType: 'bbckaldi' }); } @@ -52,7 +48,7 @@ class App extends React.Component { if (canPlay) { const fileURL = URL.createObjectURL(file); this.setState({ - // transcriptData: demoTranscript, + // transcriptData: DEMO_TRANSCRIPT, mediaUrl: fileURL, fileName: file.name }); @@ -65,7 +61,7 @@ class App extends React.Component { const fileURL = prompt("Paste the URL you'd like to use here:"); this.setState({ - // transcriptData: demoTranscript, + // transcriptData: DEMO_TRANSCRIPT, mediaUrl: fileURL }); } @@ -178,19 +174,19 @@ class App extends React.Component {
- - + + this.handleLoadMedia(e.target.files) } /> - + {this.state.fileName !== '' ? : null}
- + this.handleLoadTranscriptJson(e.target.files) } /> - + {this.state.transcriptData !== null ? : null}
@@ -254,7 +250,11 @@ class App extends React.Component { /> - + @@ -281,6 +281,4 @@ class App extends React.Component { } } -// render(, document.getElementById('root')); - export default App; diff --git a/demo/index.module.css b/demo/index.module.css deleted file mode 100644 index b5f5837c..00000000 --- a/demo/index.module.css +++ /dev/null @@ -1,143 +0,0 @@ -body { - margin: 0; - padding: 0.5em; -} - -.container { - height: 100vh; - font-family: ReithSerif, Fallback, sans-serif; -} - -.demoNavItem { - padding-right: 1em; - text-align: center; -} - -.sectionLabel { - display: block; - text-align: center; - font-size: 0.9em; - font-weight: 500; -} - -[type="file"] { - border: 0; - clip: rect(0, 0, 0, 0); - height: 1px; - overflow: hidden; - padding: 0; - position: absolute !important; - white-space: nowrap; - width: 1px; -} - -[type="file"] + label { - display: inline-block; - box-sizing: border-box; - background-color: #5b97ef; - color: white; - cursor: pointer; - font-size: 0.7em; - padding: 0.75em 1.5em; - margin-top: 0.5em; - margin-right: 0.5em; - text-align: center; - width: 140px; - max-width: 140px; -} - -[type="file"]:focus + label, -[type="file"] + label:hover { - background-color: #4f7bc0; -} - -.demoNav section button { - background-color: #5b97ef; - border: none; - color: white; - cursor: pointer; - padding: 0.75em 1.5em; - text-align: center; - text-decoration: none; - display: inline-block; - box-sizing: border-box; - font-size: 0.7em; - margin-top: 0.5em; - margin-right: 0.5em; - width: 140px; - max-width: 140px; - white-space: nowrap; -} - -.demoNav section button:hover { - background-color: #4f7bc0; -} - -.warningButton { - background-color: #d25b56 !important; -} - -.warningButton:hover { - background-color: #b6332b !important; -} - -.demoButton { - background-color: #74b567 !important; -} - -.demoButton:hover { - background-color: #528946 !important; -} - -.dropdown { - display: block; - margin: 0.5em auto; - width: 140px; -} - -.fileNameLabel { - display: block; - text-align: center; - font-size: 0.5em; - margin: 0.5em; -} - -.titleLabel { - margin-left: 0.5em; - font-style: italic; - font-size: 0.7em; -} - -.editableLabel { - font-size: 0.7em; -} - -/* desktop */ -@media (min-width: 767px) { - .demoNav { - display: flex; - justify-content: space-between; - } -} - -/* phone */ -@media (max-width: 767px) { - body { - padding: 0; - } - - .demoNavItem { - padding-top: 0.5em; - padding-bottom: 0.5em; - padding-right: 0; - border-bottom: 1px solid grey; - } - - .demoNav section button { - display: inline-block; - } - - .checkbox { - display: block; - } -} diff --git a/demo/index.module.scss b/demo/index.module.scss new file mode 100644 index 00000000..d743bede --- /dev/null +++ b/demo/index.module.scss @@ -0,0 +1,142 @@ +body { + margin: 0; + padding: 0.5em; +} + +.container { + height: 100vh; + font-family: ReithSerif, sans-serif; +} + +.demoNav { + margin-top: 1rem; + padding: 0 2rem; + + .demoNavItem { + padding-right: 0.5rem; + text-align: center; + + .dropdown { + display: block; + margin: 0.5em auto; + width: 100px; + } + + .sectionLabel { + display: block; + font-size: 0.8em; + font-weight: 500; + } + + .fileNameLabel { + display: block; + text-align: center; + font-size: 0.5em; + margin: 0.5em; + } + + .titleLabel { + margin-left: 0.5em; + font-style: italic; + font-size: 0.7em; + } + + .editableLabel { + font-size: 0.7em; + } + } +} + +[type="file"] { + position: absolute !important; + overflow: hidden; + height: 0; + width: 0; + padding: 0; + clip: rect(0, 0, 0, 0); + border: 0; + white-space: nowrap; + + + label { + display: block; + margin: 0.5rem auto; + padding: 0.75em 1.5em; + height: 30px; + width: 120px !important; + background-color: #5b97ef; + color: white; + font-size: 0.7em; + box-sizing: border-box; + border-radius: 2px; + cursor: pointer; + + &:hover { + background-color: #4f7bc0; + } + } +} + +.demoNavItem button { + display: block; + margin: 0.5rem auto; + padding: 0.75em 1.5em; + height: 30px; + width: 120px !important; + background-color: #5b97ef; + color: white; + font-size: 0.7em; + border: none; + border-radius: 2px; + box-sizing: border-box; + text-decoration: none; + white-space: nowrap; + cursor: pointer; + + &:hover { + background-color: #4f7bc0; + } +} + +.warningButton { + background-color: #d25b56 !important; + font-size: 0.6rem !important; + + &:hover { + background-color: #b6332b !important; + } +} + + +.demoButton { + background-color: #74b567 !important; + + &:hover { + background-color: #528946 !important; + } +} + +/* desktop */ +@media (min-width: 768px) { + .demoNav { + display: flex; + justify-content: space-evenly; + } +} + +/* phone */ +@media (max-width: 767px) { + body { + padding: 0; + } + + .demoNavItem { + padding-top: 0.5em; + padding-bottom: 0.5em; + padding-right: 0; + border-bottom: 1px solid grey; + } + + .checkbox { + display: block; + } +} diff --git a/package-lock.json b/package-lock.json index 33eadc37..b6a736d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1850,6 +1850,19 @@ "regenerator-runtime": "^0.12.1", "semver": "^5.6.0", "webpack": "^4.29.0" + }, + "dependencies": { + "mini-css-extract-plugin": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", + "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + } + } } }, "@storybook/router": { @@ -10744,12 +10757,13 @@ } }, "mini-css-extract-plugin": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", - "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", + "integrity": "sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw==", "dev": true, "requires": { "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", "schema-utils": "^1.0.0", "webpack-sources": "^1.1.0" } @@ -13174,8 +13188,7 @@ "react-fast-compare": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", - "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==", - "dev": true + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, "react-focus-lock": { "version": "1.18.3", diff --git a/package.json b/package.json index 743ab91c..1d8fd941 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ ], "moduleNameMapper": { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(css|less)$": "/__mocks__/styleMock.js" + "\\.(css|scss|less)$": "/__mocks__/styleMock.js" } }, "dependencies": { @@ -54,6 +54,7 @@ "mousetrap": "1.5.2", "number-to-words": "^1.2.4", "prop-types": "^15.6.2", + "react-fast-compare": "^2.0.4", "react-keyboard-shortcuts": "^1.1.3", "react-simple-tooltip": "^2.3.3", "sbd": "^1.0.15", @@ -92,6 +93,7 @@ "gh-pages": "^2.0.1", "husky": "^1.1.3", "jest": "^24.7.1", + "mini-css-extract-plugin": "^0.8.0", "node-sass": "^4.12.0", "prettier-stylelint": "^0.4.2", "react-testing-library": "^5.2.3", diff --git a/packages/components/keyboard-shortcuts/index.module.css b/packages/components/keyboard-shortcuts/index.module.css index 396692f3..c3b4a395 100644 --- a/packages/components/keyboard-shortcuts/index.module.css +++ b/packages/components/keyboard-shortcuts/index.module.css @@ -1,4 +1,4 @@ -@value color-lightest-grey, color-labs-red, color-dark-grey from '../../config/style-guide/colours.module.css'; +@import '../../config/style-guide/colours.scss'; .shortcuts { font-size: 0.85em; @@ -7,7 +7,7 @@ height: auto; vertical-align: middle; color: black; - background: color-lightest-grey; + background: $color-lightest-grey; z-index: 2; position: absolute; top: 0; @@ -43,7 +43,7 @@ .shortcut { display: inline-block; width: 6em; - background: color-labs-red; + background: $color-labs-red; font-weight: lighter; color: white; padding: 0 0; diff --git a/packages/components/media-player/index.js b/packages/components/media-player/index.js index 1a5cc8d9..0ea7e52e 100644 --- a/packages/components/media-player/index.js +++ b/packages/components/media-player/index.js @@ -1,18 +1,20 @@ import React from 'react'; import PropTypes from 'prop-types'; import { hotkeys } from 'react-keyboard-shortcuts'; + import PlayerControls from './src/PlayerControls'; import ProgressBar from './src/ProgressBar'; -import returnHotKeys from './src/defaultHotKeys'; -import styles from './index.module.css'; +import returnHotKeys from './src/config/defaultHotKeys'; + +import styles from './index.module.scss'; import { secondsToTimecode, timecodeToSeconds } from '../../util/timecode-converter'; -import PLAYBACK_RATES from './src/PLAYBACK_RATES.js'; +import PLAYBACK_RATES from './src/config/playbackRates.js'; class MediaPlayer extends React.Component { constructor(props) { @@ -35,6 +37,7 @@ class MediaPlayer extends React.Component { static getDerivedStateFromProps(nextProps) { if (nextProps.timecodeOffset !== null) { let newCurrentTimeInSeconds = nextProps.timecodeOffset; + if ( typeof newCurrentTimeInSeconds === 'string' && newCurrentTimeInSeconds.includes(':') && @@ -92,13 +95,11 @@ class MediaPlayer extends React.Component { if (newCurrentTime !== '' && newCurrentTime !== null) { // hh:mm:ss:ff - mm:ss - m:ss - ss - seconds number or string and hh:mm:ss const newCurrentTimeInSeconds = timecodeToSeconds(newCurrentTime); - if (this.props.videoRef.current !== null) { - const videoRef = this.props.videoRef.current; + const videoRef = this.props.videoRef.current; - if (videoRef.readyState === 4) { - videoRef.currentTime = newCurrentTimeInSeconds; - this.playMedia(); - } + if (videoRef.readyState === 4) { + videoRef.currentTime = newCurrentTimeInSeconds; + this.playMedia(); } } }; @@ -158,22 +159,20 @@ class MediaPlayer extends React.Component { }; rollBack = () => { - if (this.props.videoRef.current !== null) { - if (this.props.handleAnalyticsEvents) { - this.props.handleAnalyticsEvents({ - category: 'MediaPlayer', - action: 'rollBack', - name: 'rollBackValue', - value: this.state.rollBackValueInSeconds - }); - } - // get video duration - const videoElem = this.props.videoRef.current; - const tmpDesiredCurrentTime = + if (this.props.handleAnalyticsEvents) { + this.props.handleAnalyticsEvents({ + category: 'MediaPlayer', + action: 'rollBack', + name: 'rollBackValue', + value: this.state.rollBackValueInSeconds + }); + } + // get video duration + const videoElem = this.props.videoRef.current; + const tmpDesiredCurrentTime = videoElem.currentTime - this.state.rollBackValueInSeconds; // > 0 < duration of video - this.setCurrentTime(tmpDesiredCurrentTime); - } + this.setCurrentTime(tmpDesiredCurrentTime); }; handlePlayBackRateChange = e => { @@ -184,34 +183,30 @@ class MediaPlayer extends React.Component { * @param {float} input - playback rate value as a float */ setPlayBackRate = input => { - if (this.props.videoRef.current !== null) { - if (input >= 0.2 && input <= 3.5) { - this.setState( - { - playbackRate: input - }, - () => { - this.props.videoRef.current.playbackRate = input; - - if (this.props.handleAnalyticsEvents) { - this.props.handleAnalyticsEvents({ - category: 'MediaPlayer', - action: 'setPlayBackRate', - name: 'playbackRateNewValue', - value: input - }); - } + if (input >= 0.2 && input <= 3.5) { + this.setState( + { + playbackRate: input + }, + () => { + this.props.videoRef.current.playbackRate = input; + + if (this.props.handleAnalyticsEvents) { + this.props.handleAnalyticsEvents({ + category: 'MediaPlayer', + action: 'setPlayBackRate', + name: 'playbackRateNewValue', + value: input + }); } - ); - } + } + ); } }; decreasePlaybackRate = () => { const speeds = [ ...PLAYBACK_RATES ].reverse(); - const slower = speeds.find(option => { - return option.value < this.state.playbackRate; - }); + const slower = speeds.find(option => option.value < this.state.playbackRate); const newSpeed = slower ? slower.value : 0.2; this.setPlayBackRate(newSpeed); @@ -219,48 +214,36 @@ class MediaPlayer extends React.Component { increasePlaybackRate = () => { const speeds = [ ...PLAYBACK_RATES ]; - const faster = speeds.find(option => { - return option.value > this.state.playbackRate; - }); + const faster = speeds.find(option => option.value > this.state.playbackRate); const newSpeed = faster ? faster.value : 3.5; this.setPlayBackRate(newSpeed); }; handleChangeReplayRollbackValue = e => { - if (this.props.videoRef.current !== null) { - this.setState({ - rollBackValueInSeconds: e.target.value - }); - } + this.setState({ + rollBackValueInSeconds: e.target.value + }); }; handleMuteVolume = () => { - if (this.props.videoRef.current !== null) { - if (this.props.videoRef.current.volume > 0) { - this.props.videoRef.current.volume = 0; - this.setState({ isMute: true }); - } else { - this.props.videoRef.current.volume = 1; - this.setState({ isMute: false }); - } + if (this.props.videoRef.current.volume > 0) { + this.props.videoRef.current.volume = 0; + this.setState({ isMute: true }); + } else { + this.props.videoRef.current.volume = 1; + this.setState({ isMute: false }); } }; // TEMP: keeping this in for now. Might be replaced by state // The pauseWhileTyping logic (in TimedTextEditor) currently uses this isPlaying = () => { - if (this.props.videoRef.current !== null) { - if (this.props.videoRef.current.paused) return false; - - return true; - } + return !this.props.videoRef.current.paused; }; pauseMedia = () => { - this.setState({ isPlaying: false }, () => - this.props.videoRef.current.pause() - ); + this.setState({ isPlaying: false }, () => this.props.videoRef.current.pause()); if (this.props.handleAnalyticsEvents) { this.props.handleAnalyticsEvents({ @@ -273,9 +256,7 @@ class MediaPlayer extends React.Component { }; playMedia = () => { - this.setState({ isPlaying: true }, () => - this.props.videoRef.current.play() - ); + this.setState({ isPlaying: true }, () => this.props.videoRef.current.play()); if (this.props.handleAnalyticsEvents) { this.props.handleAnalyticsEvents({ @@ -290,33 +271,27 @@ class MediaPlayer extends React.Component { // Sets isPlaying state and toggles modes on the video player // TODO: modularise these / enable specific play / pause action togglePlayMedia = () => { - if (this.props.videoRef.current !== null) { - if (this.state.isPlaying) { - this.pauseMedia(); - } else { - this.playMedia(); - } + if (this.state.isPlaying) { + this.pauseMedia(); + } else { + this.playMedia(); } }; skipForward = () => { - if (this.props.videoRef.current !== null) { - // TODO track this? - const currentTime = this.props.videoRef.current.currentTime; - const newCurrentTimeIncreased = currentTime + 10; - const newCurrentTime = Number(newCurrentTimeIncreased.toFixed(1)); - this.setCurrentTime(newCurrentTime); - } + const currentTime = this.props.videoRef.current.currentTime; + const newCurrentTimeIncreased = currentTime + 10; + const newCurrentTime = Number(newCurrentTimeIncreased.toFixed(1)); + + this.setCurrentTime(newCurrentTime); }; skipBackward = () => { - // TODO track this? - if (this.props.videoRef.current !== null) { - const currentTime = this.props.videoRef.current.currentTime; - const newCurrentTimeIncreased = currentTime - 10; - const newCurrentTime = Number(newCurrentTimeIncreased.toFixed(1)); - this.setCurrentTime(newCurrentTime); - } + const currentTime = this.props.videoRef.current.currentTime; + const newCurrentTimeIncreased = currentTime - 10; + const newCurrentTime = Number(newCurrentTimeIncreased.toFixed(1)); + + this.setCurrentTime(newCurrentTime); }; handleProgressBarClick = e => { @@ -333,94 +308,66 @@ class MediaPlayer extends React.Component { } }; - getMediaCurrentTime = () => { - if (this.props.videoRef.current !== null) { - return secondsToTimecode( - this.props.videoRef.current.currentTime + this.state.timecodeOffset - ); - } - - return '00:00:00:00'; - }; + getMediaCurrentTime = () => secondsToTimecode(this.props.videoRef.current.currentTime + this.state.timecodeOffset); handlePictureInPicture = () => { - // console.log('this.props.videoRef', this.props.videoRef, this.props.videoRef.current ); - if (this.props.videoRef.current !== undefined) { - if (document.pictureInPictureElement !== undefined) { - // from https://developers.google.com/web/updates/2017/09/picture-in-picture - if (!document.pictureInPictureElement) { - if (this.props.handleAnalyticsEvents) { - this.props.handleAnalyticsEvents({ - category: 'MediaPlayer', - action: 'handlePictureInPicture', - name: 'turning-picture-in-picture-on' - }); - } - - this.props.videoRef.current.requestPictureInPicture().catch(error => { - // Video failed to enter Picture-in-Picture mode. - console.error( - 'Video failed to enter Picture-in-Picture mode', - error - ); - - if (this.props.handleAnalyticsEvents) { - this.props.handleAnalyticsEvents({ - category: 'MediaPlayer', - action: 'handlePictureInPicture', - name: 'turning-picture-in-picture-on-error' - }); - } + if (document.pictureInPictureElement !== undefined) { + // from https://developers.google.com/web/updates/2017/09/picture-in-picture + if (!document.pictureInPictureElement) { + if (this.props.handleAnalyticsEvents) { + this.props.handleAnalyticsEvents({ + category: 'MediaPlayer', + action: 'handlePictureInPicture', + name: 'turning-picture-in-picture-on' }); - } else { + } + + this.props.videoRef.current.requestPictureInPicture().catch(error => { + console.error('Video failed to enter Picture-in-Picture mode', error); + if (this.props.handleAnalyticsEvents) { this.props.handleAnalyticsEvents({ category: 'MediaPlayer', action: 'handlePictureInPicture', - name: 'turning-picture-in-picture-off' + name: 'turning-picture-in-picture-on-error' }); } - document.exitPictureInPicture().catch(error => { - // Video failed to leave Picture-in-Picture mode. - console.error( - 'Video failed to leave Picture-in-Picture mode', - error - ); - if (this.props.handleAnalyticsEvents) { - this.props.handleAnalyticsEvents({ - category: 'MediaPlayer', - action: 'handlePictureInPicture', - name: 'turning-picture-in-picture-off-error' - }); - } - }); - } + }); } else { - alert('Picture in Picture not supported in this browser, try chrome.'); if (this.props.handleAnalyticsEvents) { this.props.handleAnalyticsEvents({ category: 'MediaPlayer', action: 'handlePictureInPicture', - name: 'picture-in-picture-not-supported' + name: 'turning-picture-in-picture-off' }); } + + document.exitPictureInPicture().catch(error => { + console.error('Video failed to leave Picture-in-Picture mode', error); + + if (this.props.handleAnalyticsEvents) { + this.props.handleAnalyticsEvents({ + category: 'MediaPlayer', + action: 'handlePictureInPicture', + name: 'turning-picture-in-picture-off-error' + }); + } + }); + } + } else { + alert('Picture in Picture not supported in this browser, try chrome.'); + if (this.props.handleAnalyticsEvents) { + this.props.handleAnalyticsEvents({ + category: 'MediaPlayer', + action: 'handlePictureInPicture', + name: 'picture-in-picture-not-supported' + }); } } }; - // performance optimization - getProgressBarMax = () => { - return this.props.videoRef.current !== null - ? parseInt(this.props.videoRef.current.duration).toString() - : '100'; - } - - // performance optimization - getProgressBarValue = () => { - return this.props.videoRef.current !== null - ? parseInt(this.props.videoRef.current.currentTime) - : 0; - } + getProgressBarMax = () => parseInt(this.props.videoRef.current.duration).toString(); + getProgressBarValue = () => parseInt(this.props.videoRef.current.currentTime).toString(); render() { const progressBar = ( @@ -457,14 +404,14 @@ class MediaPlayer extends React.Component { this.props.handleSaveTranscript(); } } /> - {this.props.mediaUrl === null ? null : progressBar} + {this.props.mediaUrl ? progressBar : null} ); return (
- {this.props.mediaUrl === null ? null : playerControlsSection} + {this.props.mediaUrl ? playerControlsSection : null}
); diff --git a/packages/components/media-player/index.module.css b/packages/components/media-player/index.module.scss similarity index 69% rename from packages/components/media-player/index.module.css rename to packages/components/media-player/index.module.scss index 47f4b41b..b52c2d56 100644 --- a/packages/components/media-player/index.module.css +++ b/packages/components/media-player/index.module.scss @@ -1,5 +1,3 @@ -@value color-light-grey from '../../config/style-guide/colours.module.css'; - .topSection { background: black; } @@ -15,12 +13,14 @@ margin: auto; padding: 1em; position: relative; + // overflow-x: auto; + // overflow-y: auto; } .title { color: white; - height: 1.2em; - width: 90vw; + line-height: 1.2em; + width: 100%; margin-top: 0; margin-bottom: 0.5em; font-size: 1.2em; @@ -33,12 +33,8 @@ @media (max-width: 768px) { .title { - width: 100%; - margin-top: 0; - margin-bottom: 0.5em; text-align: center; - font-size: 1.2em; - line-height: 1.2em; padding-top: 1em; + white-space: normal; } } diff --git a/packages/components/media-player/src/PauseWhileTyping.js b/packages/components/media-player/src/PauseWhileTyping.js deleted file mode 100644 index e1662942..00000000 --- a/packages/components/media-player/src/PauseWhileTyping.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -class PauseWhileTyping extends React.Component { - render() { - return ( -
-

Pause While Typing

- -
- ); - } -} - -PauseWhileTyping.propTypes = { - handleToggle: PropTypes.func, - isPauseWhileTypingOn: PropTypes.bool -}; - -export default PauseWhileTyping; diff --git a/packages/components/media-player/src/PlaybackRate.js b/packages/components/media-player/src/PlaybackRate.js index 23ecc54a..f4834aa9 100644 --- a/packages/components/media-player/src/PlaybackRate.js +++ b/packages/components/media-player/src/PlaybackRate.js @@ -1,19 +1,17 @@ import React from 'react'; import PropTypes from 'prop-types'; -// import styles from './PlaybackRate.module.css'; +import isEqual from 'react-fast-compare'; + import Select from './Select'; -import style from './PlayerControls/index.module.css'; -class PlaybackRate extends React.Component { +import style from './PlayerControls/index.module.scss'; - // to avoid unnecessary re-renders - shouldComponentUpdate(nextProps) { - if (nextProps.playbackRate !== this.props.playbackRate) { - return true; - } +class PlaybackRate extends React.Component { - return false; + shouldComponentUpdate = (nextProps) => { + return !isEqual(this.props, nextProps); } + render() { return ( { + return !isEqual(this.props, nextProps); } - // as separate function above render for performance handleClick = (e) => { this.props.promptSetCurrentTime(e); } + render() { return (
diff --git a/packages/components/media-player/src/PlayerControls/index.js b/packages/components/media-player/src/PlayerControls/index.js index fd12c5bc..8cdf8acc 100644 --- a/packages/components/media-player/src/PlayerControls/index.js +++ b/packages/components/media-player/src/PlayerControls/index.js @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; - -import style from './index.module.css'; +import isEqual from 'react-fast-compare'; import { faSave, @@ -19,18 +18,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import PlaybackRate from '../PlaybackRate'; import TimeBox from './TimeBox.js'; +import style from './index.module.scss'; + class PlayerControls extends React.Component { shouldComponentUpdate = (nextProps) => { - if (nextProps !== this.props) return true; - - return false; + return !isEqual(this.props, nextProps); } - // to handle backward and forward mouse pressed on btn - // set a 300 ms interval to repeat the - // backward or forward function - // on mouseUp the interval is cleared setIntervalHelperBackward = () => { // this.props.skipBackward(); this.interval = setInterval(() => { @@ -115,7 +110,7 @@ class PlayerControls extends React.Component { diff --git a/packages/components/media-player/src/PlayerControls/index.module.css b/packages/components/media-player/src/PlayerControls/index.module.scss similarity index 55% rename from packages/components/media-player/src/PlayerControls/index.module.css rename to packages/components/media-player/src/PlayerControls/index.module.scss index 76d1be6e..01f619b0 100644 --- a/packages/components/media-player/src/PlayerControls/index.module.css +++ b/packages/components/media-player/src/PlayerControls/index.module.scss @@ -1,4 +1,4 @@ -@value color-light-shilo, color-darkest-grey, color-light-grey, color-labs-red from '../../../../config/style-guide/colours.module.css'; +@import '../../../../config/style-guide/colours.scss'; .playerControls { margin-bottom: 0.5em; @@ -18,11 +18,15 @@ padding: 0.5em; border: 0; color: white; - background: color-darkest-grey; + background: $color-darkest-grey; font-size: 1em; cursor: pointer; margin-right: 0.3rem; margin-top: 0.3rem; + + &:hover { + background: $color-dark-grey; + } } .playBackRate { @@ -33,24 +37,29 @@ cursor: pointer; position: relative; margin-right: 0.3rem; -} -.playBackRate::before { - content: '×'; - position: absolute; - bottom: -2px; - left: 21px; -} + &::before { + content: '×'; + position: absolute; + bottom: -2px; + 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; + & > select { + padding-top: 0.5em; + padding-bottom: 0.5em; + height: 100%; + outline: none; + width: auto; + width: 100%; + color: white; + background-color: $color-darkest-grey; + cursor: pointer; + + &:hover { + background-color: $color-dark-grey; + } + } } .timeBox { @@ -58,32 +67,32 @@ text-align: center; line-height: 48px; padding: 0 1em; - background-color: color-darkest-grey; -} + background-color: $color-darkest-grey; -.currentTime { - color: color-light-shilo; - cursor: pointer; - font-family: 'Lucida Console', monospace; -} + .currentTime { + color: $color-light-shilo; + cursor: pointer; + font-family: 'Lucida Console', monospace; + } -.separator { - color: color-light-grey; - margin: 0 1em; -} + .separator { + color: $color-light-grey; + margin: 0 1em; + } -.duration { - color: white; - font-family: 'Lucida Console', monospace; + .duration { + color: white; + font-family: 'Lucida Console', monospace; + } } @media (max-width: 768px) { .playerControls { - margin: 0 0 10px 0; display: grid; - grid-template-rows: 10% 48px 48px; + grid-template-rows: 10% 3rem 3rem; grid-row-gap: 5px; + margin: 0 0 0.5rem 0; } .playerControls > *:not(:last-child) { @@ -104,57 +113,34 @@ .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; + &::before { + bottom: 30%; + } } .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; + .currentTime { + float: left; + font-size: 0.7em; + line-height: 1em; + } + + .separator { + display: none; + } + + .duration { + color: white; + font-size: 0.7em; + float: right; + line-height: 1em; + } } .pip { diff --git a/packages/components/media-player/src/ProgressBar.js b/packages/components/media-player/src/ProgressBar.js index 43c90352..3ecf4fd8 100644 --- a/packages/components/media-player/src/ProgressBar.js +++ b/packages/components/media-player/src/ProgressBar.js @@ -1,41 +1,43 @@ import React from 'react'; import PropTypes from 'prop-types'; +import isEqual from 'react-fast-compare'; -import style from './ProgressBar.module.css'; +import style from './ProgressBar.module.scss'; class ProgressBar extends React.Component { - - // performance optimization shouldComponentUpdate = (nextProps) => { - if (nextProps !== this.props) { - return true; - } - - return false; + return !isEqual(this.props, nextProps); } - // performance optimization + handleOnChange = (e) => { this.props.buttonClick(e); } render() { return ( - +
+ +
); } } ProgressBar.propTypes = { - value: PropTypes.number, + value: PropTypes.string, max: PropTypes.string, buttonClick: PropTypes.func }; +ProgressBar.defaultProps = { + value: '0', + max: '0', +}; + export default ProgressBar; diff --git a/packages/components/media-player/src/ProgressBar.module.css b/packages/components/media-player/src/ProgressBar.module.css deleted file mode 100644 index f2da5d30..00000000 --- a/packages/components/media-player/src/ProgressBar.module.css +++ /dev/null @@ -1,39 +0,0 @@ -@value color-light-grey, color-labs-red from '../../../config/style-guide/colours.module.css'; - -.bar { - width: 100%; - -webkit-appearance: none; - height: 10px; - background: #747474; - margin: 0px; - outline: none; - cursor: pointer; - position: absolute; - bottom: 0; - left: 0; -} - -.bar::-webkit-slider-thumb { - -webkit-appearance: none; - height: 30px; - width: 16px; - background: color-labs-red; - cursor: pointer; -} - -.bar::-moz-range-thumb { - height: 30px; - width: 16px; - background: color-labs-red; - cursor: pointer; - border: 0; -} - -.bar::-webkit-slider-runnable-track { - width: 100%; - cursor: pointer; -} - -input[type=range]::-moz-focus-outer { - border: 0; -} diff --git a/packages/components/media-player/src/ProgressBar.module.scss b/packages/components/media-player/src/ProgressBar.module.scss new file mode 100644 index 00000000..00b4d9eb --- /dev/null +++ b/packages/components/media-player/src/ProgressBar.module.scss @@ -0,0 +1,82 @@ +@import '../../../config/style-guide/colours.scss'; + +$slider-width-number: 1440; +$slider-width: #{$slider-width-number}px; +$slider-height: 10px; +$background-slider: $color-light-grey; +$bar-slider-filled: $color-labs-red; +$thumb-width: 16px; +$thumb-height: 24px; +$shadow-size: -7px; +$fit-thumb-in-slider: -7px; + +@function strip-units($number) { + @return $number / ($number * 0 + 1); +} + +@function makelongshadow($color, $size) { + $val: 1px 0 0 $size $color; + + @for $i from 1 through $slider-width-number { + $val: #{$val}, #{$i}px 0 0 $size #{$color}; + } + + @return $val; +} + +.wrapper { + width: 100%; + overflow-x: hidden; + position: absolute; + left: 0; + bottom: -16px; +} + +.bar { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: none; + cursor: pointer; + width: 100%; + height: 30px; + margin: 0; + + // Chrome + &::-webkit-slider-runnable-track { + height: $slider-height; + width: $slider-width; + content: ''; + pointer-events: none; + background: $bar-slider-filled; + } + + &::-webkit-slider-thumb { + -webkit-appearance: none; + height: $thumb-height; + width: $thumb-width; + margin-top: $fit-thumb-in-slider; + background: $color-labs-red; + box-shadow: makelongshadow($color-light-grey, $shadow-size); + } + + // Firefox + &::-moz-range-track { + height: $slider-height; + width: $slider-width; + content: ''; + pointer-events: none; + background: $bar-slider-filled; + } + + &::-moz-range-thumb { + -moz-appearance: none; + height: $thumb-height; + width: $thumb-width; + margin-top: $fit-thumb-in-slider; + border: 0; + border-radius: 0; + background: $color-labs-red; + box-shadow: makelongshadow($color-light-grey, $shadow-size); + } +} diff --git a/packages/components/media-player/src/RollBack.js b/packages/components/media-player/src/RollBack.js deleted file mode 100644 index 38707e0e..00000000 --- a/packages/components/media-player/src/RollBack.js +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import styles from './RollBack.module.css'; -class RollBack extends React.Component { - - render() { - return ( -
-

Rollback - { ` x${ this.props.rollBackValueInSeconds } ` } Seconds -

- - -
- - -
- ); - } -} - -RollBack.propTypes = { - rollBackValueInSeconds: PropTypes.number, - handleChangeReplayRollbackValue: PropTypes.func, - rollBack: PropTypes.func -}; - -export default RollBack; diff --git a/packages/components/media-player/src/RollBack.module.css b/packages/components/media-player/src/RollBack.module.css deleted file mode 100644 index 3484ffaa..00000000 --- a/packages/components/media-player/src/RollBack.module.css +++ /dev/null @@ -1,8 +0,0 @@ -.helpText { - margin-top: 0; - margin-bottom: 0.1em; -} - -.rollBackValue { - font-weight: bold; -} diff --git a/packages/components/media-player/src/ScrollIntoView.js b/packages/components/media-player/src/ScrollIntoView.js deleted file mode 100644 index 8642fa13..00000000 --- a/packages/components/media-player/src/ScrollIntoView.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -class ScrollIntoView extends React.Component { - render() { - return ( -
-

ScrollIntoView

- -
- ); - } -} - -ScrollIntoView.propTypes = { - handleToggle: PropTypes.func, - isScrollIntoViewOn: PropTypes.bool -}; - -export default ScrollIntoView; diff --git a/packages/components/media-player/src/Select.js b/packages/components/media-player/src/Select.js index 65bcebef..bdccaa0a 100644 --- a/packages/components/media-player/src/Select.js +++ b/packages/components/media-player/src/Select.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import style from './Select.module.css'; +import style from './Select.module.scss'; class Select extends React.Component { @@ -12,7 +12,12 @@ class Select extends React.Component { }); return ( - {options} ); diff --git a/packages/components/media-player/src/Select.module.css b/packages/components/media-player/src/Select.module.scss similarity index 100% rename from packages/components/media-player/src/Select.module.css rename to packages/components/media-player/src/Select.module.scss diff --git a/packages/components/media-player/src/defaultHotKeys.js b/packages/components/media-player/src/config/defaultHotKeys.js similarity index 98% rename from packages/components/media-player/src/defaultHotKeys.js rename to packages/components/media-player/src/config/defaultHotKeys.js index c5e620a1..e434addb 100644 --- a/packages/components/media-player/src/defaultHotKeys.js +++ b/packages/components/media-player/src/config/defaultHotKeys.js @@ -115,4 +115,5 @@ function returnHotKeys(self) { } }; } -export default returnHotKeys; \ No newline at end of file + +export default returnHotKeys; diff --git a/packages/components/media-player/src/PLAYBACK_RATES.js b/packages/components/media-player/src/config/playbackRates.js similarity index 100% rename from packages/components/media-player/src/PLAYBACK_RATES.js rename to packages/components/media-player/src/config/playbackRates.js diff --git a/packages/components/settings/Toggle/index.module.css b/packages/components/settings/Toggle/index.module.css index fe249f19..5cd6d039 100644 --- a/packages/components/settings/Toggle/index.module.css +++ b/packages/components/settings/Toggle/index.module.css @@ -1,4 +1,4 @@ -@value color-labs-red from '../../../config/style-guide/colours.module.css'; +@import '../../../config/style-guide/colours.scss'; .switchContainer { display: inline-block; @@ -47,11 +47,11 @@ } input:checked + .slider { - background-color: color-labs-red; + background-color: $color-labs-red; } input:focus + .slider { - box-shadow: 0 0 1px color-labs-red; + box-shadow: 0 0 1px $color-labs-red; } input:checked + .slider:before { diff --git a/packages/components/timed-text-editor/WrapperBlock.module.css b/packages/components/timed-text-editor/WrapperBlock.module.css index f3f91eef..5f748632 100644 --- a/packages/components/timed-text-editor/WrapperBlock.module.css +++ b/packages/components/timed-text-editor/WrapperBlock.module.css @@ -1,8 +1,8 @@ -@value color-labs-red, color-light-grey, color-mid-grey, color-dark-grey from '../../config/style-guide/colours.module.css'; +@import '../../config/style-guide/colours.scss'; -/* https://developer.mozilla.org/en-US/docs/Web/CSS/user-select +/* https://developer.mozilla.org/en-US/docs/Web/CSS/user-select TODO: only working in Chrome, not working in Firefox, and Safari - OSX -if selecting text, not showing selection +if selecting text, not showing selection Commented out because it means cannot select speakers and timecode anymore which is the intended default behavior but needs to come with export functionality to export as plain text, word etc.. otherwise user won't be able @@ -12,7 +12,7 @@ to get text out of component with timecodes and speaker names in the interim */ -webkit-user-select: none; -ms-user-select: none; user-select: none; -} +} /* Desktop size */ @media (min-width: 768px) { @@ -36,7 +36,7 @@ to get text out of component with timecodes and speaker names in the interim */ } .speaker { - color: color-mid-grey; + color: $color-mid-grey; font-weight: bold; text-transform: uppercase; text-overflow: ellipsis; @@ -74,7 +74,7 @@ to get text out of component with timecodes and speaker names in the interim */ .speaker { padding-right: 2em; vertical-align: middle; - color: color-mid-grey; + color: $color-mid-grey; font-weight: bold; text-transform: uppercase; text-align: right; diff --git a/packages/components/timed-text-editor/index.module.css b/packages/components/timed-text-editor/index.module.css index cbeabce6..8a3fab73 100644 --- a/packages/components/timed-text-editor/index.module.css +++ b/packages/components/timed-text-editor/index.module.css @@ -1,4 +1,4 @@ -@value color-subt-green, color-darkest-grey, color-labs-red from '../../config/style-guide/colours.module.css'; +@import '../../config/style-guide/colours.scss'; .DraftEditor-root { background: #f9f9f9; diff --git a/packages/components/transcript-editor/index.js b/packages/components/transcript-editor/index.js index fe7daba0..4d2fc253 100644 --- a/packages/components/transcript-editor/index.js +++ b/packages/components/transcript-editor/index.js @@ -439,33 +439,35 @@ class TranscriptEditor extends React.Component { /> ); + const header = ( +
+ ); + return (
- {this.props.mediaUrl === null ? null :
} + {this.props.mediaUrl ? header : null}
+
- {this.props.mediaUrl !== null && - this.props.transcriptData !== null - ? timedTextEditor - : null} + {this.props.mediaUrl && this.props.transcriptData ? timedTextEditor : null}
diff --git a/packages/components/transcript-editor/index.module.css b/packages/components/transcript-editor/index.module.css index 79de0c63..e3f4084b 100644 --- a/packages/components/transcript-editor/index.module.css +++ b/packages/components/transcript-editor/index.module.css @@ -1,4 +1,4 @@ -@value color-subt-green, color-darkest-grey, color-labs-red from '../../config/style-guide/colours.module.css'; +@import '../../config/style-guide/colours.scss'; .container { position: relative; @@ -22,6 +22,24 @@ text-align: left; } +.settingsButton { + line-height: 1em; + margin-left: 0.5em; + background: $color-darkest-grey; + display: inline-block; + border: 0; + color: white; + font-size: 1em; + cursor: pointer; + text-align: center; + width: 2em; + height: 2em; + + &:hover { + background: $color-dark-grey; + } +} + @media (max-width: 768px) { .help { @@ -30,7 +48,7 @@ } .icon { - color: color-labs-red; + color: $color-labs-red; margin-right: 0.5em; } @@ -65,20 +83,6 @@ right: 0; padding: 0; } - - .settingsButton { - line-height: 1em; - margin-left: 0.5em; - background: color-darkest-grey; - display: inline-block; - border: 0; - color: white; - font-size: 1em; - cursor: pointer; - text-align: center; - width: 2em; - height: 2em; - } } /* Ipad */ @@ -107,20 +111,6 @@ top: 0; right: 0; padding: 0; - } - - .settingsButton { - line-height: 1em; - margin-left: 0.5em; - background: color-darkest-grey; - display: inline-block; - border: 0; - color: white; - font-size: 1em; - cursor: pointer; - text-align: center; - width: 2em; - height: 2em; } .keyboardShortcutsButon { diff --git a/packages/components/transcript-editor/src/Header.js b/packages/components/transcript-editor/src/Header.js index f5ee321f..f82139d4 100644 --- a/packages/components/transcript-editor/src/Header.js +++ b/packages/components/transcript-editor/src/Header.js @@ -38,18 +38,14 @@ class Header extends React.Component {