Add fluid loading animation when notes are loading
This commit is contained in:
parent
b9ee0f57f7
commit
14b0e61373
4 changed files with 185 additions and 33 deletions
111
app/components/EntryLoader.jsx
Normal file
111
app/components/EntryLoader.jsx
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
import mui from 'material-ui'
|
||||
import getMuiTheme from 'material-ui/lib/styles/getMuiTheme'
|
||||
|
||||
import RefreshIndicator from 'material-ui/lib/refresh-indicator'
|
||||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
|
||||
|
||||
const style = {
|
||||
container: {
|
||||
position: 'relative',
|
||||
},
|
||||
refresh: {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
},
|
||||
}
|
||||
|
||||
const {
|
||||
Styles
|
||||
} = mui
|
||||
|
||||
const Colors = Styles.Colors
|
||||
|
||||
const DefaultRawTheme = Styles.LightRawTheme
|
||||
|
||||
export default class EntryLoader extends React.Component {
|
||||
constructor(props, context){
|
||||
super(props, context)
|
||||
this.state = {loaded: false}
|
||||
}
|
||||
|
||||
static get childContextTypes(){
|
||||
return {muiTheme: React.PropTypes.object}
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
muiTheme: this.context.muiTheme || getMuiTheme(DefaultRawTheme)
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateState(this.props)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.updateState(nextProps)
|
||||
}
|
||||
|
||||
updateState = (props) => {
|
||||
props || (props = {})
|
||||
|
||||
var loaded = this.state.loaded
|
||||
|
||||
// update loaded state, if supplied
|
||||
if ('loaded' in props) {
|
||||
loaded = !!props.loaded
|
||||
}
|
||||
this.setState({loaded: loaded})
|
||||
};
|
||||
|
||||
getContent = () => {
|
||||
return this.props.children
|
||||
}
|
||||
|
||||
getLoader = () => {
|
||||
if(!this.state.loaded){
|
||||
return (
|
||||
<div key="loader">
|
||||
<div className="loader">
|
||||
<div className="spinner">
|
||||
<RefreshIndicator
|
||||
size={50}
|
||||
left={0}
|
||||
top={0}
|
||||
loadingColor={"#FF9800"}
|
||||
status="loading"
|
||||
style={style.refresh}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
else{
|
||||
return (<div key="loaded"></div>)
|
||||
}
|
||||
};
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div
|
||||
id={this.props.id || "entry-loader"}
|
||||
style={style.container}
|
||||
>
|
||||
<ReactCSSTransitionGroup
|
||||
transitionName="entry-loader"
|
||||
transitionLeaveTimeout={200}
|
||||
transitionEnterTimeout={200}
|
||||
>
|
||||
{this.getLoader()}
|
||||
</ReactCSSTransitionGroup>
|
||||
{this.getContent()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import Tag from 'material-ui/lib/svg-icons/maps/local-offer'
|
|||
import SearchBar from 'SearchBar'
|
||||
import SelectableList from 'SelectableList'
|
||||
import Item from 'Item'
|
||||
import EntryLoader from 'EntryLoader'
|
||||
|
||||
import uuid from 'node-uuid'
|
||||
import path from 'path-extra'
|
||||
|
|
@ -44,7 +45,7 @@ export default class EntrySelector extends React.Component {
|
|||
|
||||
constructor(props, context){
|
||||
super(props, context)
|
||||
this.state = {notes: []}
|
||||
this.state = {notes: [], loaded: false}
|
||||
this.loadNotes()
|
||||
const { store } = this.context
|
||||
store.subscribe(this.stateChanged)
|
||||
|
|
@ -75,18 +76,19 @@ export default class EntrySelector extends React.Component {
|
|||
loadNotes = () => {
|
||||
var notebook = this.props.navigation.selection
|
||||
if(!utils.isEmpty(notebook)){
|
||||
var notes = utils.loadNotes(notebook)
|
||||
notes.sort(utils.compareNotes())
|
||||
this.state.notes = notes
|
||||
var notes = utils.loadNotesAsync(notebook, (notes)=>{
|
||||
notes.sort(utils.compareNotes())
|
||||
this.setState({notes: notes, loaded: true})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
reloadNotes = (selection) => {
|
||||
this.setState({notes: []}, ()=>{
|
||||
this.setState({loaded: false}, ()=>{
|
||||
var notebook = selection || this.props.navigation.selection
|
||||
utils.loadNotesAsync(notebook, (notes) => {
|
||||
notes.sort(utils.compareNotes())
|
||||
this.setState({notes: notes})
|
||||
this.setState({notes: notes, loaded: true})
|
||||
})
|
||||
})
|
||||
};
|
||||
|
|
@ -168,7 +170,7 @@ export default class EntrySelector extends React.Component {
|
|||
|
||||
addNoteTapped = () => {
|
||||
this.createNewNote((note, err)=>{
|
||||
this.props.refreshNavigation()
|
||||
this.props.noteAdded()
|
||||
})
|
||||
};
|
||||
|
||||
|
|
@ -209,6 +211,32 @@ export default class EntrySelector extends React.Component {
|
|||
</div>)
|
||||
};
|
||||
|
||||
renderNotes = () => {
|
||||
return this.state.notes.map((note, i) =>{
|
||||
return (<ListItem
|
||||
key={i}
|
||||
value={i}
|
||||
leftIcon={<Description color={Colors.grey600}/>}
|
||||
innerDivStyle={{paddingBottom: 40}}
|
||||
style={{borderBottom: '1px solid #F1F1F1'}}
|
||||
secondaryText={
|
||||
<p>
|
||||
{note.summary}
|
||||
</p>
|
||||
}
|
||||
secondaryTextLines={2}
|
||||
>
|
||||
<div>
|
||||
{note.title || "Untitled Note"}
|
||||
</div>
|
||||
|
||||
{this.renderNoteInfo(note)}
|
||||
|
||||
</ListItem>
|
||||
)
|
||||
})
|
||||
};
|
||||
|
||||
render(){
|
||||
return (
|
||||
<Paper id={this.props.id} className={this.props.className+ " noselect"} zDepth={0}>
|
||||
|
|
@ -230,32 +258,12 @@ export default class EntrySelector extends React.Component {
|
|||
<SelectableList
|
||||
id="entry-list"
|
||||
ref="entryList"
|
||||
selectedItemStyle={{backgroundColor: colors.grey300}}>
|
||||
|
||||
{this.state.notes.map((note, i) =>{
|
||||
return (<ListItem
|
||||
key={i}
|
||||
value={i}
|
||||
leftIcon={<Description color={colors.grey600}/>}
|
||||
innerDivStyle={{paddingBottom: 40}}
|
||||
style={{borderBottom: '1px solid #F1F1F1'}}
|
||||
secondaryText={
|
||||
<p>
|
||||
{note.summary}
|
||||
</p>
|
||||
}
|
||||
secondaryTextLines={2}
|
||||
>
|
||||
<div>
|
||||
{note.title || "Untitled Note"}
|
||||
</div>
|
||||
|
||||
{this.renderNoteInfo(note)}
|
||||
|
||||
</ListItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
selectedItemStyle={{backgroundColor: Colors.grey100}}>
|
||||
<EntryLoader
|
||||
loaded={this.state.loaded}
|
||||
>
|
||||
{this.renderNotes()}
|
||||
</EntryLoader>
|
||||
</SelectableList>
|
||||
</Paper>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
"node-uuid": "^1.4.7",
|
||||
"path-extra": "^3.0.0",
|
||||
"react": "^0.14.7",
|
||||
"react-addons-css-transition-group": "^0.14.7",
|
||||
"react-dom": "^0.14.7",
|
||||
"react-quill": "^0.4.1",
|
||||
"react-redux": "^4.4.0",
|
||||
|
|
|
|||
32
style.css
32
style.css
|
|
@ -17,6 +17,38 @@ body, html{
|
|||
padding-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.entry-loader-enter {
|
||||
opacity: 0.01;
|
||||
}
|
||||
|
||||
.entry-loader-enter.entry-loader-enter-active {
|
||||
opacity: 1;
|
||||
transition: opacity 200ms ease-in;
|
||||
}
|
||||
|
||||
.entry-loader-leave {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.entry-loader-leave.entry-loader-leave-active {
|
||||
opacity: 0.01;
|
||||
transition: opacity 200ms ease-in;
|
||||
}
|
||||
|
||||
#entry-list .loader{
|
||||
height: 100% !important;
|
||||
position: fixed;
|
||||
width: 338px;
|
||||
background-color: rgba(0,0,0, 0.1);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#entry-list .loader .spinner{
|
||||
position: relative !important;
|
||||
top: -webkit-calc(50% - 50px);
|
||||
left: -webkit-calc(50% - 25px);
|
||||
}
|
||||
|
||||
.list {
|
||||
background-color: #EFEFEF !important;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue