From 7e6dfb31af78b9d5a1a4ccec6998b14beb8a1194 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 10 Mar 2021 17:33:21 +0100 Subject: refactor annotation forms out of the directory nest --- .../views/editor/annotation/annotation.form.css | 67 ++++++++ .../views/editor/annotation/annotation.index.css | 125 ++++++++++++++ .../editor/annotation/annotations.container.js | 31 ++++ .../annotation/components/annotation.form.js | 186 +++++++++++++++++++++ .../annotation/components/annotation.index.js | 126 ++++++++++++++ .../annotationForms/annotationForm.gallery.js | 132 +++++++++++++++ .../annotationForms/annotationForm.image.js | 120 +++++++++++++ .../annotationForms/annotationForm.text.js | 153 +++++++++++++++++ .../annotationForms/annotationForm.utility.js | 172 +++++++++++++++++++ .../annotationForms/annotationForm.video.js | 160 ++++++++++++++++++ .../annotation/components/annotationForms/index.js | 46 +++++ .../annotationTypes/annotationTypes.gallery.js | 75 +++++++++ .../annotationTypes/annotationTypes.image.js | 54 ++++++ .../annotationTypes/annotationTypes.text.js | 121 ++++++++++++++ .../annotationTypes/annotationTypes.utility.js | 107 ++++++++++++ .../annotationTypes/annotationTypes.video.js | 51 ++++++ .../annotation/components/annotationTypes/index.js | 57 +++++++ 17 files changed, 1783 insertions(+) create mode 100644 animism-align/frontend/app/views/editor/annotation/annotation.form.css create mode 100644 animism-align/frontend/app/views/editor/annotation/annotation.index.css create mode 100644 animism-align/frontend/app/views/editor/annotation/annotations.container.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotation.form.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotation.index.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationForms/annotationForm.gallery.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationForms/annotationForm.image.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationForms/annotationForm.text.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationForms/annotationForm.utility.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationForms/annotationForm.video.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationForms/index.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationTypes/annotationTypes.gallery.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationTypes/annotationTypes.image.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationTypes/annotationTypes.text.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationTypes/annotationTypes.utility.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationTypes/annotationTypes.video.js create mode 100644 animism-align/frontend/app/views/editor/annotation/components/annotationTypes/index.js (limited to 'animism-align/frontend/app/views/editor/annotation') diff --git a/animism-align/frontend/app/views/editor/annotation/annotation.form.css b/animism-align/frontend/app/views/editor/annotation/annotation.form.css new file mode 100644 index 0000000..fa663c7 --- /dev/null +++ b/animism-align/frontend/app/views/editor/annotation/annotation.form.css @@ -0,0 +1,67 @@ +/* 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 div.textarea { + margin-bottom: 0.5rem; +} +.annotationForm img { + max-width: 100%; + max-height: 6rem; +} + +.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; +} +.annotationForm .checkbox { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} \ No newline at end of file diff --git a/animism-align/frontend/app/views/editor/annotation/annotation.index.css b/animism-align/frontend/app/views/editor/annotation/annotation.index.css new file mode 100644 index 0000000..afedbde --- /dev/null +++ b/animism-align/frontend/app/views/editor/annotation/annotation.index.css @@ -0,0 +1,125 @@ +/* Annotation index */ + +.annotationIndex { + width: 800px; +} +.annotationIndex .annotation { + position: absolute; + left: 5px; + max-width: 300px; + 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: 2; + background-image: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.4)); +} +.annotationIndex .annotation.media { + width: 300px; + left: calc(405px + 0.5rem); +} +.annotationIndex .annotation.sentence.even { + background-color: #83b; +} +.annotationIndex .annotation.sentence.odd { + background-color: #537; +} +.annotationIndex .annotation.section_heading { + background-color: #983; + z-index: 1; +} +.annotationIndex .annotation.heading_text { + background-color: #838; +} +.annotationIndex .annotation.paragraph_end { + background-color: #003; + border-top: 1px solid #888; + width: 300px; + padding: 1px; +} +.annotationIndex .annotation.utility { + left: calc(505px + 0.5rem); + width: 200px; +} +.annotationIndex .annotation.footnote { + left: calc(605px + 0.5rem); + width: 200px; +} +.annotationIndex .annotation.text_plate { + left: calc(605px + 0.5rem); + width: 250px; +} +.annotationIndex .annotation.utility.curtain { + background-image: linear-gradient(rgba(255,255,255,1.0), rgba(255,255,255,1.0)); + width: 15rem; + padding: 0; + overflow: hidden; +} +.annotationIndex .annotation.utility.curtain span { + position: absolute; + top: 0; left: 0; + z-index: 1; + color: black; + text-shadow: 0 0 3px #fff, 0 0 2px #fff, 0 0 1px #fff; + padding: 0.25rem; +} +.annotationIndex .annotation.utility.curtain .fadeIn { + z-index: 0; + position: absolute; + top: 0; + width: 100%; + background-image: linear-gradient(rgba(0,0,0,1.0), rgba(255,255,255,1.0)); + background-size: cover; +} +.annotationIndex .annotation.utility.curtain .fadeOut { + z-index: 0; + position: absolute; + bottom: 0; + width: 100%; + background-image: linear-gradient(rgba(255,255,255,1.0), rgba(0,0,0,1.0)); + background-size: cover; +} + +/* Condensed layout (first lines) */ + +.annotationIndex.condensed .annotation.sentence { + z-index: 0; + white-space: pre; + overflow: hidden; + text-overflow: ellipsis; +} +.annotationIndex.condensed .annotation.section_heading { + z-index: 2; +} +.annotationIndex.condensed .annotation.heading_text { + 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: 4; +} +.annotationIndex.collapsed .annotation.section_heading { + z-index: 3; +} +.annotationIndex.collapsed .annotation.heading_text { + z-index: 2; +} +.annotationIndex.collapsed .annotation.paragraph_end { + border-top-color: #333; +} diff --git a/animism-align/frontend/app/views/editor/annotation/annotations.container.js b/animism-align/frontend/app/views/editor/annotation/annotations.container.js new file mode 100644 index 0000000..3a247d0 --- /dev/null +++ b/animism-align/frontend/app/views/editor/annotation/annotations.container.js @@ -0,0 +1,31 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' + +import AnnotationForm from './components/annotation.form' +import AnnotationIndex from './components/annotation.index' + +import './annotation.form.css' +import './annotation.index.css' + +class Annotations extends Component { + constructor(props){ + super(props) + } + render() { + return ( +
+ + {this.props.annotation.id && + + } +
+ ) + } +} + +const mapStateToProps = state => ({ + timeline: state.align.timeline, + annotation: state.align.annotation, +}) + +export default connect(mapStateToProps)(Annotations) diff --git a/animism-align/frontend/app/views/editor/annotation/components/annotation.form.js b/animism-align/frontend/app/views/editor/annotation/components/annotation.form.js new file mode 100644 index 0000000..6804cdd --- /dev/null +++ b/animism-align/frontend/app/views/editor/annotation/components/annotation.form.js @@ -0,0 +1,186 @@ +import React, { Component } from 'react' +// import { Link } from 'react-router-dom' +import { connect } from 'react-redux' + +import actions from 'app/actions' + +import { ANNOTATION_SELECT_OPTIONS, MEDIA_ANNOTATION_TYPES } from 'app/constants' +import { timestamp } from 'app/utils' +import { timeToPosition } from 'app/utils/align.utils' +import { Select } from 'app/common' + +import { annotationFormLookup } from './annotationForms' + +const annotationTextTypes = new Set([ + 'sentence', 'section_heading', 'heading_text', 'pullquote_credit', 'footnote', 'text_plate', +]) + +class AnnotationForm extends Component { + constructor(props){ + 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) + this.handleDestroy = this.handleDestroy.bind(this) + this.textareaRef = React.createRef() + } + componentDidMount() { + if (this.textareaRef && this.textareaRef.current) { + this.textareaRef.current.focus() + } + } + handleKeyDown(e) { + if (e.keyCode === 27) { // escape + actions.align.hideAnnotationForm() + return + } + // console.log(e.keyCode) + if (!e.metaKey && !e.ctrlKey) return + let { start_ts } = this.props.annotation + switch (e.keyCode) { + case 38: // up + e.preventDefault() + start_ts -= 0.1 + actions.align.updateAnnotationForm('start_ts', Math.max(0, start_ts)) + actions.audio.seek(start_ts) + actions.align.setCursor(start_ts) + break + case 40: // down + e.preventDefault() + start_ts += 0.1 + actions.align.updateAnnotationForm('start_ts', Math.max(0, start_ts)) + actions.audio.seek(start_ts) + actions.align.setCursor(start_ts) + break + case 83: // ctrl-S + e.preventDefault() + this.handleSubmit() + default: + break + } + } + handleChange(e) { + const { name, value } = e.target + this.handleSelect(name, value) + } + 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) + } + handleSubmit() { + const { annotation } = this.props + if (annotation.type === 'paragraph_end') { + annotation.text = '' + } + if (annotation.type in MEDIA_ANNOTATION_TYPES) { + if (!annotation.settings.media_id) return + annotation.text = '' + } + if (annotation.id === 'new') { + delete annotation.id + actions.annotation.create(annotation) + .then(response => { + console.log(response) + actions.align.hideAnnotationForm() + }) + } else { + actions.annotation.update(annotation) + .then(response => { + console.log(response) + actions.align.hideAnnotationForm() + }) + } + } + handleDestroy() { + const { annotation } = this.props + if (annotation.id === 'new') { + actions.align.hideAnnotationForm() + } else { + actions.annotation.destroy(annotation) + .then(response => { + console.log(response) + actions.align.hideAnnotationForm() + }) + } + } + render() { + const { timeline, annotation } = this.props + return ( +
+ {this.renderButtons()} + {annotationTextTypes.has(annotation.type) && this.renderTextarea()} + {(annotation.type in annotationFormLookup) && this.renderElementForm()} +
+ ) + } + renderButtons() { + const { annotation } = this.props + return ( +
+
+