Add global state and right click menu using redux

This commit is contained in:
Joey Payne 2016-02-19 12:48:47 -07:00
commit 54d64cc124
10 changed files with 190 additions and 81 deletions

13
app/actions/index.jsx Normal file
View file

@ -0,0 +1,13 @@
import * as types from '../constants'
export function updateContextMenu(items) {
return { type: types.UPDATE_CONTEXT_MENU, items }
}
export function openContextMenu(x, y){
return {type: types.OPEN_CONTEXT_MENU, x, y}
}
export function closeContextMenu(){
return {type: types.CLOSE_CONTEXT_MENU}
}

3
app/constants/index.jsx Normal file
View file

@ -0,0 +1,3 @@
export const UPDATE_CONTEXT_MENU = "UPDATE_CONTEXT_MENU"
export const OPEN_CONTEXT_MENU = "OPEN_CONTEXT_MENU"
export const CLOSE_CONTEXT_MENU = "CLOSE_CONTEXT_MENU"

View file

@ -11,6 +11,20 @@ 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 moment from 'moment' import moment from 'moment'
import mui from 'material-ui' import mui from 'material-ui'
import {Provider} from 'react-redux'
import { createStore } from 'redux'
import rootReducer from './reducers'
import * as ContextMenuActions from './actions'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
function getStore(reducer, initialState){
return createStore(reducer, initialState)
}
const store = getStore(rootReducer)
const { const {
Popover, Popover,
@ -43,11 +57,18 @@ class Main extends React.Component {
createTables() createTables()
this.state = { this.state = {
entries: [], entries: [],
contextMenuOpen: false,
contextMenuItems: this.context.contextMenuItems
} }
} }
static get childContextTypes(){
return {muiTheme: React.PropTypes.object}
}
getChildContext() {
return {
muiTheme: getMuiTheme(DefaultRawTheme)
}
}
entriesTapped = () => { entriesTapped = () => {
r.table('notes').getAll('jyapayne@gmail.com', {index: 'account_id'}).run().then( r.table('notes').getAll('jyapayne@gmail.com', {index: 'account_id'}).run().then(
@ -58,33 +79,27 @@ class Main extends React.Component {
}; };
handleRequestClose = () => { handleRequestClose = () => {
this.setState({ this.props.contextMenuActions.closeContextMenu()
contextMenuOpen: false,
})
}; };
tapped = () => {
console.log('tapped')
console.log(this.context.contextMenuItems)
};
render() { render() {
const { contextMenu, contextMenuActions } = this.props
return ( return (
<div onTouchTap={this.tapped}> <div className="fill-height">
<div style={{position: 'absolute', <div style={{position: 'absolute',
width: 1, width: 1,
height: 1, height: 1,
top: this.state.popTop, top: contextMenu.y,
left: this.state.popLeft}} ref='menuPos'></div> left: contextMenu.x}} ref='menuPos'></div>
<Popover <Popover
open={this.state.contextMenuOpen} open={contextMenu.opened}
anchorEl={this.refs.menuPos} anchorEl={this.refs.menuPos}
anchorOrigin={{horizontal: 'middle', vertical: 'bottom'}} anchorOrigin={{horizontal: 'middle', vertical: 'bottom'}}
targetOrigin={{horizontal: 'left', vertical: 'top'}} targetOrigin={{horizontal: 'left', vertical: 'top'}}
onRequestClose={this.handleRequestClose}> onRequestClose={this.handleRequestClose}>
<Menu desktop={true}> <Menu desktop={true}>
{this.state.contextMenuItems.map(function (el, i){ {contextMenu.items.map(function (el, i){
return el; return el;
})} })}
</Menu> </Menu>
@ -94,39 +109,40 @@ class Main extends React.Component {
id="library-nav" id="library-nav"
ref="libraryNav" ref="libraryNav"
entriesTapped={this.entriesTapped} entriesTapped={this.entriesTapped}
className="left inline fill-height"/> className="left inline fill-height"
{...contextMenuActions}
/>
</div> </div>
) )
} }
}
class S extends React.Component {
static get childContextTypes(){
return {muiTheme: React.PropTypes.object,
contextMenuItems: React.PropTypes.array}
}
getChildContext() {
return {
muiTheme: getMuiTheme(DefaultRawTheme),
contextMenuItems: []
}
}
render(){
return <Main />
}
} }
Main.propTypes = {
contextMenu: React.PropTypes.object.isRequired,
contextMenuActions: React.PropTypes.object.isRequired
}
Main.contextTypes = { function mapStateToProps(state) {
contextMenuItems: React.PropTypes.array.isRequired return {
}; contextMenu: state.contextMenu
}
}
function mapDispatchToProps(dispatch) {
return {
contextMenuActions: bindActionCreators(ContextMenuActions, dispatch)
}
}
let App = connect(
mapStateToProps,
mapDispatchToProps
)(Main)
export default App
ReactDOM.render( ReactDOM.render(
<S />, <Provider store={store}><App /></Provider>,
document.getElementById('main') document.getElementById('main')
); );

View file

@ -1,4 +1,5 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom'
import mui from 'material-ui' import mui from 'material-ui'
import getMuiTheme from 'material-ui/lib/styles/getMuiTheme' import getMuiTheme from 'material-ui/lib/styles/getMuiTheme'
@ -18,6 +19,16 @@ import { SelectableContainerEnhance } from 'material-ui/lib/hoc/selectable-enhan
const colors = styles.Colors const colors = styles.Colors
function eventFire(el, etype){
if (el.fireEvent) {
el.fireEvent('on' + etype);
} else {
var evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
el.dispatchEvent(evObj);
}
}
const {AppBar, const {AppBar,
AppCanvas, AppCanvas,
FontIcon, FontIcon,
@ -71,7 +82,6 @@ export default class LibraryNav extends React.Component {
constructor(props, context){ constructor(props, context){
super(props, context) super(props, context)
console.log(this.state)
this.state = { this.state = {
open: false, open: false,
navItems: [ navItems: [
@ -109,6 +119,17 @@ export default class LibraryNav extends React.Component {
], ],
notebooks: [ notebooks: [
{'state': 'editing', 'title': '', 'notes': 0}, {'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} {'state': 'displaying', 'title': 'FieldNotes', 'notes': 10}
] ]
} }
@ -139,6 +160,10 @@ export default class LibraryNav extends React.Component {
nb.state = 'displaying' nb.state = 'displaying'
this.setState({notebooks: this.state.notebooks}) this.setState({notebooks: this.state.notebooks})
} }
else if(nb.title){
nb.state = 'displaying'
this.setState({notebooks: this.state.notebooks})
}
}; };
addNotebookTapped = () => { addNotebookTapped = () => {
@ -160,49 +185,53 @@ export default class LibraryNav extends React.Component {
} }
}; };
renameTapped = (i) => {
var nbs = this.state.notebooks
nbs[i].state = 'editing'
this.setState({notebooks: nbs})
this.props.closeContextMenu()
//ReactDOM.findDOMNode(this.refs[nbs[i].title+i]).click()
}
deleteTapped = (i) => {
var nbs = this.state.notebooks
nbs.splice(i, 1)
this.setState({notebooks: nbs})
this.props.closeContextMenu()
}
contextMenuItems = (i) => {
return [
<MenuItem
key='rename'
primaryText="Rename"
leftIcon={<Edit />}
onTouchTap={this.renameTapped.bind(this, i)}/>,
<Divider key='div1'/>,
<MenuItem
key='delete'
primaryText="Delete"
onTouchTap={this.deleteTapped.bind(this, i)}
leftIcon={<Delete />} />
]
};
noteBookTapped = (i, ev) => { noteBookTapped = (i, ev) => {
var nativeEvent = ev.nativeEvent var nativeEvent = ev.nativeEvent
if(nativeEvent.button == 2){ if(nativeEvent.button == 2){
//Right click //Right click
var x = nativeEvent.pageX var x = nativeEvent.pageX
var y = nativeEvent.pageY var y = nativeEvent.pageY
this.context.contextMenuItems = [
<MenuItem primaryText="Rename" leftIcon={<Edit />} />,
<Divider />,
<MenuItem primaryText="Delete" leftIcon={<Delete />} />
]
this.setState({ this.props.updateContextMenu(this.contextMenuItems(i))
popTop: y, this.props.openContextMenu(x, y)
popLeft: x,
open: true
})
} }
}; };
handleRequestClose = () => {
this.setState({
open: false,
})
};
render(){ render(){
return ( return (
<div id={this.props.id} className={this.props.className || ""}> <div id={this.props.id} className={this.props.className || ""}>
<div style={{position: 'absolute', width: 1, height: 1, top: this.state.popTop, left: this.state.popLeft}} ref='menuPos'></div>
<Popover
open={this.state.open}
anchorEl={this.refs.menuPos}
anchorOrigin={{horizontal: 'middle', vertical: 'bottom'}}
targetOrigin={{horizontal: 'left', vertical: 'top'}}
onRequestClose={this.handleRequestClose}>
<Menu desktop={true}>
<MenuItem primaryText="Rename" leftIcon={<Edit />} />
<Divider />
<MenuItem primaryText="Delete" leftIcon={<Delete />} />
</Menu>
</Popover>
<SelectableList subheader="Library"> <SelectableList subheader="Library">
{this.state.navItems.map((item, i) => { {this.state.navItems.map((item, i) => {
return <ListItem return <ListItem
@ -216,6 +245,7 @@ export default class LibraryNav extends React.Component {
})} })}
</SelectableList> </SelectableList>
<Divider /> <Divider />
<div>
<List subheader={<div> <List subheader={<div>
<div className="inline">NoteBooks</div> <div className="inline">NoteBooks</div>
<IconButton <IconButton
@ -227,6 +257,7 @@ export default class LibraryNav extends React.Component {
color={colors.grey500}/> color={colors.grey500}/>
</IconButton> </IconButton>
</div>}> </div>}>
<div>
{this.state.notebooks.map((notebook, i) =>{ {this.state.notebooks.map((notebook, i) =>{
var l = null var l = null
@ -252,6 +283,7 @@ export default class LibraryNav extends React.Component {
l = <ListItem l = <ListItem
key={i} key={i}
primaryText={notebook.title} primaryText={notebook.title}
ref={notebook.title+i}
className="noselect" className="noselect"
onTouchTap={this.noteBookTapped.bind(this, i)} onTouchTap={this.noteBookTapped.bind(this, i)}
leftIcon={<NoteBook color={colors.grey500}/>} leftIcon={<NoteBook color={colors.grey500}/>}
@ -260,14 +292,11 @@ export default class LibraryNav extends React.Component {
} }
return l return l
})} })}
</div>
</List> </List>
</div>
</div> </div>
) )
} }
} }
LibraryNav.contextTypes = {
contextMenuItems: React.PropTypes.array.isRequired
};

View file

@ -0,0 +1,31 @@
import { UPDATE_CONTEXT_MENU, OPEN_CONTEXT_MENU, CLOSE_CONTEXT_MENU } from '../constants'
const initialState =
{
opened: false,
x: 0,
y: 0,
items: []
}
export default function contextMenu(state = initialState, action){
switch (action.type) {
case UPDATE_CONTEXT_MENU:
return Object.assign({}, state,{
items: action.items
})
case OPEN_CONTEXT_MENU:
return Object.assign({}, state, {
opened: true,
x: action.x,
y: action.y
})
case CLOSE_CONTEXT_MENU:
return Object.assign({}, state, {
opened: false
})
default:
return state
}
}

8
app/reducers/index.jsx Normal file
View file

@ -0,0 +1,8 @@
import { combineReducers } from 'redux'
import contextMenu from './contextMenu'
const rootReducer = combineReducers({
contextMenu
})
export default rootReducer

View file

@ -10,7 +10,7 @@
</style> </style>
</head> </head>
<body style="width:100%; height:100%"> <body style="width:100%; height:100%">
<div id="main" style="height:100%"> <div id="main">
</div> </div>
<script> <script>
document.addEventListener("keydown", function (e) { document.addEventListener("keydown", function (e) {
@ -20,6 +20,15 @@
location.reload(); location.reload();
} }
}); });
function resize()
{
var heights = window.innerHeight;
document.getElementById("main").style.height = heights + "px";
}
resize();
window.onresize = function() {
resize();
};
</script> </script>
<script type="text/javascript" src="dist/bundle.js"></script> <script type="text/javascript" src="dist/bundle.js"></script>
</body> </body>

View file

@ -11,11 +11,7 @@ var mainWindow = null;
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function() { app.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform != 'darwin') {
app.quit(); app.quit();
}
}); });
// This method will be called when Electron has finished // This method will be called when Electron has finished

