diff options
Diffstat (limited to 'animism-align/frontend/app/views/align/components')
10 files changed, 267 insertions, 25 deletions
diff --git a/animism-align/frontend/app/views/align/components/annotations/annotation.form.css b/animism-align/frontend/app/views/align/components/annotations/annotation.form.css new file mode 100644 index 0000000..fdb4abe --- /dev/null +++ b/animism-align/frontend/app/views/align/components/annotations/annotation.form.css @@ -0,0 +1,55 @@ +/* Annotation form */ + +.annotationForm { + width: 401px; + padding: 0.5rem; + position: absolute; + left: 0.25rem; + background: #448; + box-shadow: 0 0 2px #000, 0 0 4px #000; + z-index: 10; +} +.annotationForm textarea { + width: 100%; +} +.annotationForm .row { + justify-content: space-between; + align-items: center; +} +.annotationForm .row > div { + display: flex; + align-items: center; +} +.annotationForm .buttons { + margin-bottom: 0.5rem; +} +.annotationForm .ts { + color: #fff; +} +.annotationForm .select.media_id { + width: 100%; + margin-right: 0; +} +.annotationForm .options label span:first-child { + display: inline-block; + width: 6rem; +} +.annotationForm .options .description { + font-size: 0.75rem; + margin-top: 0.25rem; + margin-bottom: 0.5rem; +} +.annotationForm .color input[type="text"].number { + width: 8rem; +} +.annotationForm .color input[type="text"] { + width: 8rem; +} +.annotationForm .color input[type="color"] { + background: transparent; + border: 0; + height: 1.7rem; + padding: 0; + margin: 0 0.5rem 0 0; + width: 1.4rem; +} diff --git a/animism-align/frontend/app/views/align/components/annotations/annotation.form.js b/animism-align/frontend/app/views/align/components/annotations/annotation.form.js index f4620bc..7882884 100644 --- a/animism-align/frontend/app/views/align/components/annotations/annotation.form.js +++ b/animism-align/frontend/app/views/align/components/annotations/annotation.form.js @@ -7,18 +7,16 @@ import actions from 'app/actions' import { ZOOM_STEPS } from 'app/constants' import { clamp, timestamp, capitalize } from 'app/utils' -import { timeToPosition } from 'app/views/align/align.util' +import { timeToPosition } from 'app/utils/align.utils' import { Select } from 'app/common' -import { - AnnotationFormVideo, - AnnotationFormImage, -} from './annotationForms' +import { annotationFormLookup } from './annotationForms' const ANNOTATION_TYPES = [ 'sentence', 'header', 'paragraph_end', 'video', 'image', 'image_carousel', + 'curtain', ].map(name => ({ name, label: capitalize(name.replace('_', ' ')) })) class AnnotationForm extends Component { @@ -26,6 +24,7 @@ class AnnotationForm extends Component { super(props) this.handleChange = this.handleChange.bind(this) this.handleSelect = this.handleSelect.bind(this) + this.handleSettingsChange = this.handleSettingsChange.bind(this) this.handleSettingsSelect = this.handleSettingsSelect.bind(this) this.handleKeyDown = this.handleKeyDown.bind(this) this.handleSubmit = this.handleSubmit.bind(this) @@ -74,6 +73,10 @@ class AnnotationForm extends Component { handleSelect(name, value) { actions.align.updateAnnotationForm(name, value) } + handleSettingsChange(e) { + const { name, value } = e.target + this.handleSettingsSelect(name, value) + } handleSettingsSelect(name, value) { if (name.indexOf('_id') !== -1) value = parseInt(value) || 0 actions.align.updateAnnotationSettings(name, value) @@ -111,7 +114,7 @@ class AnnotationForm extends Component { } } render() { - const { timeline, annotation, media } = this.props + const { timeline, annotation } = this.props return ( <div className='annotationForm' @@ -122,15 +125,7 @@ class AnnotationForm extends Component { {this.renderButtons()} {annotation.type === 'sentence' && this.renderTextarea()} {annotation.type === 'header' && this.renderTextarea()} - {annotation.type === 'video' && - <AnnotationFormVideo annotation={annotation} media={media} handleSettingsSelect={this.handleSettingsSelect} /> - } - {annotation.type === 'image' && - <AnnotationFormImage annotation={annotation} media={media} handleSettingsSelect={this.handleSettingsSelect} /> - } - {annotation.type === 'image_carousel' && - <AnnotationFormImageCarousel annotation={annotation} media={media} handleSettingsSelect={this.handleSettingsSelect} /> - } + {(annotation.type in annotationFormLookup) && this.renderElementForm()} </div> ) } @@ -169,6 +164,18 @@ class AnnotationForm extends Component { </div> ) } + renderElementForm() { + const { annotation, media } = this.props + const AnnotationFormElement = annotationFormLookup[annotation.type] + return ( + <AnnotationFormElement + annotation={annotation} + media={media} + handleSettingsChange={this.handleSettingsChange} + handleSettingsSelect={this.handleSettingsSelect} + /> + ) + } } const mapStateToProps = state => ({ diff --git a/animism-align/frontend/app/views/align/components/annotations/annotation.index.css b/animism-align/frontend/app/views/align/components/annotations/annotation.index.css new file mode 100644 index 0000000..d39e5de --- /dev/null +++ b/animism-align/frontend/app/views/align/components/annotations/annotation.index.css @@ -0,0 +1,76 @@ +/* Annotation index */ + +.annotationIndex { + width: 800px; +} +.annotationIndex .annotation { + position: absolute; + left: 5px; + max-width: 400px; + padding: 0.25rem 0.375rem; + box-shadow: 0px 0px 3px rgba(0,0,0,1.0); + border: 1px solid transparent; + border-radius: 2px; + font-size: 12px; + cursor: pointer; + user-select: none; + background-color: #768; +} +.annotation.selected { + border-color: #bbf; + box-shadow: 0px 0px 4px rgba(0,0,0,1.0), 0px 0px 2px rgba(0,0,0,1.0); + z-index: 1; + background-image: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.4)); +} +.annotationIndex .annotation.media { + left: calc(405px + 0.5rem); +} +.annotation.sentence.even { + background-color: #83b; +} +.annotation.sentence.odd { + background-color: #537; +} +.annotation.header { + background-color: #838; +} +.annotation.paragraph_end { + background-color: #003; + border-top: 1px solid #888; + width: 100%; + padding: 1px; +} +.annotationIndex .annotation.utility { + left: calc(405px + 0.5rem); +} + + +/* Condensed layout (first lines) */ + +.annotationIndex.condensed .annotation.sentence { + z-index: 0; + white-space: pre; + overflow: hidden; + text-overflow: ellipsis; +} +.annotationIndex.condensed .annotation.header { + z-index: 1; +} +.annotationIndex.condensed .annotation.paragraph_end { + border-top-color: #888; +} + +/* Collapsed layout (borders) */ + +.annotationIndex.collapsed .annotation.sentence { + height: 2px; overflow: hidden; padding: 0; z-index: 0; +} +.annotationIndex.collapsed .annotation.sentence.selected { + z-index: 1; +} +.annotationIndex.collapsed .annotation.header { + z-index: 2; +} +.annotationIndex.collapsed .annotation.paragraph_end { + border-top-color: #333; +} 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 index aa31268..da1038f 100644 --- a/animism-align/frontend/app/views/align/components/annotations/annotation.index.js +++ b/animism-align/frontend/app/views/align/components/annotations/annotation.index.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react' +import React, { PureComponent } from 'react' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' @@ -6,11 +6,11 @@ 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 { positionToTime, timeToPosition } from 'app/utils/align.utils' import { AnnotationElementLookup } from './annotationTypes' -class AnnotationIndex extends Component { +class AnnotationIndex extends PureComponent { state = { items: [], } diff --git a/animism-align/frontend/app/views/align/components/annotations/annotationForms/annotationForm.utility.js b/animism-align/frontend/app/views/align/components/annotations/annotationForms/annotationForm.utility.js new file mode 100644 index 0000000..2b6f868 --- /dev/null +++ b/animism-align/frontend/app/views/align/components/annotations/annotationForms/annotationForm.utility.js @@ -0,0 +1,80 @@ +import React, { Component } from 'react' + +import { timestamp } from 'app/utils' +import { TextInput, LabelDescription, Select } from 'app/common' +import { CURTAIN_COLOR_SELECT_OPTIONS } from 'app/constants' +import { curtainTimings } from 'app/utils/annotation.utils' + +export const AnnotationFormCurtain = ({ annotation, handleSettingsChange, handleSettingsSelect }) => { + const { + fadeInDurationInSeconds, fadeOutDurationInSeconds, durationInSeconds, + start_ts, end_ts, fade_in_end_ts, fade_out_start_ts, + } = curtainTimings(annotations) + + return ( + <div className='options'> + <TextInput + title="Total duration" + name="duration" + className="number" + placeholder="0:00" + data={annotation.settings} + onChange={handleSettingsChange} + autoComplete="off" + /> + <LabelDescription> + {durationInSeconds} + {' seconds, ends at '} + {timestamp(end_ts)} + </LabelDescription> + + <TextInput + title="Fade in duration" + name="fade_in_duration" + className="number" + placeholder="0:00" + data={annotation.settings} + onChange={handleSettingsChange} + autoComplete="off" + /> + <LabelDescription> + {fadeInDurationInSeconds} + {' seconds, ends at '} + {timestamp(fade_in_end_ts)} + </LabelDescription> + + <TextInput + title="Fade out duration" + name="fade_out_duration" + className="number" + placeholder="0:00" + data={annotation.settings} + onChange={handleSettingsChange} + autoComplete="off" + /> + <LabelDescription> + {fadeOutDurationInSeconds} + {' seconds, starts at '} + {timestamp(fade_out_start_ts)} + </LabelDescription> + + <Select + title='Color' + name='color' + selected={annotation.settings.color} + options={CURTAIN_COLOR_SELECT_OPTIONS} + defaultOption='Pick a color' + onChange={handleSettingsSelect} + /> + + <TextInput + title="Curtain text" + name="curtain_text" + placeholder="Enter text or leave blank" + data={annotation.settings} + onChange={handleSettingsChange} + autoComplete="off" + /> + </div> + ) +} diff --git a/animism-align/frontend/app/views/align/components/annotations/annotationForms/index.js b/animism-align/frontend/app/views/align/components/annotations/annotationForms/index.js index 1411efc..29f9def 100644 --- a/animism-align/frontend/app/views/align/components/annotations/annotationForms/index.js +++ b/animism-align/frontend/app/views/align/components/annotations/annotationForms/index.js @@ -6,7 +6,12 @@ import { AnnotationFormImage, } from './annotationForm.image' -export { - AnnotationFormImage, - AnnotationFormVideo, +import { + AnnotationFormCurtain, +} from './annotationForm.utility' + +export const annotationFormLookup = { + image: AnnotationFormImage, + video: AnnotationFormVideo, + curtain: AnnotationFormCurtain, } diff --git a/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.image.js b/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.image.js index ec4d25e..00c653a 100644 --- a/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.image.js +++ b/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.image.js @@ -1,8 +1,8 @@ import React, { Component } from 'react' -import { thumbnailURL } from 'app/views/align/align.util' +import { thumbnailURL } from 'app/utils/annotation.utils' -import { checkAnnotationMediaNotReady, AnnotationMediaLoading } from './annotationTypes.util' +import { checkAnnotationMediaNotReady, AnnotationMediaLoading } from './annotationTypes.utility' export const AnnotationImage = ({ y, annotation, media, selected, onClick, onDoubleClick }) => { const { start_ts, text } = annotation diff --git a/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.util.js b/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.utility.js index 17abebd..8bd0b9d 100644 --- a/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.util.js +++ b/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.utility.js @@ -1,5 +1,19 @@ import React, { Component } from 'react' +export const AnnotationCurtain = ({ y, annotation, selected, onClick, onDoubleClick }) => { + const className = selected ? 'annotation utility curtain selected' : 'annotation utility curtain' + return ( + <div + className={className} + style={{ top: y }} + onClick={e => onClick(e, annotation)} + onDoubleClick={e => onDoubleClick(e, annotation)} + > + CURTAIN + </div> + ) +} + export const checkAnnotationMediaNotReady = (annotation, media) => { return (!media) || (!(annotation.settings.media_id in media)) } diff --git a/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.video.js b/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.video.js index f51ac71..d2fc4e5 100644 --- a/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.video.js +++ b/animism-align/frontend/app/views/align/components/annotations/annotationTypes/annotationTypes.video.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' -import { thumbnailURL } from 'app/views/align/align.util' -import { checkAnnotationMediaNotReady, AnnotationMediaLoading } from './annotationTypes.util' +import { thumbnailURL } from 'app/utils/annotation.utils' +import { checkAnnotationMediaNotReady, AnnotationMediaLoading } from './annotationTypes.utility' export const AnnotationVideo = ({ y, annotation, media, selected, onClick, onDoubleClick }) => { const { start_ts, text } = annotation diff --git a/animism-align/frontend/app/views/align/components/annotations/annotationTypes/index.js b/animism-align/frontend/app/views/align/components/annotations/annotationTypes/index.js index 560063b..ee30167 100644 --- a/animism-align/frontend/app/views/align/components/annotations/annotationTypes/index.js +++ b/animism-align/frontend/app/views/align/components/annotations/annotationTypes/index.js @@ -13,10 +13,15 @@ import { AnnotationImage, } from './annotationTypes.image' +import { + AnnotationCurtain, +} from './annotationTypes.utility' + export const AnnotationElementLookup = { sentence: React.memo(AnnotationSentence), header: React.memo(AnnotationHeader), paragraph_end: React.memo(AnnotationParagraphEnd), video: React.memo(AnnotationVideo), image: React.memo(AnnotationImage), + curtain: React.memo(AnnotationCurtain), } |
