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 SearchBar from 'SearchBar'
|
||||||
import SelectableList from 'SelectableList'
|
import SelectableList from 'SelectableList'
|
||||||
import Item from 'Item'
|
import Item from 'Item'
|
||||||
|
import EntryLoader from 'EntryLoader'
|
||||||
|
|
||||||
import uuid from 'node-uuid'
|
import uuid from 'node-uuid'
|
||||||
import path from 'path-extra'
|
import path from 'path-extra'
|
||||||
|
|
@ -44,7 +45,7 @@ export default class EntrySelector extends React.Component {
|
||||||
|
|
||||||
constructor(props, context){
|
constructor(props, context){
|
||||||
super(props, context)
|
super(props, context)
|
||||||
this.state = {notes: []}
|
this.state = {notes: [], loaded: false}
|
||||||
this.loadNotes()
|
this.loadNotes()
|
||||||
const { store } = this.context
|
const { store } = this.context
|
||||||
store.subscribe(this.stateChanged)
|
store.subscribe(this.stateChanged)
|
||||||
|
|
@ -75,18 +76,19 @@ export default class EntrySelector extends React.Component {
|
||||||
loadNotes = () => {
|
loadNotes = () => {
|
||||||
var notebook = this.props.navigation.selection
|
var notebook = this.props.navigation.selection
|
||||||
if(!utils.isEmpty(notebook)){
|
if(!utils.isEmpty(notebook)){
|
||||||
var notes = utils.loadNotes(notebook)
|
var notes = utils.loadNotesAsync(notebook, (notes)=>{
|
||||||
notes.sort(utils.compareNotes())
|
notes.sort(utils.compareNotes())
|
||||||
this.state.notes = notes
|
this.setState({notes: notes, loaded: true})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadNotes = (selection) => {
|
reloadNotes = (selection) => {
|
||||||
this.setState({notes: []}, ()=>{
|
this.setState({loaded: false}, ()=>{
|
||||||
var notebook = selection || this.props.navigation.selection
|
var notebook = selection || this.props.navigation.selection
|
||||||
utils.loadNotesAsync(notebook, (notes) => {
|
utils.loadNotesAsync(notebook, (notes) => {
|
||||||
notes.sort(utils.compareNotes())
|
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 = () => {
|
addNoteTapped = () => {
|
||||||
this.createNewNote((note, err)=>{
|
this.createNewNote((note, err)=>{
|
||||||
this.props.refreshNavigation()
|
this.props.noteAdded()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -209,6 +211,32 @@ export default class EntrySelector extends React.Component {
|
||||||
</div>)
|
</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(){
|
render(){
|
||||||
return (
|
return (
|
||||||
<Paper id={this.props.id} className={this.props.className+ " noselect"} zDepth={0}>
|
<Paper id={this.props.id} className={this.props.className+ " noselect"} zDepth={0}>
|
||||||
|
|
@ -230,32 +258,12 @@ export default class EntrySelector extends React.Component {
|
||||||
<SelectableList
|
<SelectableList
|
||||||
id="entry-list"
|
id="entry-list"
|
||||||
ref="entryList"
|
ref="entryList"
|
||||||
selectedItemStyle={{backgroundColor: colors.grey300}}>
|
selectedItemStyle={{backgroundColor: Colors.grey100}}>
|
||||||
|
<EntryLoader
|
||||||
{this.state.notes.map((note, i) =>{
|
loaded={this.state.loaded}
|
||||||
return (<ListItem
|
>
|
||||||
key={i}
|
{this.renderNotes()}
|
||||||
value={i}
|
</EntryLoader>
|
||||||
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>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</SelectableList>
|
</SelectableList>
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@
|
||||||
"node-uuid": "^1.4.7",
|
"node-uuid": "^1.4.7",
|
||||||
"path-extra": "^3.0.0",
|
"path-extra": "^3.0.0",
|
||||||
"react": "^0.14.7",
|
"react": "^0.14.7",
|
||||||
|
"react-addons-css-transition-group": "^0.14.7",
|
||||||
"react-dom": "^0.14.7",
|
"react-dom": "^0.14.7",
|
||||||
"react-quill": "^0.4.1",
|
"react-quill": "^0.4.1",
|
||||||
"react-redux": "^4.4.0",
|
"react-redux": "^4.4.0",
|
||||||
|
|
|
||||||
32
style.css
32
style.css
|
|
@ -17,6 +17,38 @@ body, html{
|
||||||
padding-bottom: 0px !important;
|
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 {
|
.list {
|
||||||
background-color: #EFEFEF !important;
|
background-color: #EFEFEF !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue