Add ability to add notebooks and refactor code
This commit is contained in:
parent
03238ad179
commit
d30477bd9d
12 changed files with 518 additions and 97 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -41,4 +41,4 @@ jspm_packages
|
||||||
|
|
||||||
# Optional REPL history
|
# Optional REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
dist
|
app/dist
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,17 @@ import Folder from 'material-ui/lib/svg-icons/file/folder'
|
||||||
import Edit from 'material-ui/lib/svg-icons/editor/mode-edit'
|
import Edit from 'material-ui/lib/svg-icons/editor/mode-edit'
|
||||||
import Delete from 'material-ui/lib/svg-icons/action/delete'
|
import Delete from 'material-ui/lib/svg-icons/action/delete'
|
||||||
import Divider from 'material-ui/lib/divider'
|
import Divider from 'material-ui/lib/divider'
|
||||||
import { SelectableContainerEnhance } from 'material-ui/lib/hoc/selectable-enhance'
|
import { SelectableContainerEnhance } from '../enhance/SelectableEnhance'
|
||||||
|
|
||||||
|
import uuid from 'node-uuid'
|
||||||
|
import path from 'path-extra'
|
||||||
|
|
||||||
|
import * as utils from 'utils'
|
||||||
|
import glob from 'glob'
|
||||||
|
|
||||||
|
import fs from 'fs'
|
||||||
|
import mkdirp from 'mkdirp'
|
||||||
|
import jsfile from 'jsonfile'
|
||||||
|
|
||||||
const colors = styles.Colors
|
const colors = styles.Colors
|
||||||
|
|
||||||
|
|
@ -28,8 +38,12 @@ function eventFire(el, etype){
|
||||||
el.dispatchEvent(evObj);
|
el.dispatchEvent(evObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
String.prototype.trunc = String.prototype.trunc || function(n){
|
||||||
|
return (this.length > n) ? this.substr(0, n-1)+'...' : this;
|
||||||
|
};
|
||||||
|
|
||||||
const {AppBar,
|
const {
|
||||||
|
AppBar,
|
||||||
AppCanvas,
|
AppCanvas,
|
||||||
FontIcon,
|
FontIcon,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
|
@ -54,12 +68,15 @@ let SelectableList = SelectableContainerEnhance(List)
|
||||||
function wrapState(ComposedComponent) {
|
function wrapState(ComposedComponent) {
|
||||||
const StateWrapper = React.createClass({
|
const StateWrapper = React.createClass({
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {selectedIndex: 0};
|
return {selectedIndex: -1};
|
||||||
|
},
|
||||||
|
setIndex(i, func){
|
||||||
|
this.setState({
|
||||||
|
selectedIndex: i,
|
||||||
|
}, func);
|
||||||
},
|
},
|
||||||
handleUpdateSelectedIndex(e, index) {
|
handleUpdateSelectedIndex(e, index) {
|
||||||
this.setState({
|
this.setIndex(index);
|
||||||
selectedIndex: index,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -118,24 +135,38 @@ export default class LibraryNav extends React.Component {
|
||||||
|
|
||||||
],
|
],
|
||||||
notebooks: [
|
notebooks: [
|
||||||
{'state': 'editing', 'title': '', 'notes': 0},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10},
|
|
||||||
{'state': 'displaying', 'title': 'FieldNotes', 'notes': 10}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
this.sortNotebooks(true)
|
this.sortNotebooks(true)
|
||||||
|
this.getNotebooks()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNotebooks = () => {
|
||||||
|
var dataPath = utils.getAppDataPath()
|
||||||
|
var notebooks = glob.sync(path.join(dataPath, '*.qvnotebook'))
|
||||||
|
for(var i=0; i<notebooks.length; i++){
|
||||||
|
var nbFile = notebooks[i]
|
||||||
|
var obj = jsfile.readFileSync(path.join(nbFile, 'meta.json'))
|
||||||
|
var notes = glob.sync(path.join(nbFile, '*.qvnote'))
|
||||||
|
var nb = {
|
||||||
|
'title': obj.name,
|
||||||
|
'uuid': obj.uuid,
|
||||||
|
'notes': notes.length,
|
||||||
|
'path': nbFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nb.title == ''){
|
||||||
|
nb.state = 'editing'
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
nb.state = 'displaying'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.notebooks.push(nb)
|
||||||
|
this.sortNotebooks(true)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
compareNotebooks = (a, b) => {
|
compareNotebooks = (a, b) => {
|
||||||
let atitle = a.title.toLowerCase()
|
let atitle = a.title.toLowerCase()
|
||||||
let btitle = b.title.toLowerCase()
|
let btitle = b.title.toLowerCase()
|
||||||
|
|
@ -147,10 +178,10 @@ export default class LibraryNav extends React.Component {
|
||||||
return 0
|
return 0
|
||||||
};
|
};
|
||||||
|
|
||||||
sortNotebooks = (notset) => {
|
sortNotebooks = (dontSet, func) => {
|
||||||
this.state.notebooks.sort(this.compareNotebooks)
|
this.state.notebooks.sort(this.compareNotebooks)
|
||||||
if (!notset){
|
if (!dontSet){
|
||||||
this.setState({notebooks: this.state.notebooks})
|
this.setState({notebooks: this.state.notebooks}, func)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -188,16 +219,71 @@ export default class LibraryNav extends React.Component {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollToRenamedNotebook = (original) => {
|
||||||
|
var newIndex = 0
|
||||||
|
for (var j = 0; j < this.state.notebooks.length; j++){
|
||||||
|
var n = this.state.notebooks[j]
|
||||||
|
if (n == original){
|
||||||
|
newIndex = j
|
||||||
|
this.refs.notebookList.setIndex(j, () => {
|
||||||
|
var nbitem = $(ReactDOM.findDOMNode(this.refs[n.title+newIndex]))
|
||||||
|
var cont = $('#notebook-list')
|
||||||
|
var newPos = nbitem.offset().top - cont.offset().top + cont.scrollTop() - cont.height()/2;
|
||||||
|
$('#notebook-list').animate({
|
||||||
|
scrollTop: newPos
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
createNewNotebook = (notebook, callback) => {
|
||||||
|
var notePath = utils.getNotebookPath(notebook)
|
||||||
|
mkdirp(notePath, (err) => {
|
||||||
|
if(err){
|
||||||
|
console.log('There was an error creating the directory '+notePath)
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (callback){
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
this.createNotebookMeta(notebook)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
createNotebookMeta = (notebook) => {
|
||||||
|
var notePath = utils.getNotebookPath(notebook)
|
||||||
|
var meta = {
|
||||||
|
'name': notebook.title,
|
||||||
|
'uuid': notebook.uuid
|
||||||
|
}
|
||||||
|
var metaPath = path.join(notePath, 'meta.json')
|
||||||
|
jsfile.writeFile(metaPath, meta, (err) => {
|
||||||
|
if(err){
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
newNotebookUnfocus = (i) => {
|
newNotebookUnfocus = (i) => {
|
||||||
|
|
||||||
var nb = this.state.notebooks[i]
|
var nb = this.state.notebooks[i]
|
||||||
|
|
||||||
var tf = this.refs["textField"+i]
|
var tf = this.refs["textField"+i]
|
||||||
var notebookName = tf.getValue()
|
var notebookName = tf.getValue()
|
||||||
if (notebookName){
|
if (notebookName){
|
||||||
nb.title = notebookName
|
nb.title = notebookName
|
||||||
nb.state = 'displaying'
|
nb.state = 'displaying'
|
||||||
this.setState({notebooks: this.state.notebooks})
|
this.setState({notebooks: this.state.notebooks})
|
||||||
this.sortNotebooks()
|
this.sortNotebooks(false, ()=>{
|
||||||
|
this.scrollToRenamedNotebook(nb)
|
||||||
|
})
|
||||||
|
this.createNotebookMeta(nb)
|
||||||
}
|
}
|
||||||
else if(nb.title){
|
else if(nb.title){
|
||||||
nb.state = 'displaying'
|
nb.state = 'displaying'
|
||||||
|
|
@ -206,11 +292,23 @@ export default class LibraryNav extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
addNotebookTapped = () => {
|
addNotebookTapped = () => {
|
||||||
var nbs = this.state.notebooks
|
var nb_uuid = uuid.v4().toUpperCase()
|
||||||
nbs.splice(0, 0, {'state': 'editing',
|
|
||||||
|
var nb = {
|
||||||
|
'state': 'editing',
|
||||||
'title': '',
|
'title': '',
|
||||||
'notes': 0})
|
'uuid': nb_uuid,
|
||||||
this.setState({notebooks: nbs})
|
'notes': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createNewNotebook(nb, () => {
|
||||||
|
var nbs = this.state.notebooks
|
||||||
|
nbs.splice(0, 0, nb)
|
||||||
|
this.setState({notebooks: nbs}, () => {
|
||||||
|
this.refs['textField0'].focus()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
newNotebookTyped = (i) => {
|
newNotebookTyped = (i) => {
|
||||||
|
|
@ -218,6 +316,7 @@ export default class LibraryNav extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
menuItemClicked = (i, ev) => {
|
menuItemClicked = (i, ev) => {
|
||||||
|
this.refs.notebookList.setIndex(-1)
|
||||||
var item = this.state.navItems[i]
|
var item = this.state.navItems[i]
|
||||||
var type = 'leftClick'
|
var type = 'leftClick'
|
||||||
var nativeEvent = ev.nativeEvent
|
var nativeEvent = ev.nativeEvent
|
||||||
|
|
@ -232,14 +331,9 @@ export default class LibraryNav extends React.Component {
|
||||||
var nbs = this.state.notebooks
|
var nbs = this.state.notebooks
|
||||||
nbs[i].state = 'editing'
|
nbs[i].state = 'editing'
|
||||||
this.setState({notebooks: nbs}, () => {
|
this.setState({notebooks: nbs}, () => {
|
||||||
console.log(i)
|
this.refs['textField'+i].focus()
|
||||||
var nbsx = this.state.notebooks
|
|
||||||
this.props.closeContextMenu()
|
|
||||||
//this.refs['textField'+i].focus()
|
|
||||||
//$(ReactDOM.findDOMNode(this.refs['listItem'+i])).click()
|
|
||||||
//console.log(node.find(':input'))
|
|
||||||
//node.find(':input')[0].focus()
|
|
||||||
})
|
})
|
||||||
|
this.props.closeContextMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteTapped = (i) => {
|
deleteTapped = (i) => {
|
||||||
|
|
@ -276,12 +370,63 @@ export default class LibraryNav extends React.Component {
|
||||||
this.props.updateContextMenu(this.contextMenuItems(i))
|
this.props.updateContextMenu(this.contextMenuItems(i))
|
||||||
this.props.openContextMenu(x, y)
|
this.props.openContextMenu(x, y)
|
||||||
}
|
}
|
||||||
|
this.refs.mainList.setIndex(-1)
|
||||||
|
};
|
||||||
|
|
||||||
|
notebookList = () => {
|
||||||
|
return (<div id="notebook-list">
|
||||||
|
|
||||||
|
{this.state.notebooks.map((notebook, i) =>{
|
||||||
|
var l = null
|
||||||
|
|
||||||
|
if (notebook.state == 'editing'){
|
||||||
|
l = <ListItem
|
||||||
|
key={notebook.uuid || i}
|
||||||
|
value={i}
|
||||||
|
innerDivStyle={{'paddingBottom': 0,
|
||||||
|
'paddingTop': 0}}
|
||||||
|
leftIcon={<NoteBook color={colors.grey500}/>}
|
||||||
|
rightIcon={<Badge badgeContent={notebook.notes}/>}>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
ref={"textField"+i}
|
||||||
|
fullWidth={true}
|
||||||
|
hintText={notebook.title.trunc(18) || "Notebook Name"}
|
||||||
|
underlineShow={false}
|
||||||
|
onBlur={this.newNotebookUnfocus.bind(this, i)}
|
||||||
|
onEnterKeyDown={this.newNotebookUnfocus.bind(this, i)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</ListItem>
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
l = <ListItem
|
||||||
|
key={notebook.uuid || i}
|
||||||
|
value={i}
|
||||||
|
primaryText={notebook.title.trunc(18)}
|
||||||
|
tooltip={notebook.title}
|
||||||
|
ref={notebook.title+i}
|
||||||
|
className="noselect"
|
||||||
|
onTouchTap={this.noteBookTapped.bind(this, i)}
|
||||||
|
leftIcon={<NoteBook onTouchTap={this.notebookIconTapped.bind(this, i)} color={colors.grey500}/>}
|
||||||
|
rightIcon={<Badge
|
||||||
|
style={{'padding': 0}}
|
||||||
|
badgeContent={notebook.notes} />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
})}
|
||||||
|
</div>)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
notebookIconTapped = (i, ev) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
return (
|
return (
|
||||||
<div id={this.props.id} className={this.props.className || ""}>
|
<div id={this.props.id} className={this.props.className || ""}>
|
||||||
<SelectableList id="main-nav" subheader="Library">
|
<SelectableList ref='mainList' className="list" id="main-nav" subheader="Library">
|
||||||
{this.state.navItems.map((item, i) => {
|
{this.state.navItems.map((item, i) => {
|
||||||
return <ListItem
|
return <ListItem
|
||||||
primaryText={item.name}
|
primaryText={item.name}
|
||||||
|
|
@ -295,7 +440,7 @@ export default class LibraryNav extends React.Component {
|
||||||
</SelectableList>
|
</SelectableList>
|
||||||
<Divider />
|
<Divider />
|
||||||
<div>
|
<div>
|
||||||
<List id="nblist" subheader={<div>
|
<SelectableList id="nblist" className="list" ref='notebookList' subheader={<div>
|
||||||
<div className="inline">NoteBooks</div>
|
<div className="inline">NoteBooks</div>
|
||||||
<IconButton
|
<IconButton
|
||||||
onTouchTap={this.addNotebookTapped}
|
onTouchTap={this.addNotebookTapped}
|
||||||
|
|
@ -306,43 +451,8 @@ export default class LibraryNav extends React.Component {
|
||||||
color={colors.grey500}/>
|
color={colors.grey500}/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>}>
|
</div>}>
|
||||||
<div id="notebook-list">
|
{this.notebookList()}
|
||||||
|
</SelectableList>
|
||||||
{this.state.notebooks.map((notebook, i) =>{
|
|
||||||
var l = null
|
|
||||||
|
|
||||||
if (notebook.state == 'editing'){
|
|
||||||
l = <ListItem
|
|
||||||
key={i}
|
|
||||||
innerDivStyle={{'paddingBottom': 0,
|
|
||||||
'paddingTop': 0}}
|
|
||||||
primaryText={<TextField
|
|
||||||
ref={"textField"+i}
|
|
||||||
fullWidth={true}
|
|
||||||
hintText={notebook.title || "Notebook Name"}
|
|
||||||
underlineShow={false}
|
|
||||||
onBlur={this.newNotebookUnfocus.bind(this, i)}
|
|
||||||
onEnterKeyDown={this.newNotebookUnfocus.bind(this, i)}
|
|
||||||
/>}
|
|
||||||
leftIcon={<NoteBook color={colors.grey500}/>}
|
|
||||||
rightIcon={<Badge badgeContent={notebook.notes} />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
l = <ListItem
|
|
||||||
key={i}
|
|
||||||
primaryText={notebook.title}
|
|
||||||
ref={notebook.title+i}
|
|
||||||
className="noselect"
|
|
||||||
onTouchTap={this.noteBookTapped.bind(this, i)}
|
|
||||||
leftIcon={<NoteBook color={colors.grey500}/>}
|
|
||||||
rightIcon={<Badge badgeContent={notebook.notes} />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</List>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
122
app/containers/App.jsx
Normal file
122
app/containers/App.jsx
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
import React from 'react'
|
||||||
|
import getMuiTheme from 'material-ui/lib/styles/getMuiTheme'
|
||||||
|
import LibraryNav from 'LibraryNav'
|
||||||
|
import Styles from 'material-ui/lib/styles'
|
||||||
|
import Rethink from 'rethinkdbdash'
|
||||||
|
import mui from 'material-ui'
|
||||||
|
import * as ContextMenuActions from '../actions'
|
||||||
|
|
||||||
|
import { bindActionCreators } from 'redux'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
Popover,
|
||||||
|
Menu,
|
||||||
|
MenuItem} = mui
|
||||||
|
|
||||||
|
const DefaultRawTheme = Styles.LightRawTheme
|
||||||
|
|
||||||
|
let r = Rethink({
|
||||||
|
db: 'technote',
|
||||||
|
servers: [
|
||||||
|
{host: '162.243.255.144',
|
||||||
|
port: 28015}
|
||||||
|
]})
|
||||||
|
|
||||||
|
function createTables(){
|
||||||
|
r.tableCreate('notes').run()
|
||||||
|
.then(function(){
|
||||||
|
r.table('notes').indexCreate('account_id').run()
|
||||||
|
})
|
||||||
|
.error(function(){})
|
||||||
|
|
||||||
|
r.tableCreate('accounts').run().error(function(){})
|
||||||
|
}
|
||||||
|
|
||||||
|
class App extends React.Component {
|
||||||
|
constructor(props, context){
|
||||||
|
super(props, context)
|
||||||
|
createTables()
|
||||||
|
this.state = {
|
||||||
|
entries: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get childContextTypes(){
|
||||||
|
return {muiTheme: React.PropTypes.object}
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildContext() {
|
||||||
|
return {
|
||||||
|
muiTheme: getMuiTheme(DefaultRawTheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entriesTapped = () => {
|
||||||
|
r.table('notes').getAll('jyapayne@gmail.com', {index: 'account_id'}).run().then(
|
||||||
|
function(notes){
|
||||||
|
}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
handleRequestClose = () => {
|
||||||
|
this.props.contextMenuActions.closeContextMenu()
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const { contextMenu, contextMenuActions } = this.props
|
||||||
|
return (
|
||||||
|
<div className="fill-height">
|
||||||
|
<div style={{position: 'absolute',
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
top: contextMenu.y,
|
||||||
|
left: contextMenu.x}} ref='menuPos'></div>
|
||||||
|
<Popover
|
||||||
|
open={contextMenu.opened}
|
||||||
|
anchorEl={this.refs.menuPos}
|
||||||
|
anchorOrigin={{horizontal: 'middle', vertical: 'bottom'}}
|
||||||
|
targetOrigin={{horizontal: 'left', vertical: 'top'}}
|
||||||
|
onRequestClose={this.handleRequestClose}>
|
||||||
|
<Menu desktop={true}>
|
||||||
|
{contextMenu.items.map(function (el, i){
|
||||||
|
return el;
|
||||||
|
})}
|
||||||
|
</Menu>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<LibraryNav
|
||||||
|
id="library-nav"
|
||||||
|
ref="libraryNav"
|
||||||
|
entriesTapped={this.entriesTapped}
|
||||||
|
className="left inline fill-height"
|
||||||
|
{...contextMenuActions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App.propTypes = {
|
||||||
|
contextMenu: React.PropTypes.object.isRequired,
|
||||||
|
contextMenuActions: React.PropTypes.object.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
contextMenu: state.contextMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return {
|
||||||
|
contextMenuActions: bindActionCreators(ContextMenuActions, dispatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(App)
|
||||||
142
app/enhance/SelectableEnhance.jsx
Normal file
142
app/enhance/SelectableEnhance.jsx
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
import React from 'react';
|
||||||
|
import getMuiTheme from 'material-ui/lib/styles/getMuiTheme';
|
||||||
|
import StylePropable from 'material-ui/lib/mixins/style-propable';
|
||||||
|
import ColorManipulator from 'material-ui/lib/utils/color-manipulator';
|
||||||
|
|
||||||
|
export const SelectableContainerEnhance = (Component) => {
|
||||||
|
const composed = React.createClass({
|
||||||
|
|
||||||
|
displayName: `Selectable${Component.displayName}`,
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
children: React.PropTypes.node,
|
||||||
|
selectedItemStyle: React.PropTypes.object,
|
||||||
|
valueLink: React.PropTypes.shape({
|
||||||
|
value: React.PropTypes.any,
|
||||||
|
requestChange: React.PropTypes.func,
|
||||||
|
}).isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
contextTypes: {
|
||||||
|
muiTheme: React.PropTypes.object,
|
||||||
|
},
|
||||||
|
|
||||||
|
childContextTypes: {
|
||||||
|
muiTheme: React.PropTypes.object,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [
|
||||||
|
StylePropable,
|
||||||
|
],
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
muiTheme: this.context.muiTheme || getMuiTheme(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getChildContext() {
|
||||||
|
return {
|
||||||
|
muiTheme: this.state.muiTheme,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
|
let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme;
|
||||||
|
this.setState({muiTheme: newMuiTheme});
|
||||||
|
},
|
||||||
|
|
||||||
|
getValueLink: function(props) {
|
||||||
|
return props.valueLink || {
|
||||||
|
value: props.value,
|
||||||
|
requestChange: props.onChange,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
extendChild(child, styles, selectedItemStyle) {
|
||||||
|
if (child && child.type && child.type.displayName === 'ListItem') {
|
||||||
|
let selected = this.isChildSelected(child, this.props);
|
||||||
|
let selectedChildrenStyles = {};
|
||||||
|
if (selected) {
|
||||||
|
selectedChildrenStyles = this.mergeStyles(styles, selectedItemStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mergedChildrenStyles = this.mergeStyles(child.props.style || {}, selectedChildrenStyles);
|
||||||
|
|
||||||
|
this.keyIndex += 1;
|
||||||
|
|
||||||
|
return React.cloneElement(child, {
|
||||||
|
onTouchTap: (e) => {
|
||||||
|
this.handleItemTouchTap(e, child);
|
||||||
|
if (child.props.onTouchTap) {
|
||||||
|
child.props.onTouchTap(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
key: this.keyIndex,
|
||||||
|
style: mergedChildrenStyles,
|
||||||
|
nestedItems: child.props.nestedItems.map((child) => this.extendChild(child, styles, selectedItemStyle)),
|
||||||
|
initiallyOpen: this.isInitiallyOpen(child),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var Type = child.type
|
||||||
|
return <Type {...child.props} {...child.state}>{React.Children.map(child.props.children, (childx) => this.extendChild(childx, styles, selectedItemStyle))}</Type>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isInitiallyOpen(child) {
|
||||||
|
if (child.props.initiallyOpen) {
|
||||||
|
return child.props.initiallyOpen;
|
||||||
|
}
|
||||||
|
return this.hasSelectedDescendant(false, child);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasSelectedDescendant(previousValue, child) {
|
||||||
|
if (React.isValidElement(child) && child.props.nestedItems && child.props.nestedItems.length > 0) {
|
||||||
|
return child.props.nestedItems.reduce(this.hasSelectedDescendant, previousValue);
|
||||||
|
}
|
||||||
|
return previousValue || this.isChildSelected(child, this.props);
|
||||||
|
},
|
||||||
|
|
||||||
|
isChildSelected(child, props) {
|
||||||
|
let itemValue = this.getValueLink(props).value;
|
||||||
|
let childValue = child.props.value;
|
||||||
|
|
||||||
|
return (itemValue === childValue);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleItemTouchTap(e, item) {
|
||||||
|
let valueLink = this.getValueLink(this.props);
|
||||||
|
let itemValue = item.props.value;
|
||||||
|
let menuValue = valueLink.value;
|
||||||
|
if ( itemValue !== menuValue) {
|
||||||
|
valueLink.requestChange(e, itemValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {children, selectedItemStyle} = this.props;
|
||||||
|
this.keyIndex = 0;
|
||||||
|
let styles = {};
|
||||||
|
|
||||||
|
if (!selectedItemStyle) {
|
||||||
|
let textColor = this.state.muiTheme.rawTheme.palette.textColor;
|
||||||
|
let selectedColor = ColorManipulator.fade(textColor, 0.2);
|
||||||
|
styles = {
|
||||||
|
backgroundColor: selectedColor,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let newChildren = React.Children.map(children, (child) => this.extendChild(child, styles, selectedItemStyle));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Component {...this.props} {...this.state}>
|
||||||
|
{newChildren}
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return composed;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectableContainerEnhance;
|
||||||
8
app/store/configureStore.jsx
Normal file
8
app/store/configureStore.jsx
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { createStore } from 'redux'
|
||||||
|
import rootReducer from '../reducers'
|
||||||
|
|
||||||
|
export default function configureStore(initialState) {
|
||||||
|
const store = createStore(rootReducer, initialState)
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
10
app/utils.jsx
Normal file
10
app/utils.jsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import path from 'path-extra'
|
||||||
|
|
||||||
|
export function getAppDataPath(){
|
||||||
|
return path.datadir(APP_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNotebookPath(notebook){
|
||||||
|
var notePath = getAppDataPath()
|
||||||
|
return path.join(notePath, notebook.uuid+'.qvnotebook')
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
<div id="main">
|
<div id="main">
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
var APP_NAME = 'TechNote'
|
||||||
window.$ = window.jQuery = require('./app/static/jquery-1.12.0.min.js');
|
window.$ = window.jQuery = require('./app/static/jquery-1.12.0.min.js');
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
document.addEventListener("keydown", function (e) {
|
document.addEventListener("keydown", function (e) {
|
||||||
|
|
@ -36,6 +37,6 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="dist/bundle.js"></script>
|
<script type="text/javascript" src="app/dist/bundle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
17
index.jsx
Normal file
17
index.jsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react'
|
||||||
|
import injectTapEventPlugin from "react-tap-event-plugin"
|
||||||
|
injectTapEventPlugin()
|
||||||
|
import { render } from 'react-dom'
|
||||||
|
import { Provider } from 'react-redux'
|
||||||
|
import App from './app/containers/App'
|
||||||
|
import configureStore from './app/store/configureStore'
|
||||||
|
|
||||||
|
const store = configureStore()
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<App />
|
||||||
|
</Provider>,
|
||||||
|
document.getElementById('main')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
@ -57,9 +57,14 @@
|
||||||
"linux-x64": true
|
"linux-x64": true
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"glob": "^7.0.0",
|
||||||
|
"jsonfile": "^2.2.3",
|
||||||
"material-ui": "^0.14.4",
|
"material-ui": "^0.14.4",
|
||||||
"materialize-css": "^0.97.5",
|
"materialize-css": "^0.97.5",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
"moment": "^2.11.2",
|
"moment": "^2.11.2",
|
||||||
|
"node-uuid": "^1.4.7",
|
||||||
|
"path-extra": "^3.0.0",
|
||||||
"react": "^0.14.7",
|
"react": "^0.14.7",
|
||||||
"react-dom": "^0.14.7",
|
"react-dom": "^0.14.7",
|
||||||
"react-quill": "^0.4.1",
|
"react-quill": "^0.4.1",
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,11 @@ body, html{
|
||||||
.noselect{
|
.noselect{
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list > div:first-child{
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
.inline{
|
.inline{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: './app/main.jsx',
|
entry: './index.jsx',
|
||||||
output: {
|
output: {
|
||||||
path: __dirname + "/dist",
|
path: __dirname + "/app/dist",
|
||||||
filename: "bundle.js",
|
filename: "bundle.js",
|
||||||
sourceMapFilename: 'bundle.map'
|
sourceMapFilename: 'bundle.map'
|
||||||
},
|
},
|
||||||
|
|
@ -23,7 +23,8 @@ module.exports = {
|
||||||
resolve:{
|
resolve:{
|
||||||
extensions: ['', '.js', '.jsx'],
|
extensions: ['', '.js', '.jsx'],
|
||||||
root: [
|
root: [
|
||||||
path.resolve('./app/modules')
|
path.resolve('./app/components'),
|
||||||
|
path.resolve('./app')
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue