diff options
Diffstat (limited to 'animism-align/frontend/app/views/editor/align')
13 files changed, 2 insertions, 879 deletions
diff --git a/animism-align/frontend/app/views/editor/align/align.container.js b/animism-align/frontend/app/views/editor/align/align.container.js index 46954e8..d61eccd 100644 --- a/animism-align/frontend/app/views/editor/align/align.container.js +++ b/animism-align/frontend/app/views/editor/align/align.container.js @@ -2,10 +2,9 @@ import React, { Component } from 'react' import { connect } from 'react-redux' import './align.css' -import './sidebar.css' -import Timeline from './containers/timeline.container.js' -import Sidebar from './containers/sidebar.container.js' +import Timeline from 'app/views/editor/timeline/timeline.container.js' +import Sidebar from 'app/views/editor/sidebar/sidebar.container.js' class Container extends Component { componentDidMount() { diff --git a/animism-align/frontend/app/views/editor/align/align.css b/animism-align/frontend/app/views/editor/align/align.css index 58adcf8..83d3777 100644 --- a/animism-align/frontend/app/views/editor/align/align.css +++ b/animism-align/frontend/app/views/editor/align/align.css @@ -22,79 +22,6 @@ height: calc(100% - 3rem); } -/* Timeline */ - -canvas { - display: block; -} -.timeline { - display: flex; - flex-direction: row; - position: relative; - width: 300px; - cursor: crosshair; -} -.timelineColumn { - position: relative; -} -.ticks .tick { - position: absolute; - right: 0; - width: 4px; - height: 1px; - background: #ddd; - pointer-events: none; -} -.ticks .tickLabel { - pointer-events: none; - position: absolute; - right: 6px; - font-size: 12px; - width: 40px; - margin-top: -7px; - text-align: right; - text-shadow: 0 0 2px #00f; -} -.timeline .cursor { - width: 100%; - position: absolute; - left: 0; - pointer-events: none; -} -.timeline .cursor .line { - width: 100%; - height: 1px; - background: #00f; -} -.timeline .cursor.playCursor .line { - background: #ddd; -} -.timeline .cursor .tickLabel { - position: absolute; - pointer-events: none; - right: 6px; - font-size: 12px; - width: 40px; - margin-top: -7px; - text-align: right; - text-shadow: 0 0 2px #000, 0 0 2px #000, 0 0 2px #000; - user-select: none; -} -.timeline .cursor_region { - position: absolute; - left: 0; - width: 100%; - border-top: 1px solid #33f; - border-bottom: 1px solid #33f; - background: rgba(32,64,255,0.2); -} -.timeline .cursor_region .tickLabel { - position: absolute; - top: 2px; - left: 6px; - user-select: none; -} - /* Audio player */ .playButton { diff --git a/animism-align/frontend/app/views/editor/align/components/sidebar/script.component.js b/animism-align/frontend/app/views/editor/align/components/sidebar/script.component.js deleted file mode 100644 index d23b3da..0000000 --- a/animism-align/frontend/app/views/editor/align/components/sidebar/script.component.js +++ /dev/null @@ -1,22 +0,0 @@ -import React, { Component } from 'react' -// import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -import actions from 'app/actions' - -const Script = ({ text }) => { - if (text.loading) return null - return ( - <textarea - className='script' - onChange={e => actions.site.updateText(e.target.value)} - value={text} - /> - ) -} - -const mapStateToProps = state => ({ - text: state.align.text, -}) - -export default connect(mapStateToProps)(Script) diff --git a/animism-align/frontend/app/views/editor/align/components/sidebar/tableOfContents.component.js b/animism-align/frontend/app/views/editor/align/components/sidebar/tableOfContents.component.js deleted file mode 100644 index 8fb3d54..0000000 --- a/animism-align/frontend/app/views/editor/align/components/sidebar/tableOfContents.component.js +++ /dev/null @@ -1,29 +0,0 @@ -import React, { Component } from 'react' -// import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -import { ROMAN_NUMERALS } from 'app/constants' -import actions from 'app/actions' - -class TableOfContents extends Component { - render() { - const { loading, order, lookup } = this.props.annotation - if (loading || !order) return null - const sectionIds = order.filter(id => lookup[id].type === "section_heading") - return ( - <div className="sidebar-content toc"> - {sectionIds.map((id, i) => ( - <div key={id} onClick={() => actions.align.setScrollPosition(lookup[id].start_ts)}> - {ROMAN_NUMERALS[i]}{'. '}{lookup[id].text} - </div> - ))} - </div> - ) - } -} - -const mapStateToProps = state => ({ - annotation: state.annotation.index, -}) - -export default connect(mapStateToProps)(TableOfContents) diff --git a/animism-align/frontend/app/views/editor/align/components/sidebar/waveUpload.component.js b/animism-align/frontend/app/views/editor/align/components/sidebar/waveUpload.component.js deleted file mode 100644 index 27047ef..0000000 --- a/animism-align/frontend/app/views/editor/align/components/sidebar/waveUpload.component.js +++ /dev/null @@ -1,157 +0,0 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' -import extractPeaks from 'webaudio-peaks' - -import actions from 'app/actions' -import { formatSize, timestampHMS } from 'app/utils' -import { Loader } from 'app/common' - -class WaveUpload extends Component { - state = { - working: false, - status: "", - filename: "", - duration: 0, - } - - upload(e) { - e.preventDefault() - document.body.className = '' - const files = e.dataTransfer ? e.dataTransfer.files : e.target.files - let i - let file - for (i = 0; i < files.length; i++) { - file = files[i] - if (file && file.type.match('image.*')) break - } - if (!file) { - console.log('No file specified') - return - } - this.setState({ working: true, status: "Loading MP3...", filename: file.name, size: file.size, duration: 0 }) - const fileReader = new FileReader() - fileReader.onload = event => { - fileReader.onload = null - this.processAudioFile(file, event.target.result) - } - fileReader.readAsArrayBuffer(file) - } - - processAudioFile(audioFile, arrayBuffer) { - this.setState({ working: true, status: "Extracting peaks..." }) - var audioContext = new (window.AudioContext || window.webkitAudioContext)(); - audioContext.decodeAudioData(arrayBuffer, (audioBuffer) => { - // buffer, samplesPerPixel, isMono, startOffset, endOffset, bitResolution - this.setState({ duration: audioBuffer.duration }) - var peaks = extractPeaks(audioBuffer, 441, true); - console.log(peaks) - const array = Array.from(peaks.data[0]) - const peaksBlob = new Blob([ JSON.stringify(array) ], {type: "application/json"}); - this.uploadAudioAndPeaks(audioFile, peaksBlob) - }) - } - - uploadAudioAndPeaks(audioFile, peaksBlob) { - const { episode } = this.props - const updatedEpisode = { ...episode } - this.setState({ status: "Removing old files..." }) - this.destroyTaggedFile('peaks') - this.destroyTaggedFile('audio') - .then(() => { - return this.uploadTaggedFile(peaksBlob, 'peaks', 'episode-' + this.props.episode.id + '-peaks.json') - }) - .then(peaksResult => { - updatedEpisode.settings.peaks = peaksResult - return this.uploadTaggedFile(audioFile, 'audio', this.state.filename) - }) - .then(audioResult => { - updatedEpisode.settings.audio = audioResult - return actions.episode.update(updatedEpisode) - }) - .then(res => { - this.setState({ status: "Upload complete", working: false }) - }) - } - - uploadTaggedFile(file, tag, fn) { - return new Promise((resolve, reject) => { - this.setState({ status: "Uploading " + tag + "..." }) - const uploadData = { - tag, - file, - __file_filename: fn, - username: this.props.currentUser.username, - } - console.log(uploadData) - return actions.upload.upload(uploadData).then(data => { - console.log(data) - resolve(data.res) - }) - }) - } - - destroyTaggedFile(tag) { - return new Promise((resolve, reject) => { - if (!this.props.episode.settings[tag]) { - return resolve(); - } - actions.upload.destroy(this.props.episode.settings[tag]) - .then(() => { - console.log('Destroy successful') - resolve() - }) - .catch(() => { - console.log('Error deleting the image') - reject() - }) - }) - } - - render() { - const { episode, peaks } = this.props - return ( - <div className="sidebar-content wave-upload"> - {peaks.length && ( - <div> - Peaks: {peaks.length} - </div> - )} - <div className="uploadButton"> - <button> - <span> - {episode.settings.audio - ? "Upload a new audio file" - : "Upload an audio file" - } - </span> - </button> - <input - type="file" - accept="audio/mp3" - onChange={this.upload.bind(this)} - required={this.props.required} - /> - </div> - <small>Upload an MP3, encoded 192kbit constant bitrate, 44.1kHz stereo</small> - {this.state.status && ( - <div className='status'> - {this.state.working && <Loader />} - <div className='status-message'>{this.state.status}</div> - {this.state.filename && <small>{this.state.filename}</small>} - {this.state.size && <small>{'Size: '}{formatSize(this.state.size)}</small>} - {!!this.state.duration && <small>{'Duration: '}{timestampHMS(this.state.duration)}</small>} - </div> - )} - </div> - ) - } -} - -const mapStateToProps = state => ({ - peaks: state.align.peaks, - currentUser: state.auth.user, - project: state.site.project, - episode: state.site.episode, -}) - -export default connect(mapStateToProps)(WaveUpload) diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/cursor.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/cursor.component.js deleted file mode 100644 index 4a94100..0000000 --- a/animism-align/frontend/app/views/editor/align/components/timeline/cursor.component.js +++ /dev/null @@ -1,26 +0,0 @@ -import React, { Component } from 'react' - -import { ZOOM_STEPS } from 'app/constants' -import { timestamp } from 'app/utils' - -const Cursor = ({ timeline, annotation }) => { - const { start_ts, zoom, cursor_ts, duration } = timeline - const ts = annotation.start_ts || cursor_ts - const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 - const y = (ts - start_ts) / secondsPerPixel - return ( - <div - className='cursor' - style={{ - top: y, - }} - > - <div className='line' /> - <div className='tickLabel'> - {timestamp(ts, 1)} - </div> - </div> - ) -} - -export default Cursor
\ No newline at end of file diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/cursorRegion.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/cursorRegion.component.js deleted file mode 100644 index a0c9bd7..0000000 --- a/animism-align/frontend/app/views/editor/align/components/timeline/cursorRegion.component.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react' - -import { ZOOM_STEPS } from 'app/constants' -import { timestamp } from 'app/utils' - -const CursorRegion = ({ timeline }) => { - const { start_ts, zoom, cursor_region } = timeline - if (!cursor_region) return null - const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 - const duration = cursor_region.b_ts - cursor_region.a_ts - const y = (cursor_region.a_ts - start_ts) / secondsPerPixel - const height = (duration) / secondsPerPixel - return ( - <div - className='cursor_region' - style={{ - top: y, - height, - }} - > - <div className='tickLabel'> - {timestamp(duration, 1, true)} - </div> - </div> - ) -} - -export default CursorRegion
\ No newline at end of file diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/playCursor.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/playCursor.component.js deleted file mode 100644 index 80da31f..0000000 --- a/animism-align/frontend/app/views/editor/align/components/timeline/playCursor.component.js +++ /dev/null @@ -1,31 +0,0 @@ -import React, { Component } from 'react' -// import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -import { ZOOM_STEPS } from 'app/constants' -import { timestamp } from 'app/utils' - -const PlayCursor = ({ timeline, audio }) => { - const { start_ts, zoom, duration } = timeline - const { play_ts } = audio - const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 - const y = (play_ts - start_ts) / secondsPerPixel - // console.log(play_ts, y) - return ( - <div - className='cursor playCursor' - style={{ - top: y, - }} - > - <div className='line' /> - </div> - ) -} - -const mapStateToProps = state => ({ - timeline: state.align.timeline, - audio: state.audio, -}) - -export default connect(mapStateToProps)(PlayCursor) diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/ticks.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/ticks.component.js deleted file mode 100644 index 4530863..0000000 --- a/animism-align/frontend/app/views/editor/align/components/timeline/ticks.component.js +++ /dev/null @@ -1,88 +0,0 @@ -import React, { Component } from 'react' - -import { ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS, INNER_HEIGHT } from 'app/constants' -import { timestamp } from 'app/utils' - -export default class Ticks extends Component { - render() { - let { start_ts, zoom, duration } = this.props.timeline - - let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step - - let widthTimeDuration = INNER_HEIGHT * secondsPerPixel // secs per pixel - - let timeMin = start_ts - let timeMax = Math.min(start_ts + widthTimeDuration, duration) - let timeWidth = timeMax - timeMin - - let pixelMin = timeMin / secondsPerPixel - - let secondsPerLabel = ZOOM_LABEL_STEPS[zoom] // secs - let pixelsPerLabel = secondsPerLabel / secondsPerPixel - let secondsPerTick = ZOOM_TICK_STEPS[zoom] - let pixelsPerTick = secondsPerTick / secondsPerPixel - - let startOffset = pixelsPerLabel - (pixelMin % pixelsPerLabel) - let startTiming = (pixelMin + startOffset) * secondsPerPixel - - let labelCount = Math.ceil(INNER_HEIGHT / pixelsPerLabel) + 1 - let offset, timing, tickLabels = [], ticks = [] - for (var i = -1; i < labelCount; i++) { - offset = i * pixelsPerLabel + startOffset - if (offset > INNER_HEIGHT) continue - timing = i * secondsPerLabel + startTiming - if (timing > duration) { - break - } - tickLabels.push( - <div className='tickLabel' key={"tickLabel_" + i} - style={{ - top: Math.floor(offset) - }}> - {timestamp(timing)} - </div> - ) - } - - let durationOffset = duration / secondsPerPixel - pixelMin - if (timing > duration) { - tickLabels.push( - <div className='tickLabel tickLabelTotal' key={"tickLabel_total"} - style={{ - top: durationOffset - }}> - {timestamp(duration, 1)} - </div> - ) - ticks.push( - <div className='tick' key={"tick_total"} - style={{ - top: Math.floor(durationOffset), - }} - /> - ) - } - let tickCount = Math.ceil(INNER_HEIGHT / pixelsPerTick) + 6 - for (var i = 0; i < tickCount; i += 1) { - offset = i * pixelsPerTick + startOffset - pixelsPerLabel - if (offset > durationOffset) { - break - } - ticks.push( - <div className='tick' key={"tick_" + i} - style={{ - top: Math.floor(offset), - }} - /> - ) - } - // console.log(ticks.length) - - return ( - <div className='ticks'> - {ticks} - {tickLabels} - </div> - ) - } -} diff --git a/animism-align/frontend/app/views/editor/align/components/timeline/waveform.component.js b/animism-align/frontend/app/views/editor/align/components/timeline/waveform.component.js deleted file mode 100644 index 0161129..0000000 --- a/animism-align/frontend/app/views/editor/align/components/timeline/waveform.component.js +++ /dev/null @@ -1,100 +0,0 @@ -import React, { Component } from 'react' -// import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -import actions from 'app/actions' - -import { - WAVEFORM_SIZE, INNER_HEIGHT, - ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS, -} from 'app/constants' - -class Waveform extends Component { - constructor(props){ - super(props) - this.canvasRef = React.createRef() - } - componentDidMount() { - this.resize() - this.draw() - } - componentDidUpdate() { - this.draw() - } - resize() { - const canvas = this.canvasRef.current - canvas.width = WAVEFORM_SIZE - canvas.height = INNER_HEIGHT - } - draw() { - const canvas = this.canvasRef.current - const ctx = canvas.getContext('2d') - const h = INNER_HEIGHT - this.clearCanvas(ctx, h) - this.drawCurve(ctx, h) - } - clearCanvas(ctx, h) { - const w = WAVEFORM_SIZE - ctx.clearRect(0, 0, w, h) - ctx.fillStyle = 'rgba(0,0,0,0.5)' - ctx.fillRect(0, 0, w, h) - ctx.fillStyle = 'rgba(64,128,192,0.5)' - } - drawCurve(ctx, h) { - let { peaks, timeline } = this.props - let { start_ts, zoom, duration } = timeline - - let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step - let stepsPerPixel = ZOOM_STEPS[zoom] // 0.1 sec / step - - let widthTimeDuration = h * secondsPerPixel // secs per pixel - - let timeMin = Math.round(start_ts / secondsPerPixel) * secondsPerPixel - let timeMax = Math.min(timeMin + widthTimeDuration, duration) - let timeWidth = timeMax - timeMin - - let stepMin = Math.floor(timeMin * 10) - let pixelWidth = Math.ceil(timeWidth / secondsPerPixel) - - let i = 0 - let step = stepMin - let waveformPeak = WAVEFORM_SIZE / 2 - let origin = (1 - peaks[step]) * waveformPeak - let y - let peak - // console.log(stepMin, pixelWidth * stepsPerPixel + stepMin) - ctx.beginPath() - ctx.moveTo(origin, 0) - for (i = 0; i < pixelWidth; i++) { - step = i * stepsPerPixel + stepMin - peak = peaks[step] - y = (1 - peak) * waveformPeak - ctx.lineTo(y, i) - } - for (i = pixelWidth - 1; i > 0; i--) { - step = i * stepsPerPixel + stepMin - peak = peaks[step] - y = (1 + peak) * waveformPeak - ctx.lineTo(y, i) - } - ctx.lineTo(origin, 0) - ctx.fillStyle = 'rgba(255,255,255,0.8)' - ctx.fill() - } - render() { - return ( - <canvas - ref={this.canvasRef} - onMouseDown={this.props.onMouseDown} - onMouseUp={this.props.onMouseUp} - /> - ) - } -} - -const mapStateToProps = state => ({ - timeline: state.align.timeline, - peaks: state.align.peaks, -}) - -export default connect(mapStateToProps)(Waveform) diff --git a/animism-align/frontend/app/views/editor/align/containers/sidebar.container.js b/animism-align/frontend/app/views/editor/align/containers/sidebar.container.js deleted file mode 100644 index 8ff8181..0000000 --- a/animism-align/frontend/app/views/editor/align/containers/sidebar.container.js +++ /dev/null @@ -1,42 +0,0 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' -// import { Link } from 'react-router-dom' - -import actions from 'app/actions' - -import Script from '../components/sidebar/script.component.js' -import TableOfContents from '../components/sidebar/tableOfContents.component.js' -import WaveUpload from '../components/sidebar/waveUpload.component.js' - -class Sidebar extends Component { - state = { - mode: "toc", - } - componentDidMount(){ - if (!this.props.peaks.length) { - this.setState({ mode: "wav" }) - } - } - - render() { - const { mode } = this.state - return ( - <div className='sidebar'> - <div className='buttons'> - <button onClick={() => this.setState({ mode: "txt" })}>text</button> - <button onClick={() => this.setState({ mode: "wav" })}>wav</button> - <button onClick={() => this.setState({ mode: "toc" })}>contents</button> - </div> - {mode === 'toc' && <TableOfContents />} - {mode === 'txt' && <Script />} - {mode === 'wav' && <WaveUpload />} - </div> - ) - } -} - -const mapStateToProps = state => ({ - peaks: state.align.peaks, -}) - -export default connect(mapStateToProps)(Sidebar) diff --git a/animism-align/frontend/app/views/editor/align/containers/timeline.container.js b/animism-align/frontend/app/views/editor/align/containers/timeline.container.js deleted file mode 100644 index dbef60f..0000000 --- a/animism-align/frontend/app/views/editor/align/containers/timeline.container.js +++ /dev/null @@ -1,196 +0,0 @@ -import React, { Component } from 'react' -// import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -import actions from 'app/actions' - -import Annotations from 'app/views/editor/annotation/annotations.container' -import Waveform from '../components/timeline/waveform.component' -import Ticks from '../components/timeline/ticks.component' -import Cursor from '../components/timeline/cursor.component' -import PlayCursor from '../components/timeline/playCursor.component' -import CursorRegion from '../components/timeline/cursorRegion.component' - -import { WAVEFORM_SIZE, ZOOM_STEPS, INNER_HEIGHT } from 'app/constants' -import { clamp } from 'app/utils' -import { positionToTime } from 'app/utils/align.utils' - -class Timeline extends Component { - state = { - dragging: false, - a_ts: -1, - } - constructor(props){ - super(props) - this.handleKeydown = this.handleKeydown.bind(this) - this.handleMouseMove = this.handleMouseMove.bind(this) - this.handleWheel = this.handleWheel.bind(this) - this.handleContainerClick = this.handleContainerClick.bind(this) - this.handleTimelineMouseDown = this.handleTimelineMouseDown.bind(this) - this.handleTimelineMouseUp = this.handleTimelineMouseUp.bind(this) - } - componentDidMount() { - this.bind() - } - componentWillUnmount() { - this.unbind() - } - shouldComponentUpdate(nextProps) { - return ( - nextProps.timeline !== this.props.timeline || - nextProps.annotation !== this.props.annotation - ) - } - bind() { - document.addEventListener('keydown', this.handleKeydown) - } - unbind() { - document.removeEventListener('keydown', this.handleKeydown) - } - handleKeydown(e) { - if (document.activeElement !== document.body) { - return - } - console.log(e.keyCode) - if (e.metaKey && this.props.selectedAnnotation.id) { - const { selectedAnnotation } = this.props - switch (e.keyCode) { - case 38: // up - e.preventDefault() - selectedAnnotation.start_ts = Math.max(selectedAnnotation.start_ts - (e.shiftKey ? 1 : 0.1), 0) - actions.align.updateSelectedAnnotation(selectedAnnotation) - actions.audio.seek(selectedAnnotation.start_ts) - actions.align.setCursor(selectedAnnotation.start_ts) - break - case 40: // down - e.preventDefault() - selectedAnnotation.start_ts += e.shiftKey ? 1 : 0.1 - actions.align.updateSelectedAnnotation(selectedAnnotation) - actions.audio.seek(selectedAnnotation.start_ts) - actions.align.setCursor(selectedAnnotation.start_ts) - break - case 68: // D - e.preventDefault() - actions.align.cloneSelectedAnnotation(selectedAnnotation) - } - return - } - if (e.shiftKey) { - switch (e.keyCode) { - case 187: // plus - actions.align.setZoom(this.props.timeline.zoom - 1) - break - case 189: // minus - actions.align.setZoom(this.props.timeline.zoom + 1) - break - } - } else { - // console.log(e.keyCode) - switch (e.keyCode) { - case 27: // escape - actions.align.hideAnnotationForm() - break - case 65: // A - add - e.preventDefault() - actions.align.showNewAnnotationForm(this.props.audio.play_ts, this.props.text) - break - case 32: // spacebar - actions.audio.toggle() - break - case 38: // up - actions.audio.jump(- ZOOM_STEPS[this.props.timeline.zoom] * 0.1) - break - case 40: // down - actions.audio.jump(ZOOM_STEPS[this.props.timeline.zoom] * 0.1) - break - } - } - } - handleWheel(e) { - let { start_ts, zoom, duration } = this.props.timeline - let { deltaY } = e - let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step - let widthTimeDuration = INNER_HEIGHT * secondsPerPixel // secs per pixel - start_ts += Math.round((deltaY) * ZOOM_STEPS[zoom]) - start_ts = clamp(start_ts, 0, Math.max(0, duration - widthTimeDuration / 2)) - if (e.shiftKey) { - if (Math.abs(deltaY) < 2) return - if (e.deltaY > 0) { - actions.align.throttledSetZoom(this.props.timeline.zoom + 1) - } else { - actions.align.throttledSetZoom(this.props.timeline.zoom - 1) - } - } else if (e.altKey) { - actions.audio.jump(e.deltaY * ZOOM_STEPS[zoom]) - } else { - actions.align.setScrollPosition(start_ts) - } - } - handleContainerClick(e) { - actions.align.clearSelectedAnnotation() - actions.align.clearSelectedParagraph() - } - handleTimelineMouseDown(e) { - const cursor_ts = positionToTime(e.pageY, this.props.timeline) - actions.align.clearCursorRegion() - actions.align.setCursor(cursor_ts) - this.setState({ - dragging: true, - a_ts: cursor_ts, - }) - } - handleMouseMove(e) { - const cursor_ts = positionToTime(e.pageY, this.props.timeline) - if (this.state.dragging) { - actions.align.setCursorRegion( - Math.min(this.state.a_ts, cursor_ts), - Math.max(this.state.a_ts, cursor_ts), - ) - } else { - actions.align.setCursor(cursor_ts) - } - } - handleTimelineMouseUp(e) { - this.setState({ dragging: false }) - const play_ts = positionToTime(e.pageY, this.props.timeline) - if (e.metaKey) { - actions.align.spliceTime(play_ts) - } else if (e.pageX < WAVEFORM_SIZE * 0.67) { - actions.audio.seek(play_ts) - } else { - actions.align.showNewAnnotationForm(play_ts, this.props.text) - } - } - render() { - return ( - <div - className='timeline' - onClick={this.handleContainerClick} - onWheel={this.handleWheel} - onMouseMove={this.handleMouseMove} - > - <div className='timelineColumn'> - <Waveform - onMouseDown={this.handleTimelineMouseDown} - onMouseUp={this.handleTimelineMouseUp} - /> - <Ticks timeline={this.props.timeline} /> - <Cursor timeline={this.props.timeline} annotation={this.props.annotation} /> - <CursorRegion timeline={this.props.timeline} /> - </div> - <Annotations timeline={this.props.timeline} /> - <PlayCursor /> - </div> - ) - } -} - -const mapStateToProps = state => ({ - timeline: state.align.timeline, - annotation: state.align.annotation, - selectedAnnotation: state.align.selectedAnnotation, - audio: state.audio, - text: state.align.text, -}) - -export default connect(mapStateToProps)(Timeline) diff --git a/animism-align/frontend/app/views/editor/align/sidebar.css b/animism-align/frontend/app/views/editor/align/sidebar.css deleted file mode 100644 index 7c19797..0000000 --- a/animism-align/frontend/app/views/editor/align/sidebar.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Sidebar */ - -.sidebar { - position: absolute; - top: 0; - right: 0; - width: 15rem; - z-index: 8; - padding-top: 2rem; -} - -/* sidebar header */ - -.sidebar .buttons { - position: absolute; - top: 0; - right: 0; - width: 15rem; - height: 2rem; - z-index: 9; - display: flex; - justify-content: flex-end; - align-items: flex-start; - background: #111; -} -.sidebar .buttons button { - border: 0; - background: transparent; -} -.sidebar .buttons button:hover { - background: #333; -} -.sidebar-content { - background: #222; - width: 15rem; - padding: 0.5rem 0; -} - -/* wavefile upload */ - -.sidebar-content.wave-upload { - padding: 0.5rem 0.75rem 1.5rem 0.75rem; -} - -.wave-upload .uploadButton { - position: relative; - text-align: center; - margin: 1rem 0; -} -.wave-upload small { - display: block; - text-align: center; -} -.wave-upload .status { - padding: 1rem 0 0 0; - text-align: center; -} -.wave-upload .circular-loader { - margin: 0 auto 1rem auto; -} -.wave-upload .status-message { -} -.wave-upload .status small { - display: block; - margin-top: 0.25rem; -} - -/* table of contents */ - -.toc div { - width: 15rem; - padding: 0.25rem 0.75rem; - cursor: pointer; -} -.toc div:hover { - background: #213; -} - - -/* script */ - -.sidebar textarea { - height: calc(100vh - 5.15rem); -} |
