diff --git a/app/actions/requests.js b/app/actions/requests.js index 6d93685e2..286752ae4 100644 --- a/app/actions/requests.js +++ b/app/actions/requests.js @@ -87,3 +87,7 @@ export function updateRequestMethod (id, method) { export function activateRequest (id) { return {type: types.REQUEST_ACTIVATE, id}; } + +export function changeFilter (filter) { + return {type: types.REQUEST_CHANGE_FILTER, filter}; +} diff --git a/app/components/RequestUrlBar.js b/app/components/RequestUrlBar.js index 66ba7e14a..d12cb2b1c 100644 --- a/app/components/RequestUrlBar.js +++ b/app/components/RequestUrlBar.js @@ -1,5 +1,5 @@ import React, {Component, PropTypes} from 'react' -import Input from './base/Input'; +import Input from './base/DebouncingInput'; import Dropdown from './base/Dropdown'; import {METHODS} from '../constants/global'; diff --git a/app/components/Sidebar.js b/app/components/Sidebar.js index 063e6c615..3d7de560b 100644 --- a/app/components/Sidebar.js +++ b/app/components/Sidebar.js @@ -1,56 +1,94 @@ -import React, {PropTypes} from 'react' +import React, {Component, PropTypes} from 'react' import Dropdown from './base/Dropdown' +import DebouncingInput from './base/DebouncingInput' -const Sidebar = (props) => ( - -); + + + + + ) + } +} Sidebar.propTypes = { activateRequest: PropTypes.func.isRequired, addRequest: PropTypes.func.isRequired, + changeFilter: PropTypes.func.isRequired, + activeFilter: PropTypes.string, requests: PropTypes.array.isRequired, activeRequest: PropTypes.object, loading: PropTypes.bool.isRequired diff --git a/app/components/base/Input.js b/app/components/base/DebouncingInput.js similarity index 83% rename from app/components/base/Input.js rename to app/components/base/DebouncingInput.js index 4e47d6877..78bb35f6f 100644 --- a/app/components/base/Input.js +++ b/app/components/base/DebouncingInput.js @@ -2,7 +2,10 @@ import React, {Component, PropTypes} from 'react'; const DEFAULT_DEBOUNCE_MILLIS = 300; -class Input extends Component { +/** + * Input that only fire onChange() after the user stops typing + */ +class DebouncingInput extends Component { valueChanged (e) { if (!this.props.onChange) { return; @@ -36,17 +39,17 @@ class Input extends Component { className={this.props.className} initialValue={initialValue || value} onChange={this.valueChanged.bind(this)} - placeholder="https://google.com" + placeholder={this.props.placeholder} /> ) } } -Input.propTypes = { +DebouncingInput.propTypes = { onChange: PropTypes.func.isRequired, initialValue: PropTypes.string, debounceMillis: PropTypes.number, value: PropTypes.string }; -export default Input; +export default DebouncingInput; diff --git a/app/components/base/Dropdown.js b/app/components/base/Dropdown.js index 988d4fd09..8827cbc2c 100644 --- a/app/components/base/Dropdown.js +++ b/app/components/base/Dropdown.js @@ -14,6 +14,10 @@ class Dropdown extends Component { document.addEventListener('click', this._clickEvenCallback.bind(this)); } + componentWillUnmount () { + document.removeEventListener('click', this._clickEvenCallback); + } + _clickEvenCallback (e) { if (!this.refs.container.contains(e.target)) { e.preventDefault(); diff --git a/app/constants/actionTypes.js b/app/constants/actionTypes.js index f56c539e8..2dda9ed7e 100644 --- a/app/constants/actionTypes.js +++ b/app/constants/actionTypes.js @@ -8,3 +8,4 @@ export const GLOBAL_STATE_RESTORED = 'GLOBAL.STATE_RESTORED'; export const REQUEST_ADD = 'REQUEST.ADD'; export const REQUEST_UPDATE = 'REQUEST.UPDATE'; export const REQUEST_ACTIVATE = 'REQUEST.ACTIVATE'; +export const REQUEST_CHANGE_FILTER = 'REQUEST.CHANGE_FILTER'; diff --git a/app/containers/App.js b/app/containers/App.js index 6a1ef0881..e4fe0bd36 100644 --- a/app/containers/App.js +++ b/app/containers/App.js @@ -27,46 +27,63 @@ class App extends Component { return (
-
-
- -
-
- - - - - - - - - - - Params - Basic Auth - Headers - +
+
+
+ +
+
+ + + + + + + + + + + Params + Basic Auth + Headers + +
-
-
-
-
200 SUCCESS
-
GET https://google.com
+
+
+
+
+
200 SUCCESS
+
TIME 143ms
+
+
+
+ + + + + + + + + + + Raw + Headers + Cookies +
-
-
-
@@ -81,8 +98,10 @@ class App extends Component {
@@ -98,6 +117,7 @@ App.propTypes = { activateRequest: PropTypes.func.isRequired, updateRequestBody: PropTypes.func.isRequired, updateRequestUrl: PropTypes.func.isRequired, + changeFilter: PropTypes.func.isRequired, updateRequestMethod: PropTypes.func.isRequired }).isRequired, requests: PropTypes.shape({ diff --git a/app/css/components/dropdown.scss b/app/css/components/dropdown.scss index 2212ea8a0..8fa0830bb 100644 --- a/app/css/components/dropdown.scss +++ b/app/css/components/dropdown.scss @@ -1,3 +1,4 @@ +@import '../constants/colors'; @import '../constants/dimensions'; .dropdown { @@ -20,7 +21,7 @@ display: none; z-index: 10; width: 180px; - border-radius: 2px; + border-radius: $radius-md; padding: 2px 0; margin-top: 4px; box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.15); @@ -28,9 +29,17 @@ a, button { font-size: $font-size-md; text-align: left; - padding: 10px $default-padding; + padding: 10px $padding-md; width: 100%; display: block; + + &:hover { + background: $hl-xs; + } + + &:active { + background: $hl-md; + } } } } diff --git a/app/css/components/forms.scss b/app/css/components/forms.scss new file mode 100644 index 000000000..1f1e869ba --- /dev/null +++ b/app/css/components/forms.scss @@ -0,0 +1,73 @@ +@import '../constants/dimensions'; +@import '../constants/colors'; + +.form-control { + outline: none; + border: 0; + height: $form-height; + + input { + width: 100%; + height: 100%; + } + + &.form-control--right button:last-child, + &.form-control--left input { + padding-left: $padding-md; + } + + &.form-control--left button:first-child, + &.form-control--right input { + padding-right: $padding-md; + } + + &.form-control--outlined { + border: 1px solid $hl-lg; + border-radius: $radius-md; + height: auto; + margin: $padding-sm; + + input { + padding: $padding-sm; + } + } +} + +.btn { + cursor: pointer; + text-align: center; + padding: 0 ($padding-md * 1.5); + height: $line-height-md; + + &.btn--compact { + padding: 0 ($padding-md); + height: $line-height-md / 2; + } +} + +.btn:hover { + background: $hl-xs; +} + +.btn:active { + background: $hl-md; +} + +input, button { + box-sizing: border-box; + font-size: inherit; + text-decoration: inherit; + background: none; + border: 0; + outline: 0; + margin: 0; + + &::-webkit-input-placeholder { + color: $hl-xl; + } +} + +button { + cursor: pointer; + width: auto; +} diff --git a/app/css/grid.scss b/app/css/components/grid.scss similarity index 91% rename from app/css/grid.scss rename to app/css/components/grid.scss index d3061b34f..9c63dac68 100644 --- a/app/css/grid.scss +++ b/app/css/components/grid.scss @@ -1,4 +1,4 @@ -@import 'constants/dimensions'; +@import '../constants/dimensions'; .grid { display: flex; @@ -40,12 +40,6 @@ } } -.un-grid { - display: initial; - height: auto; - width: auto; -} - @media (max-width: $breakpoint-md) { .grid.grid-collapse { display: flex; diff --git a/app/css/links.scss b/app/css/components/links.scss similarity index 68% rename from app/css/links.scss rename to app/css/components/links.scss index dca227e3a..b7a021486 100644 --- a/app/css/links.scss +++ b/app/css/components/links.scss @@ -1,4 +1,4 @@ -@import 'constants/colors'; +@import '../constants/colors'; a { text-decoration: none; diff --git a/app/css/layout/pane.scss b/app/css/components/pane.scss similarity index 61% rename from app/css/layout/pane.scss rename to app/css/components/pane.scss index d306728ac..73ffe71f1 100644 --- a/app/css/layout/pane.scss +++ b/app/css/components/pane.scss @@ -3,23 +3,19 @@ .pane { .pane__body, .pane__header { - border-right: 1px solid rgba(255, 255, 255, 0.08); + border-right: 1px solid $hl-sm; border-left: 2px solid rgba(0, 0, 0, 0.2); } .pane__header { border-color: transparent; - height: $header-height; + height: $line-height-md; .pane__header__content { display: block; - height: $header-height; + height: $line-height-md; box-sizing: border-box; - padding: $default-padding; + padding: $padding-md; } } - - .pane__body { - height: 100%; - } } diff --git a/app/css/components/request.scss b/app/css/components/request.scss deleted file mode 100644 index 27ee592f9..000000000 --- a/app/css/components/request.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import '../constants/colors'; -@import '../constants/dimensions'; - -#request { - .url-input { - font-size: $font-size-lg; - } -} diff --git a/app/css/components/scrollbars.scss b/app/css/components/scrollbars.scss index 940c9bccf..1985e84b5 100644 --- a/app/css/components/scrollbars.scss +++ b/app/css/components/scrollbars.scss @@ -1,10 +1,12 @@ +@import '../constants/colors'; +@import '../constants/dimensions'; ::-webkit-scrollbar { - width: 8px; + width: 6px; } ::-webkit-scrollbar { - height: 8px; + height: 6px; } ::-webkit-scrollbar-track { @@ -12,6 +14,6 @@ } ::-webkit-scrollbar-thumb { - background: rgba(100, 100, 100, 0.35); - border-radius: 4px; + background: $hl-md; + border-radius: $radius-md; } diff --git a/app/css/components/sidebar.scss b/app/css/components/sidebar.scss deleted file mode 100644 index 99a49d4f4..000000000 --- a/app/css/components/sidebar.scss +++ /dev/null @@ -1,33 +0,0 @@ -@import '../constants/dimensions'; -@import '../constants/colors'; - -#sidebar { - height: 100%; - - .pane__body { - overflow-y: auto; - } - - .pane__header, .pane__body { - border-left: 0; - } - - a { - box-sizing: border-box; - display: block; - } - - ul.sidebar-items { - width: $sidebar-width; - - li.sidebar-item > a { - color: darken($font-dark-bg, 40%); - padding: $sidebar-item-padding; - border-left: 4px solid transparent; - } - } - - .sidebar-items li.sidebar-item.active > a { - border-left-color: $bg-primary; - } -} diff --git a/app/css/components/tabs.scss b/app/css/components/tabs.scss index 8a53d9ca8..e4af90c8f 100644 --- a/app/css/components/tabs.scss +++ b/app/css/components/tabs.scss @@ -1,9 +1,17 @@ +@import '../constants/colors'; +@import '../constants/dimensions'; + .ReactTabs { - .ReactTabs__Tab { - opacity: 0.5; + .ReactTabs__TabList { + flex-basis: $line-height-md; } - .ReactTabs__Tab--selected { - opacity: 1; + .ReactTabs__Tab button { + height: $line-height-md; + color: $hl; + } + + .ReactTabs__Tab--selected button { + color: inherit; } } diff --git a/app/css/components/tag.scss b/app/css/components/tag.scss index aef1eb80f..d0b9ca782 100644 --- a/app/css/components/tag.scss +++ b/app/css/components/tag.scss @@ -3,7 +3,7 @@ .tag { display: inline-block; border: 1px solid; - padding: $slim-padding; + padding: $padding-sm; margin-right: 1em; box-sizing: border-box; diff --git a/app/css/constants/colors.scss b/app/css/constants/colors.scss index 3fba6dfd7..290f10777 100644 --- a/app/css/constants/colors.scss +++ b/app/css/constants/colors.scss @@ -4,12 +4,22 @@ $bg-light: #efefef; $bg-super-light: #ffffff; $bg-dark: #272824; +$hl-xxs: rgba(130, 130, 130, 0.05); +$hl-xs: rgba(130, 130, 130, 0.09); +$hl-sm: rgba(130, 130, 130, 0.15); +$hl-md: rgba(130, 130, 130, 0.25); +$hl-lg: rgba(130, 130, 130, 0.35); +$hl-xl: rgba(130, 130, 130, 0.5); +$hl: rgba(130, 130, 130, 1); + $success: #598f07; /* Font Colors */ $font-light-bg: #444444; $font-dark-bg: #eeeeee; +$faint-opacity: 0.5; + .success { border-color: $success !important; color: $success; @@ -20,14 +30,6 @@ $font-dark-bg: #eeeeee; &, a, textarea, input, button { color: $font-dark-bg; } - - button:hover, a:hover { - background: lighten($bg-dark, 4%); - } - - button:active, a:active { - background: lighten($bg-dark, 6%); - } } .bg-primary { @@ -36,14 +38,6 @@ $font-dark-bg: #eeeeee; &, a, textarea, input, button { color: $font-dark-bg; } - - button:hover, a:hover { - background: darken($bg-primary, 4%); - } - - button:active, a:active { - background: darken($bg-primary, 6%); - } } .bg-light { @@ -52,14 +46,6 @@ $font-dark-bg: #eeeeee; &, a, textarea, input, button { color: $font-light-bg; } - - button:hover, a:hover { - background: darken($bg-light, 4%); - } - - button:active, a:active { - background: darken($bg-light, 6%); - } } .bg-super-light { @@ -67,20 +53,4 @@ $font-dark-bg: #eeeeee; &, a, textarea, input, button { color: $font-light-bg; } - - button:hover, a:hover { - background: darken($bg-super-light, 4%); - } - - button:active, a:active { - background: darken($bg-super-light, 6%); - } -} - -.bg-super-light.btn:hover { - background: darken($bg-super-light, 4%); -} - -.bg-super-light.btn:active { - background: darken($bg-super-light, 6%); } diff --git a/app/css/constants/dimensions.scss b/app/css/constants/dimensions.scss index ec4418fed..d7277c694 100644 --- a/app/css/constants/dimensions.scss +++ b/app/css/constants/dimensions.scss @@ -1,5 +1,7 @@ -$default-padding: 15px; -$slim-padding: $default-padding / 2; +/* Padding */ +$padding-md: 15px; +$padding-sm: $padding-md / 2; +$padding-lg: $padding-md * 2; /* Fonts */ $font-size: 12px; @@ -11,17 +13,26 @@ $font-size-xxl: $font-size * 1.6; /* Breakpoints */ $breakpoint-md: 888px; +$breakpoint-sm: 555px; /* Wrapper */ -$header-height: 50px; +$line-height-md: 50px; +$line-height-sm: $line-height-md * 0.75; /* Sidebar */ $sidebar-width: 250px; -$sidebar-item-padding: $default-padding $default-padding * 2; +$sidebar-width-sm: $sidebar-width * 0.75; /* Elements */ $form-height: 50px; +/* Borders */ +$radius-md: 2px; + +.pad { + padding: $padding-md; +} + .txt-sm { font-size: $font-size-sm; } diff --git a/app/css/index.scss b/app/css/index.scss index a1c9a08f8..7c42aebc8 100644 --- a/app/css/index.scss +++ b/app/css/index.scss @@ -4,20 +4,20 @@ /* Boilerplate */ @import 'reset'; -@import 'grid'; -@import 'base'; +@import 'components/grid'; +@import 'layout/base'; /* Styles */ -@import 'links'; +@import 'components/links'; /* Layout */ -@import 'layout/pane'; -@import 'layout/forms'; +@import 'components/pane'; +@import 'components/forms'; /* Components */ -@import 'components/sidebar'; -@import 'components/request'; -@import 'components/response'; +@import 'layout/sidebar'; +@import 'layout/request'; +@import 'layout/response'; @import 'components/tag'; @import 'components/tabs'; @import 'components/scrollbars'; diff --git a/app/css/base.scss b/app/css/layout/base.scss similarity index 78% rename from app/css/base.scss rename to app/css/layout/base.scss index 2b38d07fe..897836437 100644 --- a/app/css/base.scss +++ b/app/css/layout/base.scss @@ -1,5 +1,5 @@ -@import 'constants/colors'; -@import 'constants/dimensions'; +@import '../constants/colors'; +@import '../constants/dimensions'; html, body, #root { display: block; @@ -36,14 +36,18 @@ h2 { box-sizing: border-box; } +.wide { + width: 100%; +} + .hide-scrollbars { &::-webkit-scrollbar { display: none; } } -.ReactTabs__TabList { - height: $header-height; +.stock-height { + height: $line-height-md; } strong { diff --git a/app/css/layout/forms.scss b/app/css/layout/forms.scss deleted file mode 100644 index 0d665afaf..000000000 --- a/app/css/layout/forms.scss +++ /dev/null @@ -1,42 +0,0 @@ -@import '../constants/dimensions'; -@import '../constants/colors'; - -.form-control { - outline: none; - border: 0; - height: $form-height; - - &.form-control--right button:last-child, - &.form-control--left input { - padding-left: $default-padding; - } - - &.form-control--left button:first-child, - &.form-control--right input { - padding-right: $default-padding; - } -} - -.btn { - cursor: pointer; - text-align: center; - padding: $default-padding $default-padding * 1.5; -} - -input, button { - box-sizing: border-box; - padding: $default-padding; - font-size: inherit; - text-decoration: inherit; - background: none; - border: 0; - outline: 0; - width: 100%; - height: 100%; - margin: 0; -} - -button { - cursor: pointer; - width: auto; -} diff --git a/app/css/layout/request.scss b/app/css/layout/request.scss new file mode 100644 index 000000000..23f2b0965 --- /dev/null +++ b/app/css/layout/request.scss @@ -0,0 +1,4 @@ +@import '../constants/dimensions'; + +#request-pane { +} diff --git a/app/css/components/response.scss b/app/css/layout/response.scss similarity index 84% rename from app/css/components/response.scss rename to app/css/layout/response.scss index 3982afe15..51f3b93ca 100644 --- a/app/css/components/response.scss +++ b/app/css/layout/response.scss @@ -1,8 +1,7 @@ @import '../constants/colors'; -#response { +#response-pane { .pane__header, .pane__body { border-right: 0; } } - diff --git a/app/css/layout/sidebar.scss b/app/css/layout/sidebar.scss new file mode 100644 index 000000000..0fdbe1c90 --- /dev/null +++ b/app/css/layout/sidebar.scss @@ -0,0 +1,60 @@ +@import '../constants/dimensions'; +@import '../constants/colors'; + +.sidebar { + height: 100%; + + .pane__body { + overflow-y: auto; + width: $sidebar-width; + } + + .pane__header, .pane__body { + border-left: 0; + } + + ul { + li.active > .sidebar__item { + color: inherit; + cursor: default; + } + + & > li > .sidebar__item { + display: flex; + align-items: center; + padding: 0 $padding-md; + color: $hl; + height: $line-height-sm; + line-height: $line-height-sm; + box-sizing: border-box; + width: 100%; + text-align: left; + + &.sidebar__item--right-menu { + padding-right: 0; + } + } + + & > li { + & > ul > li { + & > .sidebar__item { + padding-left: $padding-md * 2; + } + & > ul > li { + & > .sidebar__item { + padding-left: $padding-md * 3; + } + } + } + } + } +} + +@media (max-width: $breakpoint-sm) { + .sidebar { + .pane__body { + overflow-y: auto; + width: $sidebar-width-sm; + } + } +} diff --git a/app/reducers/requests.js b/app/reducers/requests.js index e52835376..453046f3d 100644 --- a/app/reducers/requests.js +++ b/app/reducers/requests.js @@ -2,7 +2,8 @@ import * as types from "../constants/actionTypes"; const initialState = { all: [], - active: null + active: null, + filter: '' }; function requestsReducer (state = [], action) { @@ -18,7 +19,6 @@ function requestsReducer (state = [], action) { break; } } - return [...state, request]; case types.REQUEST_UPDATE: return state.map(request => { @@ -26,7 +26,8 @@ function requestsReducer (state = [], action) { return Object.assign({}, request, action.patch); } else { return request; - }}); + } + }); default: return state; } @@ -43,12 +44,17 @@ export default function (state = initialState, action) { all = requestsReducer(state.all, action); return Object.assign({}, state, {all}); case types.REQUEST_ACTIVATE: - if (!state.all.find(r => r.id === action.id)) { + if (state.active === action.id) { + // If it's the same, do nothing + return state; + } else if (!state.all.find(r => r.id === action.id)) { // Don't set if the request doesn't exist return state; } else { return Object.assign({}, state, {active: action.id}); } + case types.REQUEST_CHANGE_FILTER: + return Object.assign({}, state, {filter: action.filter}); default: return state } diff --git a/app/reducers/settings.js b/app/reducers/settings.js index 18ab86b79..c7abcc4ce 100644 --- a/app/reducers/settings.js +++ b/app/reducers/settings.js @@ -1,5 +1,3 @@ -import * as types from '../constants/actionTypes'; - const initialState = { }; diff --git a/electron.js b/electron.js index d675e9999..289da7d19 100644 --- a/electron.js +++ b/electron.js @@ -5,34 +5,29 @@ const electron = require('electron'); const app = electron.app; // Module to control application life. const BrowserWindow = electron.BrowserWindow; // Module to create native browser window. - -// Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the JavaScript object is garbage collected. +const IS_DEV = process.env.NODE_ENV === 'development'; var mainWindow = null; // Quit when all windows are closed. 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(); } }); -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. app.on('ready', function () { - // Create the browser window. mainWindow = new BrowserWindow({ - width: process.env.NODE_ENV === 'development' ? 1600 : 1200, - height: 800 + width: IS_DEV ? 1600 : 1200, + height: 800, + minHeight: 400, + minWidth: 500 }); // and load the electron.html of the app. mainWindow.loadURL(`file://${__dirname}/app/electron.html`); // Open the DevTools. - if (process.env.NODE_ENV === 'development') { + if (IS_DEV) { mainWindow.webContents.openDevTools(); } diff --git a/webpack/dev.config.js b/webpack/dev.config.js index fd4329bea..d3d814a6b 100644 --- a/webpack/dev.config.js +++ b/webpack/dev.config.js @@ -8,8 +8,8 @@ base.entry = [ 'webpack/hot/only-dev-server' ].concat(base.entry); -base.devtool = 'eval'; // Fastest form of source maps base.debug = true; +base.devtool = 'inline-source-map'; base.output.path = path.join(base.output.path, '/dev'); base.output.publicPath = 'http://localhost:3000/dist/';