Editor Sidebar options on mobile devices
This commit is contained in:
parent
125cef05bb
commit
b745d2e186
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
New {helpers.docTypeName(docType)}
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ const mapDispatchToProps = dispatch => {
|
|||
},
|
||||
onNewDocumentClick: docType => {
|
||||
dispatch(actions.createNewDocument(docType))
|
||||
},
|
||||
},
|
||||
onDocumentClick: (id, docType) => {
|
||||
dispatch(actions.setCurrentDocument(id, docType))
|
||||
},
|
||||
|
|
|
@ -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
|
|
@ -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':
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const initialState = {
|
||||
loading: true,
|
||||
highlights: true,
|
||||
admin: true,
|
||||
admin: false,
|
||||
}
|
||||
|
||||
const toggles = (state=initialState, action) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -85,7 +85,7 @@ button {
|
|||
}
|
||||
|
||||
@include respond-above(sm) {
|
||||
#mobile_reader_sidebar {
|
||||
.mobile_sidebar {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue