Add loading indicators and basic components/api for a search page. Still needs some wiring up.
This commit is contained in:
parent
1c8586f3fe
commit
83c657ceec
|
@ -4,6 +4,7 @@ from werkzeug.serving import run_simple
|
|||
|
||||
from blueprints.reader import reader
|
||||
from blueprints.editor import editor
|
||||
from blueprints.search import search
|
||||
from blueprints.api import api
|
||||
|
||||
# Initialize application
|
||||
|
@ -24,8 +25,9 @@ webpack.init_app(application)
|
|||
|
||||
# Register blueprints
|
||||
application.register_blueprint(reader)
|
||||
application.register_blueprint(editor, url_prefix='/editor')
|
||||
application.register_blueprint(editor, url_prefix='/edit')
|
||||
application.register_blueprint(api, url_prefix='/api')
|
||||
application.register_blueprint(search, url_prefix='/search')
|
||||
|
||||
if __name__ == "__main__":
|
||||
# application.debug=True
|
||||
|
|
17
setup.py
17
setup.py
|
@ -27,19 +27,32 @@ create_index_settings = {
|
|||
'number_of_shards' : 1,
|
||||
'number_of_replicas' : 0
|
||||
},
|
||||
'analysis': {
|
||||
'analyzer': {
|
||||
'html_analyzer': {
|
||||
'type': 'custom',
|
||||
'tokenizer': 'standard',
|
||||
'char_filter': ['html_strip']
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'mappings': {
|
||||
'chapter': {
|
||||
'properties': {
|
||||
'number': {'type': 'integer'},
|
||||
'title': {'type': 'keyword'},
|
||||
'text': {'type': 'text'}
|
||||
'text': {
|
||||
'type': 'text',
|
||||
}
|
||||
}
|
||||
},
|
||||
'note': {
|
||||
'properties': {
|
||||
'title': {'type': 'keyword'},
|
||||
'text': {'type': 'text'}
|
||||
'text': {
|
||||
'type': 'text',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,4 +158,13 @@ export const editTextReceived = data =>
|
|||
({
|
||||
type: 'SELECT_ANNOTATION_NOTE',
|
||||
id: id
|
||||
})
|
||||
})
|
||||
|
||||
// Search
|
||||
|
||||
export const updateSearchInput = searchInput => {
|
||||
return ({
|
||||
type: 'UPDATE_SEARCH_INPUT',
|
||||
data: searchInput.target.value
|
||||
})
|
||||
}
|
|
@ -39,6 +39,15 @@ export const HighlightButton = ({highlightActive, onHighlightClick}) =>
|
|||
</div>
|
||||
</div>
|
||||
|
||||
export const SearchButton = () =>
|
||||
<div>
|
||||
<div id='search_button' className='text-center'>
|
||||
<button className='btn btn-primary btn-sm'>
|
||||
Search <i className='fa fa_inline fa-search'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
export const NewChapterButton = ({onNewChapterClick}) =>
|
||||
<div>
|
||||
<div id='new_chapter_button' className='text-center'>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react'
|
||||
|
||||
const Content = (props) =>
|
||||
<div id='content_window' className='col-md-8'>
|
||||
<div id='content_window' className='col-md-8 order-2 order-xs-1 order-sm-1'>
|
||||
{props.children}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import React from 'react'
|
||||
|
||||
const LoadingSpinner = () =>
|
||||
<div className='loading_spinner'>
|
||||
<i className="fa fa-cog fa-spin fa-2x fa-fw"></i>
|
||||
</div>
|
||||
|
||||
export default LoadingSpinner
|
|
@ -1,15 +1,15 @@
|
|||
import React from 'react'
|
||||
import React from 'react'
|
||||
|
||||
const Navbar = () =>
|
||||
<nav className='navbar navbar-inverse navbar-static-top navbar-expand-lg'>
|
||||
<nav className='navbar navbar-dark navbar-static-top navbar-expand-lg'>
|
||||
<a className='navbar-brand' href='/'>The Joyce Project</a>
|
||||
<button className='navbar-toggler' type='button' data-toggle='collapse' data-target='#navbarItems'>
|
||||
<span className='navbar-toggler-icon'><i className='fa fa-bars fa-lg' /></span>
|
||||
<span className='navbar-toggler-icon'></span>
|
||||
</button>
|
||||
<div id='navbarItems' className='collapse navbar-collapse'>
|
||||
<ul className='navbar-nav mr-auto'>
|
||||
<li className='nav-item'>
|
||||
<a className='nav-link' href='/editor'>Editor</a>
|
||||
<a className='nav-link' href='/edit'>Edit</a>
|
||||
</li>
|
||||
<li className='nav-item'>
|
||||
<a className='nav-link' href='/notes'>Notes</a>
|
||||
|
|
|
@ -5,11 +5,15 @@ import { Editor } from 'draft-js'
|
|||
import { ReadModeTopBar, EditModeTopBar, AnnotateModeTopBar } from '../components/contentTopBar'
|
||||
import { EditModeBottomBar } from '../components/contentBottomBar'
|
||||
import DocumentTitle from '../components/documentTitle'
|
||||
import LoadingSpinner from '../components/loadingSpinner'
|
||||
import { updateEditorState, handleEditorKeyCommand, applyInlineStyles, setMode, cancelEdit, submitDocumentEdit, updateDocumentTitleChange, addAnnotation, removeAnnotation } from '../actions'
|
||||
|
||||
const JoyceEditorContent = ({currentDocument, editorState, mode, handleKeyCommand, onChangeEditorState, onToolButtonClick, setMode, cancelEdit, onSubmitClick, documentTitleInput, onDocumentTitleChange, onNewAnnotationClick, onRemoveAnnotationClick, docType}) =>
|
||||
const JoyceEditorContent = ({currentDocument, editorState, mode, handleKeyCommand, onChangeEditorState, onToolButtonClick, setMode, cancelEdit, onSubmitClick, documentTitleInput, onDocumentTitleChange, onNewAnnotationClick, onRemoveAnnotationClick, docType, loadingToggle}) =>
|
||||
<div>
|
||||
<div id='editor_metadata'>
|
||||
{loadingToggle === true &&
|
||||
<LoadingSpinner />
|
||||
}
|
||||
{(mode === 'READ_MODE' || mode === 'ANNOTATE_MODE') &&
|
||||
<DocumentTitle docType={docType} currentDocument={currentDocument} />
|
||||
}
|
||||
|
@ -45,6 +49,7 @@ const mapStateToProps = (state, props ) => {
|
|||
docType: state.docType,
|
||||
editorState: state.editorState,
|
||||
documentTitleInput: state.documentTitleInput,
|
||||
loadingToggle: state.loadingToggle
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,20 +2,25 @@ import React from 'react'
|
|||
import { connect } from 'react-redux'
|
||||
import { Editor } from 'draft-js'
|
||||
|
||||
import { setCurrentChapter, setAnnotationNote } from '../actions'
|
||||
import { setCurrentChapter, setAnnotationNote, toggleLoading } from '../actions'
|
||||
import DocumentTitle from '../components/documentTitle'
|
||||
import LoadingSpinner from '../components/loadingSpinner'
|
||||
|
||||
const JoyceReaderContent = ({currentDocument, highlightActive, editorState}) =>
|
||||
<div id="page" className={highlightActive ? 'annotations' : 'hidden_annotations'}>
|
||||
const JoyceReaderContent = ({currentDocument, highlightToggle, editorState, loadingToggle}) =>
|
||||
<div id="page" className={highlightToggle ? 'annotations' : 'hidden_annotations'}>
|
||||
{loadingToggle === true &&
|
||||
<LoadingSpinner />
|
||||
}
|
||||
<DocumentTitle docType={'chapters'} currentDocument={currentDocument} />
|
||||
<Editor editorState={editorState} readOnly={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
currentDocument: state.currentDocument,
|
||||
highlightActive: state.highlightActive,
|
||||
editorState: state.editorState
|
||||
editorState: state.editorState,
|
||||
highlightToggle: state.highlightToggle,
|
||||
loadingToggle: state.loadingToggle
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { HighlightButton } from '../components/button'
|
|||
import SidebarSpacer from '../components/sidebarSpacer'
|
||||
|
||||
const JoyceReaderSidebar = ({chapters, currentDocument, onDocumentClick, highlightActive, onHighlightClick, docType}) =>
|
||||
<div className='col-md-3' id='sidebar'>
|
||||
<div className='col-md-3 order-1 order-xs-2 order-sm-2' id='sidebar'>
|
||||
<div className='d-none d-md-block'>
|
||||
<SidebarSpacer />
|
||||
<HighlightButton highlightActive={highlightActive} onHighlightClick={onHighlightClick}/>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { Editor } from 'draft-js'
|
||||
|
||||
import { SearchButton } from '../components/button'
|
||||
import { updateSearchInput } from '../actions'
|
||||
|
||||
const JoyceSearchContent = ({searchResults, searchInput, onSearchInputChange}) =>
|
||||
<div id='search_content' className='col-md-11'>
|
||||
<input type='text' value={searchInput} onChange={onSearchInputChange}/>
|
||||
<SearchButton />
|
||||
</div>
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
searchResults: state.searchResults,
|
||||
searchInput: state.searchInput
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
onSearchInputChange: searchInput => {
|
||||
dispatch(updateSearchInput(searchInput))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const JoyceSearchContentContainer = connect(mapStateToProps, mapDispatchToProps)(JoyceSearchContent)
|
||||
|
||||
export default JoyceSearchContentContainer
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
|
||||
import Navbar from '../components/navbar'
|
||||
import JoyceSearchContentContainer from '../containers/joyceSearchContentContainer'
|
||||
|
||||
const JoyceSearchPage = () =>
|
||||
<div>
|
||||
<Navbar />
|
||||
<div id='joyce_search' className='container-fluid'>
|
||||
<div className="row">
|
||||
<JoyceSearchContentContainer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const JoyceSearchPageContainer = connect()(JoyceSearchPage)
|
||||
|
||||
export default JoyceSearchPageContainer
|
|
@ -4,14 +4,14 @@ import { createStore, applyMiddleware } from 'redux'
|
|||
import { Provider } from 'react-redux'
|
||||
import 'bootstrap'
|
||||
|
||||
import reduceDocuments from './reducers/reduceDocuments'
|
||||
import reduceEditor from './reducers/reduceEditor'
|
||||
import { getDocumentList, setCurrentDocument, setDocType } from './actions'
|
||||
import { joyceAPI, logger } from './middleware/'
|
||||
import { getFirstDocument } from './mixins/firstDocument'
|
||||
import JoyceEditorPageContainer from './containers/joyceEditorPageContainer'
|
||||
|
||||
const docType = 'chapters'
|
||||
const store = createStore(reduceDocuments, applyMiddleware(joyceAPI, logger))
|
||||
const store = createStore(reduceEditor, applyMiddleware(joyceAPI, logger))
|
||||
store.dispatch(setDocType(docType))
|
||||
|
||||
ReactDOM.render(
|
||||
|
|
|
@ -3,7 +3,10 @@ const currentDocument = (state={}, action) => {
|
|||
case 'GET_DOCUMENT_TEXT':
|
||||
if (action.status === 'success' && action.state === 'currentDocument') {
|
||||
return action.data
|
||||
} else { return state }
|
||||
} else if (action.status ==='request' && action.state === 'currentDocument') {
|
||||
return {}
|
||||
}
|
||||
else { return state }
|
||||
case 'CREATE_DOCUMENT':
|
||||
return {id: null, number: null, title: '', text: ''}
|
||||
default:
|
||||
|
|
|
@ -31,6 +31,8 @@ const editorState = (state=blankEditor, action) => {
|
|||
if (action.status === 'success' && action.state === 'currentDocument') {
|
||||
const editorState = EditorState.createWithContent(stateFromHTML(action.data.text), decorator)
|
||||
return editorState
|
||||
} else if (action.status === 'request' && action.state === 'currentDocument') {
|
||||
return blankEditor
|
||||
} else { return state }
|
||||
case 'CREATE_CHAPTER':
|
||||
return blankEditor
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const highlightActive = (state=false, action) => {
|
||||
const highlightToggle = (state=false, action) => {
|
||||
switch(action.type) {
|
||||
case 'TOGGLE_HIGHLIGHT':
|
||||
return !state
|
||||
|
@ -7,4 +7,4 @@ const highlightActive = (state=false, action) => {
|
|||
}
|
||||
}
|
||||
|
||||
export default highlightActive
|
||||
export default highlightToggle
|
|
@ -0,0 +1,14 @@
|
|||
const loadingToggle = (state=true, action) => {
|
||||
switch(action.type) {
|
||||
case 'GET_DOCUMENT_TEXT':
|
||||
if (action.status === 'request' && action.state === 'currentDocument') {
|
||||
return true
|
||||
} else if (action.status === 'success' && action.state === 'currentDocument') {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export default loadingToggle
|
|
@ -9,6 +9,7 @@ import documentTitleInput from './documentTitleInput'
|
|||
import editorState from './editorState'
|
||||
import selectionState from './selectionState'
|
||||
import annotationNote from './annotationNote'
|
||||
import loadingToggle from './loadingToggle'
|
||||
|
||||
const reduceDocuments = combineReducers({
|
||||
notes,
|
||||
|
@ -19,7 +20,8 @@ const reduceDocuments = combineReducers({
|
|||
documentTitleInput,
|
||||
editorState,
|
||||
docType,
|
||||
selectionState
|
||||
selectionState,
|
||||
loadingToggle
|
||||
})
|
||||
|
||||
export default reduceDocuments
|
|
@ -5,7 +5,8 @@ import notes from './notes'
|
|||
import currentDocument from './currentDocument'
|
||||
import editorState from './editorState'
|
||||
import annotationNote from './annotationNote'
|
||||
import highlightActive from './highlightActive'
|
||||
import highlightToggle from './highlightToggle'
|
||||
import loadingToggle from './loadingToggle'
|
||||
import docType from './docType'
|
||||
|
||||
const reduceReader = combineReducers({
|
||||
|
@ -15,7 +16,8 @@ const reduceReader = combineReducers({
|
|||
annotationNote,
|
||||
docType,
|
||||
currentDocument,
|
||||
highlightActive
|
||||
highlightToggle,
|
||||
loadingToggle
|
||||
})
|
||||
|
||||
export default reduceReader
|
|
@ -0,0 +1,11 @@
|
|||
import { combineReducers } from 'redux'
|
||||
|
||||
import searchResults from './searchResults'
|
||||
import searchInput from './searchInput'
|
||||
|
||||
const reduceSearch = combineReducers({
|
||||
searchResults,
|
||||
searchInput
|
||||
})
|
||||
|
||||
export default reduceSearch
|
|
@ -0,0 +1,10 @@
|
|||
const searchInput = (state='', action) => {
|
||||
switch(action.type) {
|
||||
case 'UPDATE_SEARCH_INPUT':
|
||||
return action.data
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export default searchInput
|
|
@ -0,0 +1,8 @@
|
|||
const searchResults = (state={}, action) => {
|
||||
switch(action.type) {
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export default searchResults
|
|
@ -0,0 +1,20 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { createStore, applyMiddleware } from 'redux'
|
||||
import { Provider } from 'react-redux'
|
||||
import 'bootstrap'
|
||||
|
||||
import reduceSearch from './reducers/reduceSearch'
|
||||
import { logger, joyceAPI } from './middleware/'
|
||||
import JoyceSearchPageContainer from './containers/joyceSearchPageContainer'
|
||||
|
||||
// TODO: Pass routing from Flask?
|
||||
|
||||
let store = createStore(reduceSearch, applyMiddleware(logger, joyceAPI))
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<JoyceSearchPageContainer />
|
||||
</Provider>,
|
||||
document.getElementById('wrapper')
|
||||
)
|
|
@ -9,6 +9,10 @@
|
|||
margin-bottom: 0.5%;
|
||||
}
|
||||
|
||||
#editor_metadata h4 {
|
||||
font-size: 1.75rem
|
||||
}
|
||||
|
||||
#editor_topbar button img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
@ -36,6 +36,23 @@ html body {
|
|||
box-shadow: 2px 5px 10px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
#search_content {
|
||||
height: 44em;
|
||||
overflow: hidden;
|
||||
background-color: rgba(250,250,250,.8);
|
||||
margin: 0 auto;
|
||||
padding: 2% 2%;
|
||||
border: 1px solid $border_color;
|
||||
border-radius: 5px;
|
||||
box-shadow: 2px 5px 10px 1px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.fa_inline {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
.loading_spinner {
|
||||
margin: 0 auto;
|
||||
text-align:center;
|
||||
color: $nav_gradient1
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
@import "variables";
|
||||
@import "window";
|
||||
@import "navbar";
|
||||
@import "sidebar";
|
||||
@import "page";
|
||||
@import "node_modules/bootstrap/scss/bootstrap";
|
||||
|
||||
$fa-font-path: "../../node_modules/font-awesome/fonts";
|
||||
@import "node_modules/font-awesome/scss/font-awesome";
|
||||
|
||||
#joyce_search {
|
||||
overflow: hidden;
|
||||
width: 95%;
|
||||
height: 100%;
|
||||
background-color: rgba(256,256,256,0.5);
|
||||
border-left: 1px solid $border_color;
|
||||
border-right: 1px solid $border_color;
|
||||
}
|
||||
|
||||
#joyce_search > div {
|
||||
margin-top: 20px;
|
||||
}
|
Loading…
Reference in New Issue