From d18d9a8f97216afae4a6c9d8d659952ed5cba765 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 May 2016 12:30:03 -0700 Subject: [PATCH 1/4] Add animated sandwich button --- package.json | 2 ++ ui/app/actions.js | 11 +++++++++++ ui/app/app.js | 14 +++++++++++--- ui/app/reducers/app.js | 6 ++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index d7f41097f..bb15d658b 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "identicon.js": "^1.2.1", "inject-css": "^0.1.1", "jazzicon": "^1.1.3", + "menu-droppo": "^1.0.2", "metamask-logo": "^1.1.5", "multiplex": "^6.7.0", "once": "^1.3.3", @@ -52,6 +53,7 @@ "redux": "^3.0.5", "redux-logger": "^2.3.1", "redux-thunk": "^1.0.2", + "sandwich-expando": "^1.0.4", "textarea-caret": "^3.0.1", "three.js": "^0.73.2", "through2": "^2.0.1", diff --git a/ui/app/actions.js b/ui/app/actions.js index f489eede7..c08019d9c 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1,6 +1,9 @@ var actions = { GO_HOME: 'GO_HOME', goHome: goHome, + // menu state + TOGGLE_MENU: 'TOGGLE_MENU', + toggleMenu: toggleMenu, // remote state UPDATE_METAMASK_STATE: 'UPDATE_METAMASK_STATE', updateMetamaskState: updateMetamaskState, @@ -105,6 +108,14 @@ function goHome() { } } +// menu state + +function toggleMenu() { + return { + type: this.TOGGLE_MENU, + } +} + // async actions function tryUnlockMetamask(password) { diff --git a/ui/app/app.js b/ui/app/app.js index 68d34e52f..ec869145e 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -24,6 +24,8 @@ const ConfigScreen = require('./config') const InfoScreen = require('./info') const LoadingIndicator = require('./loading') const txHelper = require('../lib/tx-helper') +const SandwichExpando = require('sandwich-expando') +const MenuDroppo = require('menu-droppo') module.exports = connect(mapStateToProps)(App) @@ -42,6 +44,7 @@ function mapStateToProps(state) { seedWords: state.metamask.seedWords, unconfTxs: state.metamask.unconfTxs, unconfMsgs: state.metamask.unconfMsgs, + menuOpen: state.appState.menuOpen, } } @@ -143,12 +146,17 @@ App.prototype.renderAppBar = function(){ src: '/images/icon-128.png', }), - // metamask name + // metamask namlterChangese h('h1', 'MetaMask'), // hamburger - h('i.fa.fa-bars.cursor-pointer.color-orange', { - onClick: (event) => state.dispatch(actions.showConfigPage()), + h(SandwichExpando, { + width: 16, + barHeight: 2, + padding: 0, + isOpen: state.menuOpen, + color: 'rgb(247,146,30)', + onClick: () => this.props.dispatch(actions.toggleMenu()), }), ]) diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 0e0740c9d..a7429c8fb 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -22,6 +22,7 @@ function reduceApp(state, action) { var seedWords = state.metamask.seedWords var appState = extend({ + menuOpen: false, currentView: seedWords ? seedConfView : defaultView, accountDetail: { subview: 'transactions', @@ -34,6 +35,11 @@ function reduceApp(state, action) { switch (action.type) { + case actions.TOGGLE_MENU: + return extend(appState, { + menuOpen: !appState.menuOpen, + }) + // intialize case actions.SHOW_CREATE_VAULT: From d0b0526765000ab6f56e8c35545d66a760ed7b61 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 May 2016 14:36:35 -0700 Subject: [PATCH 2/4] Add dynamic list item styles --- package.json | 4 +- ui/app/actions.js | 9 +++ ui/app/app.js | 99 +++++++++++++++++++++-------- ui/app/components/drop-menu-item.js | 31 +++++++++ ui/app/reducers/app.js | 5 ++ 5 files changed, 121 insertions(+), 27 deletions(-) create mode 100644 ui/app/components/drop-menu-item.js diff --git a/package.json b/package.json index bb15d658b..4c8f66633 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "identicon.js": "^1.2.1", "inject-css": "^0.1.1", "jazzicon": "^1.1.3", - "menu-droppo": "^1.0.2", + "menu-droppo": "^1.0.3", "metamask-logo": "^1.1.5", "multiplex": "^6.7.0", "once": "^1.3.3", @@ -53,7 +53,7 @@ "redux": "^3.0.5", "redux-logger": "^2.3.1", "redux-thunk": "^1.0.2", - "sandwich-expando": "^1.0.4", + "sandwich-expando": "^1.0.5", "textarea-caret": "^3.0.1", "three.js": "^0.73.2", "through2": "^2.0.1", diff --git a/ui/app/actions.js b/ui/app/actions.js index c08019d9c..ee5e417d4 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -4,6 +4,8 @@ var actions = { // menu state TOGGLE_MENU: 'TOGGLE_MENU', toggleMenu: toggleMenu, + SET_MENU_STATE: 'SET_MENU_STATE', + closeMenu: closeMenu, // remote state UPDATE_METAMASK_STATE: 'UPDATE_METAMASK_STATE', updateMetamaskState: updateMetamaskState, @@ -116,6 +118,13 @@ function toggleMenu() { } } +function closeMenu() { + return { + type: this.SET_MENU_STATE, + value: false, + } +} + // async actions function tryUnlockMetamask(password) { diff --git a/ui/app/app.js b/ui/app/app.js index ec869145e..2f4136b78 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -26,6 +26,7 @@ const LoadingIndicator = require('./loading') const txHelper = require('../lib/tx-helper') const SandwichExpando = require('sandwich-expando') const MenuDroppo = require('menu-droppo') +const DropMenuItem = require('./components/drop-menu-item') module.exports = connect(mapStateToProps)(App) @@ -130,37 +131,85 @@ App.prototype.renderAppBar = function(){ return ( - h('.app-header.flex-row.flex-space-between', { - style: { - alignItems: 'center', - visibility: state.isUnlocked ? 'visibile' : 'none', - background: state.isUnlocked ? 'white' : 'none', - height: '36px', - }, - }, state.isUnlocked && [ + h('div', [ - // mini logo - h('img', { - height: 24, - width: 24, - src: '/images/icon-128.png', - }), + h('.app-header.flex-row.flex-space-between', { + style: { + alignItems: 'center', + visibility: state.isUnlocked ? 'visibile' : 'none', + background: state.isUnlocked ? 'white' : 'none', + height: '36px', + }, + }, state.isUnlocked && [ - // metamask namlterChangese - h('h1', 'MetaMask'), + // mini logo + h('img', { + height: 24, + width: 24, + src: '/images/icon-128.png', + }), - // hamburger - h(SandwichExpando, { - width: 16, - barHeight: 2, - padding: 0, + // metamask namlterChangese + h('h1', 'MetaMask'), + + // hamburger + h(SandwichExpando, { + width: 16, + barHeight: 2, + padding: 0, + isOpen: state.menuOpen, + color: 'rgb(247,146,30)', + onClick: (event) => { + event.preventDefault() + event.stopPropagation() + this.props.dispatch(actions.toggleMenu()) + }, + }), + ]), + + h(MenuDroppo, { + style: { + right: '0px', + }, + innerStyle: { + background: 'white', + + // This shadow is hidden by the surrounding bounding box. + // Maybe worth revealing in the future: + boxShadow: '1px 1px 2px rgba(0,0,0,0.1)', + float: 'right', + }, isOpen: state.menuOpen, - color: 'rgb(247,146,30)', - onClick: () => this.props.dispatch(actions.toggleMenu()), - }), + onClickOutside: (event) => { + this.props.dispatch(actions.closeMenu()) + }, + }, [ // DROP MENU ITEMS + h('menu', [ + h('style', '.drop-menu-item:hover { background:rgb(235, 235, 235); }'), + h(DropMenuItem, { + closeMenu:() => this.props.dispatch(actions.closeMenu()), + action:() => this.props.dispatch(actions.showConfigPage()), + icon: null, + label: 'Settings' + }), + + h(DropMenuItem, { + closeMenu:() => this.props.dispatch(actions.closeMenu()), + action:() => this.props.dispatch(actions.lockMetamask()), + icon: null, + label: 'Lock Account' + }), + + h(DropMenuItem, { + closeMenu:() => this.props.dispatch(actions.closeMenu()), + action:() => this.props.dispatch(actions.showInfoPage()), + icon: null, + label: 'Help' + }), + ]), + ]), ]) - ) } diff --git a/ui/app/components/drop-menu-item.js b/ui/app/components/drop-menu-item.js new file mode 100644 index 000000000..1adbba519 --- /dev/null +++ b/ui/app/components/drop-menu-item.js @@ -0,0 +1,31 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits + +module.exports = DropMenuItem + + +inherits(DropMenuItem, Component) +function DropMenuItem() { + Component.call(this) +} + +DropMenuItem.prototype.render = function() { + + return h('li.drop-menu-item', { + onClick:() => { + this.props.closeMenu() + this.props.action() + }, + style: { + listStyle: 'none', + padding: '6px 10px 6px 17px', + fontFamily: 'Transat Medium', + color: 'rgb(125, 128, 130)', + cursor: 'pointer', + }, + }, [ + this.props.icon, + this.props.label, + ]) +} diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index a7429c8fb..2cdbaf6f3 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -40,6 +40,11 @@ function reduceApp(state, action) { menuOpen: !appState.menuOpen, }) + case actions.SET_MENU_STATE: + return extend(appState, { + menuOpen: action.value, + }) + // intialize case actions.SHOW_CREATE_VAULT: From 9d0cad0e16414a4334e92fc7e06c48e7730deae6 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 May 2016 17:32:26 -0700 Subject: [PATCH 3/4] Finish clean implementation of drop-down menu --- package.json | 2 +- ui/app/app.js | 105 +++++++++++++++------------- ui/app/components/drop-menu-item.js | 2 +- 3 files changed, 57 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 4c8f66633..99569e5db 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "identicon.js": "^1.2.1", "inject-css": "^0.1.1", "jazzicon": "^1.1.3", - "menu-droppo": "^1.0.3", + "menu-droppo": "^1.1.0", "metamask-logo": "^1.1.5", "multiplex": "^6.7.0", "once": "^1.3.3", diff --git a/ui/app/app.js b/ui/app/app.js index 2f4136b78..66f42c5cc 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -68,6 +68,7 @@ App.prototype.render = function() { // app bar this.renderAppBar(), + this.renderDropdown(), // panel content h('.app-primary.flex-grow' + (transForward ? '.from-right' : '.from-left'), { @@ -139,6 +140,8 @@ App.prototype.renderAppBar = function(){ visibility: state.isUnlocked ? 'visibile' : 'none', background: state.isUnlocked ? 'white' : 'none', height: '36px', + position: 'relative', + zIndex: 1, }, }, state.isUnlocked && [ @@ -166,65 +169,67 @@ App.prototype.renderAppBar = function(){ }, }), ]), - - h(MenuDroppo, { - style: { - right: '0px', - }, - innerStyle: { - background: 'white', - - // This shadow is hidden by the surrounding bounding box. - // Maybe worth revealing in the future: - boxShadow: '1px 1px 2px rgba(0,0,0,0.1)', - float: 'right', - }, - isOpen: state.menuOpen, - onClickOutside: (event) => { - this.props.dispatch(actions.closeMenu()) - }, - }, [ // DROP MENU ITEMS - h('menu', [ - h('style', '.drop-menu-item:hover { background:rgb(235, 235, 235); }'), - - h(DropMenuItem, { - closeMenu:() => this.props.dispatch(actions.closeMenu()), - action:() => this.props.dispatch(actions.showConfigPage()), - icon: null, - label: 'Settings' - }), - - h(DropMenuItem, { - closeMenu:() => this.props.dispatch(actions.closeMenu()), - action:() => this.props.dispatch(actions.lockMetamask()), - icon: null, - label: 'Lock Account' - }), - - h(DropMenuItem, { - closeMenu:() => this.props.dispatch(actions.closeMenu()), - action:() => this.props.dispatch(actions.showInfoPage()), - icon: null, - label: 'Help' - }), - ]), - ]), ]) ) } -App.prototype.renderPrimary = function(state){ - var state = this.props +App.prototype.renderDropdown = function() { + const props = this.props + return h(MenuDroppo, { + isOpen: props.menuOpen, + onClickOutside: (event) => { + this.props.dispatch(actions.closeMenu()) + }, + style: { + position: 'fixed', + right: 0, + zIndex: 0, + }, + innerStyle: { + background: 'white', + boxShadow: '1px 1px 2px rgba(0,0,0,0.1)', + }, + }, [ // DROP MENU ITEMS + h('style', ` + .drop-menu-item:hover { background:rgb(235, 235, 235); } + .drop-menu-item i { margin: 11px; } + `), - if (state.seedWords) { + h(DropMenuItem, { + label: 'Settings', + closeMenu:() => this.props.dispatch(actions.closeMenu()), + action:() => this.props.dispatch(actions.showConfigPage()), + icon: h('i.fa.fa-gear.fa-lg', { ariaHidden: true }), + }), + + h(DropMenuItem, { + label: 'Lock Account', + closeMenu:() => this.props.dispatch(actions.closeMenu()), + action:() => this.props.dispatch(actions.lockMetamask()), + icon: h('i.fa.fa-lock.fa-lg', { ariaHidden: true }), + }), + + h(DropMenuItem, { + label: 'Help', + closeMenu:() => this.props.dispatch(actions.closeMenu()), + action:() => this.props.dispatch(actions.showInfoPage()), + icon: h('i.fa.fa-question.fa-lg', { ariaHidden: true }), + }), + ]) +} + +App.prototype.renderPrimary = function(){ + var props = this.props + + if (props.seedWords) { return h(CreateVaultCompleteScreen, {key: 'createVaultComplete'}) } // show initialize screen - if (!state.isInitialized) { + if (!props.isInitialized) { // show current view - switch (state.currentView.name) { + switch (props.currentView.name) { case 'createVault': return h(CreateVaultScreen, {key: 'createVault'}) @@ -242,12 +247,12 @@ App.prototype.renderPrimary = function(state){ } // show unlock screen - if (!state.isUnlocked) { + if (!props.isUnlocked) { return h(UnlockScreen, {key: 'locked'}) } // show current view - switch (state.currentView.name) { + switch (props.currentView.name) { case 'accounts': return h(AccountsScreen, {key: 'accounts'}) diff --git a/ui/app/components/drop-menu-item.js b/ui/app/components/drop-menu-item.js index 1adbba519..c8e61278c 100644 --- a/ui/app/components/drop-menu-item.js +++ b/ui/app/components/drop-menu-item.js @@ -19,7 +19,7 @@ DropMenuItem.prototype.render = function() { }, style: { listStyle: 'none', - padding: '6px 10px 6px 17px', + padding: '6px 16px 6px 5px', fontFamily: 'Transat Medium', color: 'rgb(125, 128, 130)', cursor: 'pointer', From e64e3bbea54eeb946eeae70ced1c381ed52fa4e3 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 18 May 2016 17:47:30 -0700 Subject: [PATCH 4/4] Fix typos --- ui/app/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/app.js b/ui/app/app.js index 66f42c5cc..511012fab 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -137,7 +137,7 @@ App.prototype.renderAppBar = function(){ h('.app-header.flex-row.flex-space-between', { style: { alignItems: 'center', - visibility: state.isUnlocked ? 'visibile' : 'none', + visibility: state.isUnlocked ? 'visible' : 'none', background: state.isUnlocked ? 'white' : 'none', height: '36px', position: 'relative', @@ -152,7 +152,7 @@ App.prototype.renderAppBar = function(){ src: '/images/icon-128.png', }), - // metamask namlterChangese + // metamask name h('h1', 'MetaMask'), // hamburger