From a176e54f852864651106b252c14cc5faf41d6377 Mon Sep 17 00:00:00 2001 From: Pietro Passarelli - News Labs Date: Wed, 31 Jul 2019 22:48:31 +0100 Subject: [PATCH] implemented export in UI --- .../components/transcript-editor/index.js | 83 ++++++++++++++++- .../transcript-editor/src/ExportOptions.js | 63 +++++++++++++ .../transcript-editor/src/Header.js | 13 ++- .../transcript-editor/src/index.module.css | 90 +++++++++++++++++++ packages/export-adapters/txt/index.js | 5 +- 5 files changed, 250 insertions(+), 4 deletions(-) create mode 100644 packages/components/transcript-editor/src/ExportOptions.js create mode 100644 packages/components/transcript-editor/src/index.module.css diff --git a/packages/components/transcript-editor/index.js b/packages/components/transcript-editor/index.js index 1e0cff6f..0095ad7c 100644 --- a/packages/components/transcript-editor/index.js +++ b/packages/components/transcript-editor/index.js @@ -7,12 +7,28 @@ import Settings from '../settings'; import Shortcuts from '../keyboard-shortcuts'; import { secondsToTimecode } from '../../util/timecode-converter'; import Header from './src/Header.js'; - +import ExportOptions from './src/ExportOptions.js'; import style from './index.module.css'; // TODO: move to another file with tooltip - rename HowDoesThisWork or HelpMessage import HowDoesThisWork from './src/HowDoesThisWork.js'; +const exportOptionsList = [ + { value: 'txt', label: 'Text file' }, + { value: 'txtspeakertimecodes', label: 'Text file - with Speakers and Timecodes' }, + { value: 'docx', label: 'MS Word' }, + { value: 'srt', label: 'Srt - Captions' }, + { value: 'ttml', label: 'TTML - Captions' }, + { value: 'premiereTTML', label: 'TTML for Adobe Premiere - Captions' }, + { value: 'itt', label: 'iTT - Captions' }, + { value: 'csv', label: 'CSV - Captions' }, + { value: 'vtt', label: 'VTT - Captions' }, + { value: 'pre-segment-txt', label: 'Pre segmented txt - Captions' }, + { value: 'json-captions', label: 'Json - Captions' }, + { value: 'draftjs', label: 'Draft Js - json' }, + { value: 'digitalpaperedit', label: 'Digital Paper Edit - Json' } +]; + class TranscriptEditor extends React.Component { constructor(props) { super(props); @@ -24,6 +40,7 @@ class TranscriptEditor extends React.Component { isScrollIntoViewOn: false, showSettings: false, showShortcuts: false, + showExportOptions: false, isPauseWhileTypingOn: true, rollBackValueInSeconds: 15, timecodeOffset: 0, @@ -72,6 +89,9 @@ class TranscriptEditor extends React.Component { if (nextState.showShortcuts !== this.state.showShortcuts) { return true; } + if (nextState.showExportOptions !== this.state.showExportOptions) { + return true; + } if (nextState.isPauseWhileTypingOn !== this.state.isPauseWhileTypingOn) { return true; @@ -280,6 +300,55 @@ class TranscriptEditor extends React.Component { } }; + handleExportToggle = () => { + console.log('handleExportToggle', this.state.showExportOptions); + this.setState(prevState => ({ + showExportOptions: !prevState.showExportOptions + })); + + if (this.props.handleAnalyticsEvents) { + this.props.handleAnalyticsEvents({ + category: 'TranscriptEditor', + action: 'handleExportToggle', + name: 'showExportOptions', + value: !this.state.showExportOptions + }); + } + } + + handleExportOptionsChange = (e) => { + const exportFormat = e.target.value; + console.log(exportFormat); + if (exportFormat !== 'instructions') { + + const fileName = this.props.title ? this.props.title : this.props.mediaUrl; + + const { data, ext } = this.getEditorContent(exportFormat); + let tmpData = data; + if (ext === 'json') { + tmpData = JSON.stringify(data, null, 2); + } + if (ext !== 'docx') { + this.download(tmpData, `${ fileName }.${ ext }`); + } + } + } + + // https://stackoverflow.com/questions/2897619/using-html5-javascript-to-generate-and-save-a-file + download = (content, filename, contentType) => { + const type = contentType || 'application/octet-stream'; + const link = document.createElement('a'); + const blob = new Blob([ content ], { type: type }); + + link.href = window.URL.createObjectURL(blob); + link.download = filename; + // Firefox fix - cannot do link.click() if it's not attached to DOM in firefox + // https://stackoverflow.com/questions/32225904/programmatical-click-on-a-tag-not-working-in-firefox + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + getEditorContent = exportFormat => { const title = this.props.title ? this.props.title : '' ; @@ -381,6 +450,13 @@ class TranscriptEditor extends React.Component { /> ); + const exportOptions = ( + ); + const shortcuts = ( ); @@ -407,18 +483,23 @@ class TranscriptEditor extends React.Component { /> ); + // const export = (

