From 6f5ff3cdfac3fc154281fdda7c1ec9ff7ebbd1fa Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 6 Jul 2020 17:57:25 +0200 Subject: paragraph list view --- animism-align/frontend/views/align/align.css | 25 +++++- .../frontend/views/align/align.reducer.js | 1 + animism-align/frontend/views/align/align.util.js | 13 +-- .../components/annotations/annotation.form.js | 21 ++--- .../components/annotations/annotation.index.js | 32 +++++++- .../components/annotations/annotation.types.js | 8 +- .../components/player/playButton.component.js | 31 ++++++++ .../components/timeline/playButton.component.js | 31 -------- .../components/timeline/playCursor.component.js | 7 -- .../align/components/timeline/ticks.component.js | 11 ++- .../components/timeline/waveform.component.js | 6 +- animism-align/frontend/views/align/constants.js | 3 + .../views/align/containers/timeline.container.js | 9 +-- .../views/annotation/annotation.reducer.js | 2 +- animism-align/frontend/views/index.js | 1 + .../frontend/views/nav/header.component.js | 42 ++++++++++ animism-align/frontend/views/nav/nav.css | 73 +++++++++++++++++ .../views/paragraph/components/paragraph.types.js | 44 +++++++++++ .../containers/paragraphList.container.js | 92 ++++++++++++++++++++++ .../views/paragraph/paragraph.container.js | 35 ++++++++ .../frontend/views/paragraph/paragraph.css | 36 +++++++++ 21 files changed, 448 insertions(+), 75 deletions(-) create mode 100644 animism-align/frontend/views/align/components/player/playButton.component.js delete mode 100644 animism-align/frontend/views/align/components/timeline/playButton.component.js create mode 100644 animism-align/frontend/views/nav/header.component.js create mode 100644 animism-align/frontend/views/nav/nav.css create mode 100644 animism-align/frontend/views/paragraph/components/paragraph.types.js create mode 100644 animism-align/frontend/views/paragraph/containers/paragraphList.container.js create mode 100644 animism-align/frontend/views/paragraph/paragraph.container.js create mode 100644 animism-align/frontend/views/paragraph/paragraph.css (limited to 'animism-align/frontend/views') diff --git a/animism-align/frontend/views/align/align.css b/animism-align/frontend/views/align/align.css index d430e1f..12d5b1d 100644 --- a/animism-align/frontend/views/align/align.css +++ b/animism-align/frontend/views/align/align.css @@ -1,5 +1,8 @@ * { +} +.body.loading > div { + padding: 1rem; } .body { width: 100%; @@ -74,8 +77,8 @@ canvas { /* Audio player */ .playButton { - position: absolute; - top: 0; left: 0; + /*position: absolute;*/ + /*top: 0; left: 0;*/ width: 3rem; height: 3rem; padding: 1rem; background: #000; @@ -104,6 +107,9 @@ canvas { position: relative; width: 450px; } + +/* Annotation form */ + .annotationForm { width: 401px; padding: 0.5rem; @@ -118,7 +124,18 @@ canvas { } .annotationForm .row { justify-content: space-between; + align-items: center; } +.annotationForm .row > div { + display: flex; + align-items: center; +} +.annotationForm .ts { + color: #fff; +} + +/* Annotation index */ + .annotationIndex { width: 405px; } @@ -132,6 +149,8 @@ canvas { border-radius: 2px; font-size: 12px; cursor: pointer; + user-select: none; + background-color: #768; } .annotation.selected { border-color: #bbf; @@ -143,7 +162,7 @@ canvas { background-color: #83b; } .annotation.sentence.odd { - background-color: #638; + background-color: #537; } .annotation.header { background-color: #838; diff --git a/animism-align/frontend/views/align/align.reducer.js b/animism-align/frontend/views/align/align.reducer.js index 679f63b..dc471a6 100644 --- a/animism-align/frontend/views/align/align.reducer.js +++ b/animism-align/frontend/views/align/align.reducer.js @@ -21,6 +21,7 @@ export default function alignReducer(state = initialState, action) { case types.peaks.loaded: console.log('peaks duration:', action.data.length / 10) return state + case types.align.set_display_setting: return { ...state, diff --git a/animism-align/frontend/views/align/align.util.js b/animism-align/frontend/views/align/align.util.js index ce72aef..d7b4c39 100644 --- a/animism-align/frontend/views/align/align.util.js +++ b/animism-align/frontend/views/align/align.util.js @@ -1,9 +1,13 @@ import { ZOOM_STEPS } from './constants' import { clamp } from '../../util' +import actions from '../../actions' + +import { HEADER_MARGIN, INNER_HEIGHT } from './constants' export const positionToTime = (y, { start_ts, zoom, duration }) => { + y -= HEADER_MARGIN const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 - const widthTimeDuration = window.innerHeight * secondsPerPixel + const widthTimeDuration = INNER_HEIGHT * secondsPerPixel const timeMin = start_ts const timeMax = Math.min(start_ts + widthTimeDuration, duration) const timeWidth = timeMax - timeMin @@ -11,20 +15,19 @@ export const positionToTime = (y, { start_ts, zoom, duration }) => { } export const timeToPosition = (ts, { start_ts, zoom, duration }) => { - const height = window.innerHeight const secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 - const widthTimeDuration = height * secondsPerPixel + const widthTimeDuration = INNER_HEIGHT * secondsPerPixel const timeMin = start_ts const timeMax = Math.min(start_ts + widthTimeDuration, duration) const timeWidth = timeMax - timeMin - const timeHalfHeight = height * secondsPerPixel / 2 + const timeHalfHeight = INNER_HEIGHT * secondsPerPixel / 2 if (ts < timeMin - timeHalfHeight) { return -9999 } if (ts > timeMax) { return -9999 } - return (ts - timeMin) / timeWidth * height + return (ts - timeMin) / timeWidth * INNER_HEIGHT } export const getFirstPunctuationMarkIndex = text => { diff --git a/animism-align/frontend/views/align/components/annotations/annotation.form.js b/animism-align/frontend/views/align/components/annotations/annotation.form.js index 6972f93..e18d6df 100644 --- a/animism-align/frontend/views/align/components/annotations/annotation.form.js +++ b/animism-align/frontend/views/align/components/annotations/annotation.form.js @@ -7,7 +7,7 @@ import actions from '../../../../actions' // import * as alignActions from '../align.actions' import { ZOOM_STEPS } from '../../constants' -import { clamp } from '../../../../util' +import { clamp, timestamp } from '../../../../util' import { timeToPosition } from '../../align.util' import { Select } from '../../../../common' @@ -95,14 +95,17 @@ class AnnotationForm extends Component { > {annotation.type === 'sentence' && this.renderTextarea()} {annotation.type === 'header' && this.renderTextarea()} -
- +
{timestamp(annotation.start_ts, 1, true)}
+
diff --git a/animism-align/frontend/views/align/components/annotations/annotation.index.js b/animism-align/frontend/views/align/components/annotations/annotation.index.js index 8121d1d..d5c1eed 100644 --- a/animism-align/frontend/views/align/components/annotations/annotation.index.js +++ b/animism-align/frontend/views/align/components/annotations/annotation.index.js @@ -4,7 +4,7 @@ import { connect } from 'react-redux' import actions from '../../../../actions' -import { ZOOM_STEPS } from '../../constants' +import { ZOOM_STEPS, INNER_HEIGHT } from '../../constants' import { clamp } from '../../../../util' import { positionToTime, timeToPosition } from '../../align.util' @@ -30,7 +30,7 @@ class AnnotationIndex extends Component { const { order, lookup } = index let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step - let widthTimeDuration = window.innerHeight * secondsPerPixel // secs per pixel + let widthTimeDuration = INNER_HEIGHT * secondsPerPixel // secs per pixel let timeMin = start_ts - 30.0 let timeMax = Math.min(start_ts + widthTimeDuration, duration) @@ -43,9 +43,37 @@ class AnnotationIndex extends Component { } handleClick(e, annotation) { e.stopPropagation() + if (e.shiftKey) { + e.preventDefault() + this.handleParagraphSelection(annotation) + } actions.audio.seek(annotation.start_ts) actions.align.setSelectedAnnotation(annotation.id) } + handleParagraphSelection(annotation) { + const { selected_paragraph_id } = this.props.timeline + console.log("___________________") + console.log(annotation) + console.log(selected_paragraph_id) + if (!selected_paragraph_id || selected_paragraph_id === -1) { + if (annotation.paragraph_id) { + actions.align.setSelectedParagraph(annotation.paragraph_id) + } else { + actions.paragraph.create({ + type: 'paragraph', + start_ts: annotation.start_ts, + }) .then(data => { + console.log(data.res) + 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) diff --git a/animism-align/frontend/views/align/components/annotations/annotation.types.js b/animism-align/frontend/views/align/components/annotations/annotation.types.js index 7639821..f95589d 100644 --- a/animism-align/frontend/views/align/components/annotations/annotation.types.js +++ b/animism-align/frontend/views/align/components/annotations/annotation.types.js @@ -8,9 +8,11 @@ import { positionToTime, timeToPosition } from '../../align.util' export const AnnotationSentence = ({ y, annotation, selected, onClick, onDoubleClick }) => { const { start_ts, text, paragraph_id } = annotation - let className = (paragraph_id % 2) - ? 'annotation sentence odd' - : 'annotation sentence even' + let className = !paragraph_id + ? 'annotation sentence' + : (paragraph_id % 2) + ? 'annotation sentence odd' + : 'annotation sentence even' if (selected) className += ' selected' return (
{ + return ( +
{ + audio.playing ? actions.audio.pause() : actions.audio.play() + }} + /> + ) +} + +const mapStateToProps = state => ({ + audio: state.audio, +}) + +const mapDispatchToProps = dispatch => ({ + // alignActions: bindActionCreators({ ...alignActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(PlayButton) diff --git a/animism-align/frontend/views/align/components/timeline/playButton.component.js b/animism-align/frontend/views/align/components/timeline/playButton.component.js deleted file mode 100644 index 486eaee..0000000 --- a/animism-align/frontend/views/align/components/timeline/playButton.component.js +++ /dev/null @@ -1,31 +0,0 @@ -import React, { Component } from 'react' -// import { Link } from 'react-router-dom' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' - -import actions from '../../../../actions' -// import * as alignActions from '../align.actions' - -import { ZOOM_STEPS } from '../../constants' -import { clamp } from '../../../../util' - -const PlayButton = ({ audio }) => { - return ( -
{ - audio.playing ? actions.audio.pause() : actions.audio.play() - }} - /> - ) -} - -const mapStateToProps = state => ({ - audio: state.audio, -}) - -const mapDispatchToProps = dispatch => ({ - // alignActions: bindActionCreators({ ...alignActions }, dispatch), -}) - -export default connect(mapStateToProps, mapDispatchToProps)(PlayButton) diff --git a/animism-align/frontend/views/align/components/timeline/playCursor.component.js b/animism-align/frontend/views/align/components/timeline/playCursor.component.js index 71e6a3a..e03d212 100644 --- a/animism-align/frontend/views/align/components/timeline/playCursor.component.js +++ b/animism-align/frontend/views/align/components/timeline/playCursor.component.js @@ -24,13 +24,6 @@ const PlayCursor = ({ timeline, audio }) => { ) } -/* -
- {timestamp(cursor_ts, 1)} -
- -*/ - const mapStateToProps = state => ({ timeline: state.align.timeline, audio: state.audio, diff --git a/animism-align/frontend/views/align/components/timeline/ticks.component.js b/animism-align/frontend/views/align/components/timeline/ticks.component.js index 72f9bd0..747fb7a 100644 --- a/animism-align/frontend/views/align/components/timeline/ticks.component.js +++ b/animism-align/frontend/views/align/components/timeline/ticks.component.js @@ -1,16 +1,15 @@ import React, { Component } from 'react' -import { ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS } from '../../constants' +import { ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS, INNER_HEIGHT } from '../../constants' import { timestamp } from '../../../../util' export default class Ticks extends Component { render() { let { start_ts, zoom, duration } = this.props.timeline - const width = window.innerHeight let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step - let widthTimeDuration = width * secondsPerPixel // secs per pixel + let widthTimeDuration = INNER_HEIGHT * secondsPerPixel // secs per pixel let timeMin = start_ts let timeMax = Math.min(start_ts + widthTimeDuration, duration) @@ -26,11 +25,11 @@ export default class Ticks extends Component { let startOffset = pixelsPerLabel - (pixelMin % pixelsPerLabel) let startTiming = (pixelMin + startOffset) * secondsPerPixel - let labelCount = Math.ceil(width / pixelsPerLabel) + 1 + 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 > width) continue + if (offset > INNER_HEIGHT) continue timing = i * secondsPerLabel + startTiming if (timing > duration) { break @@ -63,7 +62,7 @@ export default class Ticks extends Component { /> ) } - let tickCount = Math.ceil(width / pixelsPerTick) + 6 + let tickCount = Math.ceil(INNER_HEIGHT / pixelsPerTick) + 6 for (var i = 0; i < tickCount; i += 1) { offset = i * pixelsPerTick + startOffset - pixelsPerLabel if (offset > durationOffset) { diff --git a/animism-align/frontend/views/align/components/timeline/waveform.component.js b/animism-align/frontend/views/align/components/timeline/waveform.component.js index 785b020..16ceaf6 100644 --- a/animism-align/frontend/views/align/components/timeline/waveform.component.js +++ b/animism-align/frontend/views/align/components/timeline/waveform.component.js @@ -6,7 +6,7 @@ import { connect } from 'react-redux' import actions from '../../../../actions' // import * as uploadActions from './upload.actions' -import { WAVEFORM_SIZE, ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS } from '../../constants' +import { WAVEFORM_SIZE, ZOOM_STEPS, ZOOM_LABEL_STEPS, ZOOM_TICK_STEPS, INNER_HEIGHT } from '../../constants' class Waveform extends Component { constructor(props){ @@ -23,12 +23,12 @@ class Waveform extends Component { resize() { const canvas = this.canvasRef.current canvas.width = WAVEFORM_SIZE - canvas.height = window.innerHeight + canvas.height = INNER_HEIGHT } draw() { const canvas = this.canvasRef.current const ctx = canvas.getContext('2d') - const h = window.innerHeight + const h = INNER_HEIGHT this.clearCanvas(ctx, h) this.drawCurve(ctx, h) } diff --git a/animism-align/frontend/views/align/constants.js b/animism-align/frontend/views/align/constants.js index c797784..cf504d3 100644 --- a/animism-align/frontend/views/align/constants.js +++ b/animism-align/frontend/views/align/constants.js @@ -29,3 +29,6 @@ export const ZOOM_TICK_STEPS = [ 60, 600, ] + +export const HEADER_MARGIN = 50 +export const INNER_HEIGHT = window.innerHeight - HEADER_MARGIN diff --git a/animism-align/frontend/views/align/containers/timeline.container.js b/animism-align/frontend/views/align/containers/timeline.container.js index 760da82..ba6b7e0 100644 --- a/animism-align/frontend/views/align/containers/timeline.container.js +++ b/animism-align/frontend/views/align/containers/timeline.container.js @@ -10,10 +10,10 @@ import Annotations from '../containers/annotations.container' import Waveform from '../components/timeline/waveform.component' import Ticks from '../components/timeline/ticks.component' import Cursor from '../components/timeline/cursor.component' -import PlayButton from '../components/timeline/playButton.component' +import PlayButton from '../components/player/playButton.component' import PlayCursor from '../components/timeline/playCursor.component' -import { WAVEFORM_SIZE, ZOOM_STEPS } from '../constants' +import { WAVEFORM_SIZE, ZOOM_STEPS, INNER_HEIGHT } from '../constants' import { clamp } from '../../../util' import { positionToTime } from '../align.util' @@ -84,7 +84,7 @@ class Timeline extends Component { let { start_ts, zoom, duration } = this.props.timeline let { deltaY } = e let secondsPerPixel = ZOOM_STEPS[zoom] * 0.1 // 0.1 sec / step - let widthTimeDuration = window.innerHeight * secondsPerPixel // secs per pixel + let widthTimeDuration = INNER_HEIGHT * secondsPerPixel // secs per pixel start_ts += Math.round((deltaY / 8) * ZOOM_STEPS[zoom]) start_ts = clamp(start_ts, 0, Math.max(0, duration - widthTimeDuration / 2)) if (e.shiftKey) { @@ -105,8 +105,8 @@ class Timeline extends Component { actions.align.setCursor(cursor_ts) } handleContainerClick(e) { - console.log('container click') actions.align.clearSelectedAnnotation() + actions.align.clearSelectedParagraph() } handleTimelineClick(e) { const play_ts = positionToTime(e.pageY, this.props.timeline) @@ -131,7 +131,6 @@ class Timeline extends Component {
-
) } diff --git a/animism-align/frontend/views/annotation/annotation.reducer.js b/animism-align/frontend/views/annotation/annotation.reducer.js index 80be5b2..98b785e 100644 --- a/animism-align/frontend/views/annotation/annotation.reducer.js +++ b/animism-align/frontend/views/annotation/annotation.reducer.js @@ -5,7 +5,7 @@ import { crudState, crudReducer } from '../../api/crud.reducer' const initialState = crudState('annotation', { options: { - sort: 'start_ts desc', + sort: 'start_ts asc', } }) diff --git a/animism-align/frontend/views/index.js b/animism-align/frontend/views/index.js index 9af42ce..b7b633e 100644 --- a/animism-align/frontend/views/index.js +++ b/animism-align/frontend/views/index.js @@ -1,2 +1,3 @@ export { default as align } from './align/align.container' +export { default as paragraph } from './paragraph/paragraph.container' export { default as upload } from './upload/upload.container' diff --git a/animism-align/frontend/views/nav/header.component.js b/animism-align/frontend/views/nav/header.component.js new file mode 100644 index 0000000..be9a6dc --- /dev/null +++ b/animism-align/frontend/views/nav/header.component.js @@ -0,0 +1,42 @@ +import React from 'react' +// import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { Link } from 'react-router-dom' + +import PlayButton from '../align/components/player/playButton.component' + +import './nav.css' + +function Header(props) { + return ( +
+ +
+ Align + Paragraphs + Media +
+
+ ) +} + +// const changeUsername = () => { +// const username = prompt("Please enter your username:", session('username')) +// if (username && username.length) { +// session.set('username', username) +// document.querySelector('Header div span').innerText = ' → ' + username // very naughty +// } +// } + + +const mapStateToProps = (state) => ({ + // auth: state.auth, + site: state.site, + // username: session.get('username'), + // isAuthenticated: state.auth.isAuthenticated, +}) + +const mapDispatchToProps = (dispatch) => ({ +}) + +export default connect(mapStateToProps, mapDispatchToProps)(Header) diff --git a/animism-align/frontend/views/nav/nav.css b/animism-align/frontend/views/nav/nav.css new file mode 100644 index 0000000..485ace2 --- /dev/null +++ b/animism-align/frontend/views/nav/nav.css @@ -0,0 +1,73 @@ +/* header */ + +header { + height: 3.125rem; + font-size: 0.875rem; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + background: rgba(32,16,64,0.8); + color: white; + z-index: 50; + position: relative; +} +header b { + font-weight: 900; +} +header a { + color: rgba(255,255,255,0.95); + text-decoration: none; + font-size: 0.875rem; + font-weight: 500; +} +header > div:first-child { + display: flex; + justify-content: flex-start; + align-items: center; + padding-left: 1.5rem; +} +header > div:last-child { + padding-right: 1.5rem; +} +header > div > button { + padding: 0.25rem; + margin: 0 0 0 0.5rem; + background: #000; + border-color: #888; + color: #888; +} +header > div > button:hover { + border-color: #fff; + color: #fff; +} +header > div:last-child a { + padding: 0.5rem; +} +header .btn-link:focus, +header .btn-link:hover, +header .btn-link:active, +header a:focus, +header a:hover, +header a:active { + text-decoration: none; + color: white; +} +header a:focus, +header a:hover, +header a:active { + color: white; +} +.menuToggle { + width: 1.625rem; + height: 1.625rem; + cursor: pointer; + line-height: 1; +} +header a.navbar-brand { + font-size: .8rem; +} + +header .username { + cursor: pointer; +} \ No newline at end of file diff --git a/animism-align/frontend/views/paragraph/components/paragraph.types.js b/animism-align/frontend/views/paragraph/components/paragraph.types.js new file mode 100644 index 0000000..c200a19 --- /dev/null +++ b/animism-align/frontend/views/paragraph/components/paragraph.types.js @@ -0,0 +1,44 @@ +import React, { Component } from 'react' + +import actions from '../../../actions' + +export const Paragraph = ({ paragraph, selectedParagraph, selectedAnnotation, onAnnotationClick, onDoubleClick }) => { + const className = paragraph.type + if (selectedParagraph) className += ' selected' + return ( +
onDoubleClick(e, paragraph)} + > + {paragraph.annotations.map(annotation => ( + onAnnotationClick(e, paragraph, annotation)} + > + {annotation.text} + + ))} +
+ ) +} + +export const ParagraphHeader = ({ paragraph, selectedParagraph, selectedAnnotation, onAnnotationClick, onDoubleClick }) => { + const className = selectedParagraph ? 'header selected' : 'header' + const text = paragraph.annotations.map(annotation => annotation.text).join(' ') + console.log(text) + return ( +
onDoubleClick(e, paragraph)} + > + {text} +
+ ) +} + +export const ParagraphElementLookup = { + paragraph: Paragraph, + blockquote: Paragraph, + header: ParagraphHeader, +} diff --git a/animism-align/frontend/views/paragraph/containers/paragraphList.container.js b/animism-align/frontend/views/paragraph/containers/paragraphList.container.js new file mode 100644 index 0000000..059baff --- /dev/null +++ b/animism-align/frontend/views/paragraph/containers/paragraphList.container.js @@ -0,0 +1,92 @@ +import React, { Component } from 'react' +import { Route } from 'react-router-dom' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import actions from '../../../actions' +import { ParagraphElementLookup } from '../components/paragraph.types' + +class ParagraphList extends Component { + state = { + paragraphs: [], + } + constructor(props) { + super(props) + this.onAnnotationClick = this.onAnnotationClick.bind(this) + this.onParagraphDoubleClick = this.onParagraphDoubleClick.bind(this) + } + componentDidMount() { + this.build() + } + build() { + const { order: annotationOrder, lookup: annotationLookup } = this.props.annotation + const { lookup: paragraphLookup } = this.props.paragraph + let currentParagraph = {} + const paragraphs = [] + annotationOrder.forEach((annotation_id, i) => { + const annotation = annotationLookup[annotation_id] + const paragraph = paragraphLookup[annotation.paragraph_id] + if (annotation.paragraph_id !== currentParagraph.id) { + const paragraph_type = getParagraphType(annotation, paragraph) + currentParagraph = { + id: annotation.paragraph_id || ('index_' + i), + type: paragraph_type, + annotations: [], + } + paragraphs.push(currentParagraph) + } + currentParagraph.annotations.push(annotation) + }) + this.setState({ paragraphs }) + } + onAnnotationClick(e, paragraph, annotation){ + // + } + onParagraphDoubleClick(e, paragraph) { + // + } + render() { + const { paragraphs } = this.state + return ( +
+
+ {paragraphs.map(paragraph => { + if (paragraph.type in ParagraphElementLookup) { + const ParagraphElement = ParagraphElementLookup[paragraph.type] + return ( + + ) + } else { + return
{'(empty)'}
+ } + })} +
+
+ ) + } +} + +const getParagraphType = (annotation, paragraph) => { + if (!paragraph) { + return annotation.type + } + return paragraph.type +} + +const mapStateToProps = state => ({ + paragraph: state.paragraph.index, + annotation: state.annotation.index, + audio: state.audio, +}) + +const mapDispatchToProps = dispatch => ({ +}) + +export default connect(mapStateToProps, mapDispatchToProps)(ParagraphList) diff --git a/animism-align/frontend/views/paragraph/paragraph.container.js b/animism-align/frontend/views/paragraph/paragraph.container.js new file mode 100644 index 0000000..ecd5417 --- /dev/null +++ b/animism-align/frontend/views/paragraph/paragraph.container.js @@ -0,0 +1,35 @@ +import React, { Component } from 'react' +import { Route } from 'react-router-dom' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import './paragraph.css' + +import actions from '../../actions' +import { Loader } from '../../common' + +import ParagraphList from './containers/paragraphList.container' + +class ParagraphContainer extends Component { + render() { + if (!this.props.annotation.lookup || !this.props.paragraph.lookup) { + return
+ } + return ( +
+ +
+ ) + } +} + +const mapStateToProps = state => ({ + paragraph: state.paragraph.index, + annotation: state.annotation.index, +}) + +const mapDispatchToProps = dispatch => ({ + // alignActions: bindActionCreators({ ...alignActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(ParagraphContainer) diff --git a/animism-align/frontend/views/paragraph/paragraph.css b/animism-align/frontend/views/paragraph/paragraph.css new file mode 100644 index 0000000..e96fd4f --- /dev/null +++ b/animism-align/frontend/views/paragraph/paragraph.css @@ -0,0 +1,36 @@ +.paragraphs { + width: 100%; + height: calc(100% - 3.125rem); + overflow: scroll; + padding-top: 1rem; + padding-left: 1rem; +} + +.paragraphs .content { + font-family: 'Georgia', serif; + width: 650px; + padding-bottom: 6rem; +} +.paragraphs .content > div { + margin-bottom: 16px; +} + +.paragraphs .header { + font-size: 32px; +} + +.paragraphs .paragraph { + font-size: 16px; + line-height: 1.5; +} +.paragraphs .paragraph span:after { + content: ' '; +} + +.paragraphs .blockquote { + line-height: 1.5; + padding-left: 3rem; +} +.paragraphs .blockquote span:after { + content: ' '; +} \ No newline at end of file -- cgit v1.2.3-70-g09d2