diff options
Diffstat (limited to 'animism-align/frontend/app/views/align/components/annotations/annotation.index.js')
| -rw-r--r-- | animism-align/frontend/app/views/align/components/annotations/annotation.index.js | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/animism-align/frontend/app/views/align/components/annotations/annotation.index.js b/animism-align/frontend/app/views/align/components/annotations/annotation.index.js new file mode 100644 index 0000000..aa31268 --- /dev/null +++ b/animism-align/frontend/app/views/align/components/annotations/annotation.index.js @@ -0,0 +1,126 @@ +import React, { Component } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import actions from 'app/actions' + +import { ZOOM_STEPS, INNER_HEIGHT } from 'app/constants' +import { clamp } from 'app/utils' +import { positionToTime, timeToPosition } from 'app/views/align/align.util' + +import { AnnotationElementLookup } from './annotationTypes' + +class AnnotationIndex extends Component { + state = { + items: [], + } + constructor(props){ + super(props) + this.handleClick = this.handleClick.bind(this) + } + componentDidUpdate(prevProps) { + if (this.props.index.loading) return + if (prevProps.timeline !== this.props.timeline || prevProps.index !== this.props.index) { + this.update() + } + } + update() { + let { timeline, index } = this.props + let { start_ts, zoom, duration } = this.props.timeline + const { order, lookup } = index + + let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step + let widthTimeDuration = INNER_HEIGHT * secondsPerPixel // secs per pixel + + let timeMin = start_ts - 50.0 + let timeMax = Math.min(start_ts + widthTimeDuration, duration) + + const items = order.filter(id => { + const { start_ts: ts } = lookup[id] + return (timeMin < ts && ts < timeMax) + }).map(id => lookup[id]).reverse() + this.setState({ items }) + } + handleClick(e, annotation) { + e.stopPropagation() + if (e.shiftKey) { + e.preventDefault() + this.handleParagraphSelection(annotation, e.metaKey) + } + actions.audio.seek(annotation.start_ts) + actions.align.setSelectedAnnotation(annotation) + } + handleParagraphSelection(annotation, shouldClear) { + const { selected_paragraph_id } = this.props.timeline + if (!selected_paragraph_id || selected_paragraph_id === -1 || shouldClear) { + if (annotation.paragraph_id && !shouldClear) { + actions.align.setSelectedParagraph(annotation.paragraph_id) + } else { + actions.paragraph.create({ + type: 'paragraph', + start_ts: annotation.start_ts, + }).then(data => { + actions.align.setSelectedParagraph(data.res.id) + annotation.paragraph_id = data.res.id + actions.annotation.update(annotation) + }) + } + } else if (selected_paragraph_id !== annotation.paragraph_id) { + annotation.paragraph_id = selected_paragraph_id + actions.annotation.update(annotation) + } + } + handleDoubleClick(e, annotation) { + e.stopPropagation() + actions.align.showEditAnnotationForm(annotation) + } + render() { + const { timeline, media, annotationInForm, selectedAnnotation } = this.props + const { start_ts, zoom, selected_annotation_id } = timeline + const { items } = this.state + const className = (zoom < 2) + ? 'annotationIndex' + : (zoom < 3) + ? 'annotationIndex condensed' + : 'annotationIndex collapsed' + return ( + <div className={className}> + {items.map(annotation => { + if (annotationInForm && annotation.id === annotationInForm.id) { + return null + } + if (annotation.id === selected_annotation_id) { + annotation = selectedAnnotation + } + const { id, type, start_ts } = annotation + const AnnotationElement = AnnotationElementLookup[type] + const y = timeToPosition(start_ts, timeline) + return ( + <AnnotationElement + key={id} + y={y} + selected={annotation.id === selected_annotation_id} + annotation={annotation} + media={media} + onClick={this.handleClick} + onDoubleClick={this.handleDoubleClick} + /> + ) + })} + </div> + ) + } +} + +const mapStateToProps = state => ({ + timeline: state.align.timeline, + annotationInForm: state.align.annotation, + selectedAnnotation: state.align.selectedAnnotation, + index: state.annotation.index, + media: state.media.index.lookup, +}) + +const mapDispatchToProps = dispatch => ({ +}) + +export default connect(mapStateToProps, mapDispatchToProps)(AnnotationIndex) |