Export

) + return (
{this.props.mediaUrl === null ? null :
}
diff --git a/packages/components/transcript-editor/src/ExportOptions.js b/packages/components/transcript-editor/src/ExportOptions.js new file mode 100644 index 00000000..54b9a052 --- /dev/null +++ b/packages/components/transcript-editor/src/ExportOptions.js @@ -0,0 +1,63 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faWindowClose } from '@fortawesome/free-solid-svg-icons'; + +import style from './index.module.css'; + +class ExportOptions extends React.Component { + + render() { + const btns = this.props.exportOptionsList.map((opt) => { + return (<> +
+ ); + }); + + return ( +
+

Export Options

+
+ +
+ +
+ {btns} +
+
+ ); + } +} + +ExportOptions.propTypes = { + handleExportToggle: PropTypes.func +// showTimecodes: PropTypes.bool, +// showSpeakers: PropTypes.bool, +// timecodeOffset: PropTypes.number, +// handleShowTimecodes: PropTypes.func, +// handleShowSpeakers: PropTypes.func, +// handleSetTimecodeOffset: PropTypes.func, +// handleSettingsToggle: PropTypes.func, +// handlePauseWhileTyping: PropTypes.func, +// handleIsScrollIntoViewChange: PropTypes.func, +// handleRollBackValueInSeconds: PropTypes.func, +// defaultValueScrollSync: PropTypes.bool, +// defaultValuePauseWhileTyping: PropTypes.bool, +// defaultRollBackValueInSeconds: PropTypes.number, +// previewIsDisplayed: PropTypes.bool, +// handlePreviewIsDisplayed: PropTypes.func, +// // previewViewWidth: PropTypes.string, +// handleChangePreviewViewWidth: PropTypes.func, +// handleAnalyticsEvents: PropTypes.func +}; + +export default ExportOptions; diff --git a/packages/components/transcript-editor/src/Header.js b/packages/components/transcript-editor/src/Header.js index fd805bb6..61ffa532 100644 --- a/packages/components/transcript-editor/src/Header.js +++ b/packages/components/transcript-editor/src/Header.js @@ -1,7 +1,8 @@ import React from 'react'; import { faCog, - faKeyboard + faKeyboard, + faShare } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -31,6 +32,7 @@ class Header extends React.Component {
{props.showSettings ? props.settings : null} {props.showShortcuts ? props.shortcuts : null} + {props.showExportOptions ? props.exportOptions : null} {props.tooltip}
); }; diff --git a/packages/components/transcript-editor/src/index.module.css b/packages/components/transcript-editor/src/index.module.css new file mode 100644 index 00000000..67786c38 --- /dev/null +++ b/packages/components/transcript-editor/src/index.module.css @@ -0,0 +1,90 @@ +@value color-darkest-grey from '../../../config/style-guide/colours.module.css'; + +.settings { + position: absolute; + left: 0; + right: 0; + margin: 0 auto; + width: 30%; + min-width: 300px; + min-height: 300px; + text-align: center; + vertical-align: middle; + color: white; + background: #4a4a4a; + padding: 1em; + font-weight: lighter; + z-index: 2; + } + + .header { + margin-top: 0; + margin-bottom: 1em; + } + + .closeButton { + position: absolute; + top: 0; + right: 0; + padding: 1em; + cursor: pointer; + } + + .controlsContainer { + display: flex; + flex-direction: column; + align-content: flex-start; + align-items: center; + margin: 0 auto; + } + + .settingElement { + text-align: left; + align-self: auto; + margin-bottom: 0.5em; + } + + .label { + display: inline-block; + min-width: 200px; + width: 200px; + } + + .rollbackValue { + height: 2em; + width: 48px; + box-sizing: border-box; + border: none; + text-align: center; + font-weight: bold; + margin-right: 16px; + vertical-align: middle; + } + + .timecodeLabel { + display: block; + text-align: center; + } + + +.playerButton { + width: 48px; + height: 48px; + padding: 0.5em; + border: 0; + color: white; + background: color-darkest-grey; + font-size: 1em; + cursor: pointer; + margin-right: 0.3rem; + margin-top: 0.3rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.playerButton { + width: 100%; + height: 48px; + margin: 0; +} diff --git a/packages/export-adapters/txt/index.js b/packages/export-adapters/txt/index.js index a0b1a381..1bc534ee 100644 --- a/packages/export-adapters/txt/index.js +++ b/packages/export-adapters/txt/index.js @@ -18,13 +18,14 @@ he'd ordered and I was really excited about it because I've always loved about t I thought. ``` */ -import { shortTimecode } from '../../util/timecode-converter/'; +// import { shortTimecode } from '../../util/timecode-converter/'; export default (blockData) => { // TODO: to export text without speaker or timecodes use line below // const lines = blockData.blocks.map(paragraph => paragraph.text); const lines = blockData.blocks.map(paragraph => { - return `${ shortTimecode(paragraph.data.words[0].start) }\t${ paragraph.data.speaker }\n${ paragraph.text }`; + // return `${ shortTimecode(paragraph.data.words[0].start) }\t${ paragraph.data.speaker }\n${ paragraph.text }`; + return `${ paragraph.text }`; }); return lines.join('\n\n');