diff options
Diffstat (limited to 'animism-align/frontend/views')
20 files changed, 417 insertions, 44 deletions
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,6 +1,9 @@ * { } +.body.loading > div { + padding: 1rem; +} .body { width: 100%; height: 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()} - <div className='row'> - <Select - name='type' - selected={annotation.type} - options={ANNOTATION_TYPES} - defaultOption='text' - onChange={this.handleSelect} - /> + <div className='row buttons'> + <div> + <Select + name='type' + selected={annotation.type} + options={ANNOTATION_TYPES} + defaultOption='text' + onChange={this.handleSelect} + /> + <div className='ts'>{timestamp(annotation.start_ts, 1, true)}</div> + </div> <button onClick={this.handleSubmit}>Save</button> </div> </div> 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 ( <div diff --git a/animism-align/frontend/views/align/components/timeline/playButton.component.js b/animism-align/frontend/views/align/components/player/playButton.component.js index 486eaee..486eaee 100644 --- a/animism-align/frontend/views/align/components/timeline/playButton.component.js +++ b/animism-align/frontend/views/align/components/player/playButton.component.js 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 }) => { ) } -/* - <div className='tickLabel'> - {timestamp(cursor_ts, 1)} - </div> - -*/ - 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 { </div> <Annotations timeline={this.props.timeline} /> <PlayCursor /> - <PlayButton /> </div> ) } 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 ( + <header> + <PlayButton /> + <div> + <Link to="/align">Align</Link> + <Link to="/paragraph">Paragraphs</Link> + <Link to="/media">Media</Link> + </div> + </header> + ) +} + +// 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 ( + <div + className={className} + onDoubleClick={e => onDoubleClick(e, paragraph)} + > + {paragraph.annotations.map(annotation => ( + <span + key={annotation.id} + className={annotation.id === selectedAnnotation ? 'selected' : ''} + onClick={e => onAnnotationClick(e, paragraph, annotation)} + > + {annotation.text} + </span> + ))} + </div> + ) +} + +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 ( + <div + className={className} + onDoubleClick={e => onDoubleClick(e, paragraph)} + > + {text} + </div> + ) +} + +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 ( + <div className='paragraphs'> + <div className='content'> + {paragraphs.map(paragraph => { + if (paragraph.type in ParagraphElementLookup) { + const ParagraphElement = ParagraphElementLookup[paragraph.type] + return ( + <ParagraphElement + key={paragraph.id} + paragraph={paragraph} + selectedParagraph={false} + selectedAnnotation={-1} + onAnnotationClick={this.onAnnotationClick} + onDoubleClick={this.onParagraphDoubleClick} + /> + ) + } else { + return <div key={paragraph.id}>{'(empty)'}</div> + } + })} + </div> + </div> + ) + } +} + +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 <div className='body loading'><Loader /></div> + } + return ( + <div className='body'> + <ParagraphList /> + </div> + ) + } +} + +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 |