View file

@ -63,7 +63,9 @@
"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",
"react-redux": "^4.4.0",
"react-tap-event-plugin": "^0.2.2", "react-tap-event-plugin": "^0.2.2",
"redux": "^3.3.1",
"rethinkdbdash": "^2.2.17" "rethinkdbdash": "^2.2.17"
}, },
"ppapi_flash_version": "", "ppapi_flash_version": "",
@ -149,6 +151,7 @@
"babel-preset-stage-2": "^6.5.0", "babel-preset-stage-2": "^6.5.0",
"babel-preset-stage-3": "^6.5.0", "babel-preset-stage-3": "^6.5.0",
"http-server": "^0.8.4", "http-server": "^0.8.4",
"redux-devtools": "^3.1.1",
"webpack": "*", "webpack": "*",
"webpack-dev-server": "^1.11.0" "webpack-dev-server": "^1.11.0"
}, },

View file

@ -3,7 +3,6 @@ body, html{
height: 100%; height: 100%;
} }
#main{ #main{
min-height: 100%;
} }
.fill-height{ .fill-height{
height: 100%; height: 100%;
@ -23,4 +22,6 @@ body, html{
#library-nav{ #library-nav{
border-right: solid 1px #d9d9d9; border-right: solid 1px #d9d9d9;
width: 300px; width: 300px;
height: 100%;
min-height: 100%;
} }