Skip to content

Commit a176e54

Browse files
author
Pietro Passarelli - News Labs
committed
implemented export in UI
1 parent 36aaf0a commit a176e54

File tree

5 files changed

+250
-4
lines changed

5 files changed

+250
-4
lines changed

packages/components/transcript-editor/index.js

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,28 @@ import Settings from '../settings';
77
import Shortcuts from '../keyboard-shortcuts';
88
import { secondsToTimecode } from '../../util/timecode-converter';
99
import Header from './src/Header.js';
10-
10+
import ExportOptions from './src/ExportOptions.js';
1111
import style from './index.module.css';
1212

1313
// TODO: move to another file with tooltip - rename HowDoesThisWork or HelpMessage
1414
import HowDoesThisWork from './src/HowDoesThisWork.js';
1515

16+
const exportOptionsList = [
17+
{ value: 'txt', label: 'Text file' },
18+
{ value: 'txtspeakertimecodes', label: 'Text file - with Speakers and Timecodes' },
19+
{ value: 'docx', label: 'MS Word' },
20+
{ value: 'srt', label: 'Srt - Captions' },
21+
{ value: 'ttml', label: 'TTML - Captions' },
22+
{ value: 'premiereTTML', label: 'TTML for Adobe Premiere - Captions' },
23+
{ value: 'itt', label: 'iTT - Captions' },
24+
{ value: 'csv', label: 'CSV - Captions' },
25+
{ value: 'vtt', label: 'VTT - Captions' },
26+
{ value: 'pre-segment-txt', label: 'Pre segmented txt - Captions' },
27+
{ value: 'json-captions', label: 'Json - Captions' },
28+
{ value: 'draftjs', label: 'Draft Js - json' },
29+
{ value: 'digitalpaperedit', label: 'Digital Paper Edit - Json' }
30+
];
31+
1632
class TranscriptEditor extends React.Component {
1733
constructor(props) {
1834
super(props);
@@ -24,6 +40,7 @@ class TranscriptEditor extends React.Component {
2440
isScrollIntoViewOn: false,
2541
showSettings: false,
2642
showShortcuts: false,
43+
showExportOptions: false,
2744
isPauseWhileTypingOn: true,
2845
rollBackValueInSeconds: 15,
2946
timecodeOffset: 0,
@@ -72,6 +89,9 @@ class TranscriptEditor extends React.Component {
7289
if (nextState.showShortcuts !== this.state.showShortcuts) {
7390
return true;
7491
}
92+
if (nextState.showExportOptions !== this.state.showExportOptions) {
93+
return true;
94+
}
7595

7696
if (nextState.isPauseWhileTypingOn !== this.state.isPauseWhileTypingOn) {
7797
return true;
@@ -280,6 +300,55 @@ class TranscriptEditor extends React.Component {
280300
}
281301
};
282302

303+
handleExportToggle = () => {
304+
console.log('handleExportToggle', this.state.showExportOptions);
305+
this.setState(prevState => ({
306+
showExportOptions: !prevState.showExportOptions
307+
}));
308+
309+
if (this.props.handleAnalyticsEvents) {
310+
this.props.handleAnalyticsEvents({
311+
category: 'TranscriptEditor',
312+
action: 'handleExportToggle',
313+
name: 'showExportOptions',
314+
value: !this.state.showExportOptions
315+
});
316+
}
317+
}
318+
319+
handleExportOptionsChange = (e) => {
320+
const exportFormat = e.target.value;
321+
console.log(exportFormat);
322+
if (exportFormat !== 'instructions') {
323+
324+
const fileName = this.props.title ? this.props.title : this.props.mediaUrl;
325+
326+
const { data, ext } = this.getEditorContent(exportFormat);
327+
let tmpData = data;
328+
if (ext === 'json') {
329+
tmpData = JSON.stringify(data, null, 2);
330+
}
331+
if (ext !== 'docx') {
332+
this.download(tmpData, `${ fileName }.${ ext }`);
333+
}
334+
}
335+
}
336+
337+
// https://stackoverflow.com/questions/2897619/using-html5-javascript-to-generate-and-save-a-file
338+
download = (content, filename, contentType) => {
339+
const type = contentType || 'application/octet-stream';
340+
const link = document.createElement('a');
341+
const blob = new Blob([ content ], { type: type });
342+
343+
link.href = window.URL.createObjectURL(blob);
344+
link.download = filename;
345+
// Firefox fix - cannot do link.click() if it's not attached to DOM in firefox
346+
// https://stackoverflow.com/questions/32225904/programmatical-click-on-a-tag-not-working-in-firefox
347+
document.body.appendChild(link);
348+
link.click();
349+
document.body.removeChild(link);
350+
};
351+
283352
getEditorContent = exportFormat => {
284353
const title = this.props.title ? this.props.title : '' ;
285354

@@ -381,6 +450,13 @@ class TranscriptEditor extends React.Component {
381450
/>
382451
);
383452

453+
const exportOptions = (
454+
<ExportOptions
455+
exportOptionsList={ exportOptionsList }
456+
handleExportOptionsChange={ this.handleExportOptionsChange }
457+
handleExportToggle={ this.handleExportToggle }
458+
/>);
459+
384460
const shortcuts = (
385461
<Shortcuts handleShortcutsToggle={ this.handleShortcutsToggle } />
386462
);
@@ -407,18 +483,23 @@ class TranscriptEditor extends React.Component {
407483
/>
408484
);
409485

486+
// const export = (<h1>Export</h1>)
487+
410488
return (
411489
<div className={ style.container }>
412490
{this.props.mediaUrl === null ? null : <Header
413491
showSettings={ this.state.showSettings }
414492
showShortcuts={ this.state.showShortcuts }
493+
showExportOptions={ this.state.showExportOptions }
415494
settings={ settings }
416495
shortcuts={ shortcuts }
496+
exportOptions={ exportOptions }
417497
tooltip={ HowDoesThisWork }
418498
mediaUrl={ this.props.mediaUrl }
419499
mediaControls={ mediaControls }
420500
handleSettingsToggle={ this.handleSettingsToggle }
421501
handleShortcutsToggle={ this.handleShortcutsToggle }
502+
handleExportToggle={ this.handleExportToggle }
422503
/>}
423504

424505
<div className={ style.grid }>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4+
import { faWindowClose } from '@fortawesome/free-solid-svg-icons';
5+
6+
import style from './index.module.css';
7+
8+
class ExportOptions extends React.Component {
9+
10+
render() {
11+
const btns = this.props.exportOptionsList.map((opt) => {
12+
return (<><button
13+
title={ opt.label }
14+
className={ style.playerButton }
15+
key={ opt.value }
16+
onClick={ this.props.handleExportOptionsChange }
17+
value={ opt.value }>
18+
{opt.label}
19+
</button>
20+
<br/>
21+
</>);
22+
});
23+
24+
return (
25+
<div className={ style.settings }>
26+
<h2 className={ style.header }>Export Options</h2>
27+
<div className={ style.closeButton }
28+
onClick={ this.props.handleExportToggle }
29+
>
30+
<FontAwesomeIcon icon={ faWindowClose } />
31+
</div>
32+
33+
<div className={ style.controlsContainer }>
34+
{btns}
35+
</div>
36+
</div>
37+
);
38+
}
39+
}
40+
41+
ExportOptions.propTypes = {
42+
handleExportToggle: PropTypes.func
43+
// showTimecodes: PropTypes.bool,
44+
// showSpeakers: PropTypes.bool,
45+
// timecodeOffset: PropTypes.number,
46+
// handleShowTimecodes: PropTypes.func,
47+
// handleShowSpeakers: PropTypes.func,
48+
// handleSetTimecodeOffset: PropTypes.func,
49+
// handleSettingsToggle: PropTypes.func,
50+
// handlePauseWhileTyping: PropTypes.func,
51+
// handleIsScrollIntoViewChange: PropTypes.func,
52+
// handleRollBackValueInSeconds: PropTypes.func,
53+
// defaultValueScrollSync: PropTypes.bool,
54+
// defaultValuePauseWhileTyping: PropTypes.bool,
55+
// defaultRollBackValueInSeconds: PropTypes.number,
56+
// previewIsDisplayed: PropTypes.bool,
57+
// handlePreviewIsDisplayed: PropTypes.func,
58+
// // previewViewWidth: PropTypes.string,
59+
// handleChangePreviewViewWidth: PropTypes.func,
60+
// handleAnalyticsEvents: PropTypes.func
61+
};
62+
63+
export default ExportOptions;

packages/components/transcript-editor/src/Header.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React from 'react';
22
import {
33
faCog,
4-
faKeyboard
4+
faKeyboard,
5+
faShare
56
} from '@fortawesome/free-solid-svg-icons';
67
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
78

@@ -31,6 +32,7 @@ class Header extends React.Component {
3132
<header className={ style.header }>
3233
{props.showSettings ? props.settings : null}
3334
{props.showShortcuts ? props.shortcuts : null}
35+
{props.showExportOptions ? props.exportOptions : null}
3436
{props.tooltip}
3537
</header>
3638
<nav className={ style.nav }>
@@ -53,6 +55,15 @@ class Header extends React.Component {
5355
>
5456
<FontAwesomeIcon icon={ faKeyboard } />
5557
</button>
58+
<button
59+
className={ `${ style.settingsButton } ${
60+
style.keyboardShortcutsButon
61+
}` }
62+
title="Export"
63+
onClick={ props.handleExportToggle }
64+
>
65+
<FontAwesomeIcon icon={ faShare } />
66+
</button>
5667
</div>
5768
</>);
5869
};
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
@value color-darkest-grey from '../../../config/style-guide/colours.module.css';
2+
3+
.settings {
4+
position: absolute;
5+
left: 0;
6+
right: 0;
7+
margin: 0 auto;
8+
width: 30%;
9+
min-width: 300px;
10+
min-height: 300px;
11+
text-align: center;
12+
vertical-align: middle;
13+
color: white;
14+
background: #4a4a4a;
15+
padding: 1em;
16+
font-weight: lighter;
17+
z-index: 2;
18+
}
19+
20+
.header {
21+
margin-top: 0;
22+
margin-bottom: 1em;
23+
}
24+
25+
.closeButton {
26+
position: absolute;
27+
top: 0;
28+
right: 0;
29+
padding: 1em;
30+
cursor: pointer;
31+
}
32+
33+
.controlsContainer {
34+
display: flex;
35+
flex-direction: column;
36+
align-content: flex-start;
37+
align-items: center;
38+
margin: 0 auto;
39+
}
40+
41+
.settingElement {
42+
text-align: left;
43+
align-self: auto;
44+
margin-bottom: 0.5em;
45+
}
46+
47+
.label {
48+
display: inline-block;
49+
min-width: 200px;
50+
width: 200px;
51+
}
52+
53+
.rollbackValue {
54+
height: 2em;
55+
width: 48px;
56+
box-sizing: border-box;
57+
border: none;
58+
text-align: center;
59+
font-weight: bold;
60+
margin-right: 16px;
61+
vertical-align: middle;
62+
}
63+
64+
.timecodeLabel {
65+
display: block;
66+
text-align: center;
67+
}
68+
69+
70+
.playerButton {
71+
width: 48px;
72+
height: 48px;
73+
padding: 0.5em;
74+
border: 0;
75+
color: white;
76+
background: color-darkest-grey;
77+
font-size: 1em;
78+
cursor: pointer;
79+
margin-right: 0.3rem;
80+
margin-top: 0.3rem;
81+
white-space: nowrap;
82+
overflow: hidden;
83+
text-overflow: ellipsis;
84+
}
85+
86+
.playerButton {
87+
width: 100%;
88+
height: 48px;
89+
margin: 0;
90+
}

packages/export-adapters/txt/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ he'd ordered and I was really excited about it because I've always loved about t
1818
I thought.
1919
```
2020
*/
21-
import { shortTimecode } from '../../util/timecode-converter/';
21+
// import { shortTimecode } from '../../util/timecode-converter/';
2222

2323
export default (blockData) => {
2424
// TODO: to export text without speaker or timecodes use line below
2525
// const lines = blockData.blocks.map(paragraph => paragraph.text);
2626
const lines = blockData.blocks.map(paragraph => {
27-
return `${ shortTimecode(paragraph.data.words[0].start) }\t${ paragraph.data.speaker }\n${ paragraph.text }`;
27+
// return `${ shortTimecode(paragraph.data.words[0].start) }\t${ paragraph.data.speaker }\n${ paragraph.text }`;
28+
return `${ paragraph.text }`;
2829
});
2930

3031
return lines.join('\n\n');

0 commit comments

Comments
 (0)