Editor Sidebar options on mobile devices

This commit is contained in:
Alex Hunt 2018-12-02 16:55:11 -08:00
parent 125cef05bb
commit b745d2e186
14 changed files with 136 additions and 22 deletions

View File

@ -76,7 +76,7 @@ export const NewDocumentButton = ({onClick, docType}) =>
<div id='new_document_button' className='text-center'>
<button onClick={onClick} className='btn btn-outline-success btn-sm'>
New {helpers.docTypeName(docType)}
<i className='fas fa_inline fa-plus-square-o'></i>
<i className='fas fa_inline fa-plus-square'></i>
</button>
</div>
</div>

View File

@ -1,9 +1,12 @@
import React from 'react'
import PropTypes from 'prop-types'
export const DocTypeDropdown = ({docType, setDocType}) =>
import helpers from '../modules/helpers'
import TagColorPreview from './tagColorPreview'
export const DocTypeDropdown = ({docType, setDocType, size='sm'}) =>
<div className='dropdown'>
<button className='btn btn-primary btn-sm dropdown-toggle' type='button' id='doc_type_select' data-toggle='dropdown'>
<button className={'btn btn-primary dropdown-toggle btn-' + size} type='button' id='doc_type_select' data-toggle='dropdown'>
{docType.charAt(0).toUpperCase() + docType.slice(1)}
</button>
<div className='dropdown-menu'>
@ -13,15 +16,27 @@ export const DocTypeDropdown = ({docType, setDocType}) =>
</div>
</div>
export const ReaderDocDropdown = ({currentDocument, docs, docType, onDocumentClick}) =>
export const DocListDropdown = ({currentDocument, docs, docType, onDocumentClick, editMode=false, onNewDocumentClick}) =>
<div className='dropdown'>
<button className='btn btn-primary btn-md dropdown-toggle' type='button' id='doc_type_select' data-toggle='dropdown'>
{docType=='chapters' && currentDocument.number ? currentDocument.number + '. ' : ''}{currentDocument.title}
{docType==='chapters' && currentDocument.number ? currentDocument.number + '. ' : ''}{currentDocument.title}
</button>
<div className='dropdown-menu'>
{docs.map(doc =>
<a key={doc.id} className='dropdown-item' onClick={()=>onDocumentClick(doc.id, docType)}>{docType=='chapters' ? doc.number + '. ' : ''}{doc.title}</a>
)}
<a key={doc.id} className='dropdown-item' onClick={()=>onDocumentClick(doc.id, docType)}>
{docType==='tags' &&
<TagColorPreview color={doc.color}/>
}
{docType==='chapters' ? doc.number + '. ' : ''}
{doc.title}
</a>
)}
{editMode=true &&
<a className='dropdown-item' onClick={onNewDocumentClick}>
<i className='fas fa-plus-square'></i>&nbsp;
New {helpers.docTypeName(docType)}
</a>
}
</div>
</div>

View File

@ -1,16 +1,27 @@
import React from 'react'
import PropTypes from 'prop-types'
import { DocTypeDropdown } from './dropdown'
import { HighlightButton } from './button'
import { ReaderDocDropdown } from './dropdown'
import { DocListDropdown } from './dropdown'
export const ReaderSidebarOptions = ({docs, currentDocument, highlightToggle, docType, onHighlightClick, onDocumentClick}) =>
<div id='mobile_reader_sidebar' className='row'>
<div id='mobile_reader_sidebar' className='mobile_sidebar row'>
<div className='col-6 d-block d-md-none'>
<HighlightButton toggle={highlightToggle} onClick={onHighlightClick} size='md'/>
</div>
<div className='col-6 d-block d-md-none'>
<ReaderDocDropdown currentDocument={currentDocument} docs={docs} docType={docType} onDocumentClick={onDocumentClick}/>
<DocListDropdown currentDocument={currentDocument} docs={docs} docType={docType} onDocumentClick={onDocumentClick}/>
</div>
</div>
export const EditorSidebarOptions = ({docType, setDocType, currentDocument, docs, onDocumentClick, onNewDocumentClick}) =>
<div id='mobile_editor_sidebar' className='mobile_sidebar row'>
<div className='col-6 d-block d-md-none'>
<DocTypeDropdown docType={docType} setDocType={setDocType} size='md'/>
</div>
<div className='col-6 d-block d-md-none'>
<DocListDropdown currentDocument={currentDocument} docs={docs} docType={docType} onDocumentClick={onDocumentClick} editMode={true} onNewDocumentClick={onNewDocumentClick}/>
</div>
</div>

View File

@ -15,6 +15,7 @@ const AdminHeader = ({toggles, hideAdmin, showAdmin}) => {
if (toggles.admin === true) {
return (
<div id='admin_header' className={toggles.admin ? 'admin_show' : 'admin_hide'}>
<div id='admin_title'>Admin Tools:</div>
<button type='button' className='btn btn-sm btn-outline-primary' onClick={refreshElasticsearch}>
Refresh Seed Data
</button>

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import actions from '../actions'
import helpers from '../modules/helpers'
import Content from '../components/content'
import { EditorWelcome } from '../components/welcome'
import LoadingSpinner from '../components/loadingSpinner'
@ -11,8 +12,10 @@ import EditorContentContainer from './editorContentContainer'
import DeleteConfirmModal from '../components/deleteConfirmModal'
import AnnotationModal from '../components/annotationModal'
import ChooseAnnotationModal from '../components/chooseAnnotationModal'
import { EditorSidebarOptions } from '../components/mobileSidebarOptions'
const EditorPage = ({
chapters,
notes,
currentDocument,
annotationNote,
@ -22,6 +25,9 @@ const EditorPage = ({
docType,
tags,
toggles,
setDocType,
onDocumentClick,
onNewDocumentClick,
onDeleteConfirm,
onSubmitAnnotationClick,
selectAnnotationNote,
@ -31,7 +37,15 @@ const EditorPage = ({
userErrors,
}) =>
<div id='joyce_reader' className='container-fluid'>
<div className="row">
<EditorSidebarOptions
docs={helpers.documentsOfDocType(docType, chapters, notes, tags)}
currentDocument={currentDocument}
docType={docType}
setDocType={setDocType}
onDocumentClick={onDocumentClick}
onNewDocumentClick={()=>onNewDocumentClick(docType)}
/>
<div id='content_container' className="row">
<EditorSidebarContainer />
<Content>
{toggles.loading === true &&
@ -63,6 +77,7 @@ const EditorPage = ({
const mapStateToProps = state => {
return {
chapters: state.chapters,
notes: state.notes,
tags: state.tags,
currentDocument: state.currentDocument,
@ -79,6 +94,15 @@ const mapStateToProps = state => {
const mapDispatchToProps = dispatch => {
return {
setDocType: docType => {
dispatch(actions.setEditorDocType(docType))
},
onNewDocumentClick: docType => {
dispatch(actions.createNewDocument(docType))
},
onDocumentClick: (id, docType) => {
dispatch(actions.setCurrentDocument(id, docType))
},
onDeleteConfirm: (id, docType) => {
dispatch(actions.deleteCurrentDocument(id, docType))
},
@ -90,7 +114,8 @@ const mapDispatchToProps = dispatch => {
},
selectAnnotationTag: tag => {
dispatch(actions.selectAnnotationTag(tag))
},
},
onSubmitAnnotationClick: (annotationNote, annotationTag, selectionState, editorState) => {
dispatch(actions.submitAnnotation(annotationNote, annotationTag, selectionState, editorState))
}

View File

@ -46,7 +46,7 @@ const mapDispatchToProps = dispatch => {
},
onNewDocumentClick: docType => {
dispatch(actions.createNewDocument(docType))
},
},
onDocumentClick: (id, docType) => {
dispatch(actions.setCurrentDocument(id, docType))
},

View File

@ -0,0 +1,41 @@
// Same as LinkContainer but w/o modal-toggle attributes
// TODO: Dry this up later
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import actions from '../actions'
const ModalLink = (props) => {
const data = props.contentState.getEntity(props.entityKey).getData()
return (
<a href='#'
onClick={()=>props.onAnnotationClick(data['url'])}
style={{color: '#' + data['data-color']}}
data-color={data['data-color']}
data-url={data['url']}
>
{props.children}
</a>
)
}
const mapStateToProps = state => {
return {}
}
const mapDispatchToProps = dispatch => {
return {
onAnnotationClick: id => {
dispatch(actions.selectAnnotationNote(id))
}
}
}
ModalLink.propTypes = {
onAnnotationClick: PropTypes.func,
}
const ModalLinkContainer = connect(mapStateToProps, mapDispatchToProps)(ModalLink)
export default ModalLinkContainer

View File

@ -9,6 +9,11 @@ import { html_export_options, convertToSearchText, linkDecorator } from '../modu
const joyceInterface = store => next => action => {
next(action)
const chapters = store.getState().chapters
const notes = store.getState().notes
const tags = store.getState().tags
const docType = store.getState().docType
const docs = helpers.documentsOfDocType(docType, chapters, notes, tags)
switch(action.type) {
case 'SET_CURRENT_DOCUMENT':
store.dispatch(actions.getDocumentText({id: action.id, docType: action.docType, state: 'currentDocument'}))
@ -18,12 +23,12 @@ const joyceInterface = store => next => action => {
if (docErrors.length < 1) {
const textContent = action.editorState.getCurrentContent()
const data = {
title: action.documentTitleInput,
title: action.inputs.documentTitle,
html_source: stateToHTML(textContent, html_export_options),
search_text: convertToSearchText(textContent)
}
if (action.docType === 'tags') {
data.color = action.colorPickerInput
data.color = action.inputs.color
}
if (action.currentDocument.id) {
data.id = action.currentDocument.id
@ -48,7 +53,7 @@ const joyceInterface = store => next => action => {
if (currentDocument.id) {
store.dispatch(actions.getDocumentText({id: currentDocument.id, docType: docType, state: 'currentDocument'}))
} else {
store.dispatch(actions.getDocumentText({id: notes[0].id, docType: docType, state: 'currentDocument'}))
store.dispatch(actions.getDocumentText({id: docs[0].id, docType: docType, state: 'currentDocument'}))
}
break
case 'SET_DOC_TYPE':

View File

@ -1,6 +1,7 @@
import { convertToRaw, ContentState, CompositeDecorator } from 'draft-js'
import LinkContainer from '../containers/linkContainer'
import ModalLinkContainer from '../containers/linkModalContainer'
export const html_export_options = {
blockStyleFn: (block) => {
@ -49,6 +50,13 @@ export const linkDecorator = new CompositeDecorator([
}
])
export const modalLinkDecorator = new CompositeDecorator([
{
strategy: findLinkEntities,
component: ModalLinkContainer,
}
])
export const convertToSearchText = contentState => {
const rawState = convertToRaw(contentState)
const searchText = rawState.blocks.reduce(

View File

@ -2,15 +2,15 @@ import React from 'react'
import { EditorState } from 'draft-js'
import { stateFromHTML } from 'draft-js-import-html'
import { linkDecorator } from '../modules/editorSettings.js'
import { modalLinkDecorator } from '../modules/editorSettings.js'
const blankEditor = EditorState.createEmpty(linkDecorator)
const blankEditor = EditorState.createEmpty(modalLinkDecorator)
const modalEditorState = (state=blankEditor, action) => {
switch(action.type) {
case 'GET_DOCUMENT_TEXT':
if (action.status === 'success' && action.state === 'annotationNote') {
const editorState = EditorState.createWithContent(stateFromHTML(action.data.html_source), linkDecorator)
const editorState = EditorState.createWithContent(stateFromHTML(action.data.html_source), modalLinkDecorator)
return editorState
} else if (action.status === 'request' && action.state === 'annotationNote') {
return blankEditor

View File

@ -1,7 +1,7 @@
const initialState = {
loading: true,
highlights: true,
admin: true,
admin: false,
}
const toggles = (state=initialState, action) => {

View File

@ -9,6 +9,14 @@
display: none;
}
#admin_title {
display: inline;
font-size: 1.2em;
font-weight: 700;
margin-top: 25px;
padding: 0 1rem;
}
#open_admin_button {
position: absolute;
z-index: 1;

View File

@ -85,7 +85,7 @@ button {
}
@include respond-above(sm) {
#mobile_reader_sidebar {
.mobile_sidebar {
display: none;
}
}

View File

@ -47,7 +47,7 @@ $fa-font-path: "../../node_modules/@fortawesome/fontawesome-free/webfonts";
border: none;
}
#mobile_reader_sidebar {
.mobile_sidebar {
background-color: rgba(255, 255, 255, 0.8);
margin-top: 0 !important;
padding-top: 1em;