diff --git a/CHANGELOG.md b/CHANGELOG.md index 0793127..0e8e9fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Set the theme for the editor - Set the theme for the output - Settings and input are persisted between app restarts +- Editor pane can be resized # 1.0.0 diff --git a/example.gif b/example.gif index 9eb02b8..ad93453 100644 Binary files a/example.gif and b/example.gif differ diff --git a/package.json b/package.json index 0ac85b1..07161b8 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "react-codemirror": "^0.3.0", "react-dom": "^15.3.1", "react-redux": "^4.4.5", + "react-split-pane": "0.1.66", "redux": "^3.6.0", "redux-persist": "4.8.3", "redux-thunk": "^2.1.0" diff --git a/src/app/components/App/index.js b/src/app/components/App/index.js deleted file mode 100644 index 70c681a..0000000 --- a/src/app/components/App/index.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react' -import { connect } from 'react-redux' -import Editor from '../../containers/Editor' -import Status from '../Status' -import Output from '../Output' -import Settings from '../../containers/Settings' - -import './styles.css' - -const App = ({ coffeeScriptVersion, editorTheme, outputTheme, input, output, status }) => { - return ( -
-
- -
-
- {input} - {output} -
-
- -
-
- ) -} - -const mapStateToProps = (state) => { - return { - coffeeScriptVersion: state.app.coffeeScriptVersion, - editorTheme: state.app.editorTheme, - outputTheme: state.app.outputTheme, - input: state.app.input, - output: state.app.output, - status: state.app.status - } -} - -export default connect(mapStateToProps)(App) diff --git a/src/app/components/Output/styles.css b/src/app/components/Output/styles.css index 487d753..67f62fa 100644 --- a/src/app/components/Output/styles.css +++ b/src/app/components/Output/styles.css @@ -1,13 +1,10 @@ .Output { - flex: 0 0 50%; - display: flex; - flex-direction: column; overflow: hidden; + height: 100%; } .Output .ReactCodeMirror { - flex: 0 0 100%; - overflow: hidden; + height: 100%; } .Output .CodeMirror { diff --git a/src/app/components/Setting/CoffeeScriptVersion/index.js b/src/app/components/Setting/CoffeeScriptVersion/index.js index fdae764..ccb1069 100644 --- a/src/app/components/Setting/CoffeeScriptVersion/index.js +++ b/src/app/components/Setting/CoffeeScriptVersion/index.js @@ -1,21 +1,14 @@ import React from 'react' -import { connect } from 'react-redux' -import { setCoffeeScriptVersion } from '../../../actions/settings' +import Selector from '../Selector' import { getAllVersions as getAllCoffeeScriptVersions } from '../../../coffeescript' -const CoffeeScriptVersionSetting = ({ dispatch, version }) => { +export default function ({ version, onChange }) { return ( -
- - -
+ ) } - -export default connect()(CoffeeScriptVersionSetting) diff --git a/src/app/components/Setting/EditorTheme/index.js b/src/app/components/Setting/EditorTheme/index.js index 272b55f..ff78a84 100644 --- a/src/app/components/Setting/EditorTheme/index.js +++ b/src/app/components/Setting/EditorTheme/index.js @@ -1,21 +1,14 @@ import React from 'react' -import { connect } from 'react-redux' -import { setEditorTheme } from '../../../actions/settings' +import Selector from '../Selector' import { getAllThemes as getAllEditorThemes } from '../../../editor' -const EditorThemeSetting = ({ dispatch, theme }) => { +export default function ({ theme, onChange }) { return ( -
- - -
+ ) } - -export default connect()(EditorThemeSetting) diff --git a/src/app/components/Setting/OutputTheme/index.js b/src/app/components/Setting/OutputTheme/index.js index 61df7c1..841c0ea 100644 --- a/src/app/components/Setting/OutputTheme/index.js +++ b/src/app/components/Setting/OutputTheme/index.js @@ -1,21 +1,14 @@ import React from 'react' -import { connect } from 'react-redux' -import { setOutputTheme } from '../../../actions/settings' +import Selector from '../Selector' import { getAllThemes as getAllEditorThemes } from '../../../editor' -const OutputThemeSetting = ({ dispatch, theme }) => { +export default function ({ theme, onChange }) { return ( -
- - -
+ ) } - -export default connect()(OutputThemeSetting) diff --git a/src/app/components/Setting/Selector/index.js b/src/app/components/Setting/Selector/index.js new file mode 100644 index 0000000..8335797 --- /dev/null +++ b/src/app/components/Setting/Selector/index.js @@ -0,0 +1,14 @@ +import React from 'react' + +export default function ({ label, initialValue, options, onChange }) { + return ( +
+ + +
+ ) +} diff --git a/src/app/containers/App/index.js b/src/app/containers/App/index.js new file mode 100644 index 0000000..3043c94 --- /dev/null +++ b/src/app/containers/App/index.js @@ -0,0 +1,38 @@ +import React from 'react' +import { connect } from 'react-redux' +import SplitPane from 'react-split-pane' +import Editor from '../Editor' +import Status from '../../components/Status' +import Output from '../../components/Output' +import Settings from '../Settings' + +import './styles.css' + +const App = ({ outputTheme, output, status }) => { + return ( +
+
+ +
+
+ + + {output} + +
+
+ +
+
+ ) +} + +const mapStateToProps = state => { + return { + outputTheme: state.app.outputTheme, + output: state.app.output, + status: state.app.status + } +} + +export default connect(mapStateToProps)(App) diff --git a/src/app/components/App/styles.css b/src/app/containers/App/styles.css similarity index 67% rename from src/app/components/App/styles.css rename to src/app/containers/App/styles.css index a79bf0b..78eecdf 100644 --- a/src/app/components/App/styles.css +++ b/src/app/containers/App/styles.css @@ -15,3 +15,9 @@ flex-direction: row; flex: 2 0 auto; } + +.Resizer.vertical { + border-left: 3px solid #ddd; + border-right: 3px solid #ddd; + cursor: col-resize; +} diff --git a/src/app/containers/Editor/index.js b/src/app/containers/Editor/index.js index 8a9ea12..cd919cb 100644 --- a/src/app/containers/Editor/index.js +++ b/src/app/containers/Editor/index.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { Component } from 'react' import Codemirror from 'react-codemirror' import { connect } from 'react-redux' import { compileInput } from '../../actions/editor' @@ -6,12 +6,12 @@ import 'codemirror/mode/coffeescript/coffeescript' import './styles.css' -class Editor extends React.Component { +class Editor extends Component { get options () { return { lineNumbers: true, mode: 'coffeescript', - theme: this.props.theme + theme: this.props.editorTheme } } @@ -31,22 +31,24 @@ class Editor extends React.Component { } handleFocusChange (focused) { - if (focused && this.props.children === this.defaultValue) { + if (focused && this.props.input === this.defaultValue) { this.props.dispatch(compileInput('', this.props.coffeeScriptVersion)) } - if (!focused && this.props.children === '') { + if (!focused && this.props.input === '') { this.props.dispatch(compileInput(this.defaultValue, this.props.coffeeScriptVersion)) } } componentDidMount () { - this.props.dispatch(compileInput(this.props.children, this.props.coffeeScriptVersion)) + // re-compile input when the component is mounted + this.props.dispatch(compileInput(this.props.input, this.props.coffeeScriptVersion)) } componentDidUpdate (prevProps) { + // re-compile input if the CoffeeScript version changed if (this.props.coffeeScriptVersion !== prevProps.coffeeScriptVersion) { - this.props.dispatch(compileInput(this.props.children, this.props.coffeeScriptVersion)) + this.props.dispatch(compileInput(this.props.input, this.props.coffeeScriptVersion)) } } @@ -57,11 +59,19 @@ class Editor extends React.Component { options={this.options} onChange={this.handleInputChange} onFocusChange={this.handleFocusChange} - value={this.props.children} + value={this.props.input} /> ) } } -export default connect()(Editor) +const mapStateToProps = state => { + return { + coffeeScriptVersion: state.app.coffeeScriptVersion, + editorTheme: state.app.editorTheme, + input: state.app.input + } +} + +export default connect(mapStateToProps)(Editor) diff --git a/src/app/containers/Editor/styles.css b/src/app/containers/Editor/styles.css index f447a6c..d656901 100644 --- a/src/app/containers/Editor/styles.css +++ b/src/app/containers/Editor/styles.css @@ -1,13 +1,10 @@ .Editor { - flex: 0 0 50%; - display: flex; - flex-direction: column; overflow: hidden; + height: 100% } .Editor .ReactCodeMirror { - flex: 0 0 100%; - overflow: hidden; + height: 100% } .Editor .CodeMirror { diff --git a/src/app/containers/Settings/index.js b/src/app/containers/Settings/index.js index 152eece..3b86a88 100644 --- a/src/app/containers/Settings/index.js +++ b/src/app/containers/Settings/index.js @@ -1,4 +1,6 @@ import React from 'react' +import { connect } from 'react-redux' +import { setCoffeeScriptVersion, setEditorTheme, setOutputTheme } from '../../actions/settings' import CoffeeScriptVersion from '../../components/Setting/CoffeeScriptVersion' import EditorTheme from '../../components/Setting/EditorTheme' import OutputTheme from '../../components/Setting/OutputTheme' @@ -6,12 +8,28 @@ import OutputTheme from '../../components/Setting/OutputTheme' import './styles.css' import '../../components/Setting/styles.css' -export default function ({ coffeeScriptVersion, editorTheme, outputTheme }) { +const Settings = ({ dispatch, coffeeScriptVersion, editorTheme, outputTheme }) => { return (
- - - + { + dispatch(setOutputTheme(e.target.value)) + }} /> + { + dispatch(setEditorTheme(e.target.value)) + }} /> + { + dispatch(setCoffeeScriptVersion(e.target.value)) + }} />
) } + +const mapStateToProps = state => { + return { + coffeeScriptVersion: state.app.coffeeScriptVersion, + editorTheme: state.app.editorTheme, + outputTheme: state.app.outputTheme + } +} + +export default connect(mapStateToProps)(Settings) diff --git a/src/app/index.js b/src/app/index.js index 663c005..977e3d1 100644 --- a/src/app/index.js +++ b/src/app/index.js @@ -1,9 +1,8 @@ import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' - import store from './store' -import App from './components/App' +import App from './containers/App' import 'normalize.css' import 'codemirror/lib/codemirror.css' diff --git a/src/app/store.js b/src/app/store.js index e615f73..67101b4 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -1,5 +1,5 @@ -import {compose, applyMiddleware, createStore} from 'redux' -import {persistStore, autoRehydrate} from 'redux-persist' +import { applyMiddleware, compose, createStore } from 'redux' +import { autoRehydrate, persistStore } from 'redux-persist' import thunk from 'redux-thunk' import reducer from './reducers' @@ -13,7 +13,6 @@ const store = createStore( ) persistStore(store, { - blacklist: [], // todo keyPrefix: 'CoffeeWriter-' }) diff --git a/src/app/styles.css b/src/app/styles.css index 01511ae..ed13fc2 100644 --- a/src/app/styles.css +++ b/src/app/styles.css @@ -1,9 +1,13 @@ body { font-size: 14px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } .CodeMirror { font-size: 15px; font-family: 'Source Code Pro', monospace; } + +*, *:before, *:after { + position: relative; +} diff --git a/src/electron/main.js b/src/electron/main.js index 2a17bce..f0b799e 100644 --- a/src/electron/main.js +++ b/src/electron/main.js @@ -13,10 +13,10 @@ const createWindow = () => { let windowOptions = { width: 800, height: 600, + titleBarStyle: 'hidden', webPreferences: { preload: path.resolve(__dirname, 'preload.js') - }, - titleBarStyle: 'hidden' + } } let url = `file://${path.resolve(__dirname, '../app/index.html')}` @@ -39,21 +39,42 @@ const createWindow = () => { }) } -const createMainMenu = (app) => { +const createMainMenu = app => { var template = [ { label: app.getName(), submenu: [ - { label: `About ${app.getName()}`, selector: 'orderFrontStandardAboutPanel:' }, - { type: 'separator' }, - { label: 'Quit', accelerator: 'Command+Q', click: () => app.quit() } + { + label: `About ${app.getName()}`, + selector: 'orderFrontStandardAboutPanel:' + }, + { + type: 'separator' + }, + { + label: 'Quit', + accelerator: 'Command+Q', + click: () => app.quit() + } ] }, { label: 'Edit', submenu: [ - { label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' }, - { label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' }, - { label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' } + { + label: 'Cut', + accelerator: 'CmdOrCtrl+X', + selector: 'cut:' + }, + { + label: 'Copy', + accelerator: 'CmdOrCtrl+C', + selector: 'copy:' + }, + { + label: 'Paste', + accelerator: 'CmdOrCtrl+V', + selector: 'paste:' + } ]} ] diff --git a/src/electron/preload.js b/src/electron/preload.js index 22837b2..cf7a18c 100644 --- a/src/electron/preload.js +++ b/src/electron/preload.js @@ -7,13 +7,16 @@ const Menu = remote.Menu const InputMenu = Menu.buildFromTemplate([ { type: 'separator' - }, { + }, + { label: 'Cut', role: 'cut' - }, { + }, + { label: 'Copy', role: 'copy' - }, { + }, + { label: 'Paste', role: 'paste' } @@ -21,7 +24,7 @@ const InputMenu = Menu.buildFromTemplate([ // https://github.com/electron/electron/issues/4068#issuecomment-170911307 window.addEventListener('DOMContentLoaded', () => { - document.body.addEventListener('contextmenu', (e) => { + document.body.addEventListener('contextmenu', e => { e.preventDefault() e.stopPropagation() @@ -30,6 +33,7 @@ window.addEventListener('DOMContentLoaded', () => { while (node) { if (node.nodeName.match(/DIV/) && node.className && node.className.match(/^CodeMirror.*$/i)) { InputMenu.popup(remote.getCurrentWindow()) + break } diff --git a/yarn.lock b/yarn.lock index 5cd9686..b47afe1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -819,6 +819,10 @@ boom@2.x.x: dependencies: hoek "2.x.x" +bowser@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.1.tgz#a4de8f18a1a0dc9531eb2a92a1521fb6a9ba96a5" + brace-expansion@^1.0.0: version "1.1.6" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" @@ -1197,6 +1201,12 @@ css-color-names@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" +css-in-js-utils@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-1.0.3.tgz#9ac7e02f763cf85d94017666565ed68a5b5f3215" + dependencies: + hyphenate-style-name "^1.0.2" + css-loader@^0.25.0: version "0.25.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.25.0.tgz#c3febc8ce28f4c83576b6b13707f47f90c390223" @@ -1859,6 +1869,18 @@ fbjs@^0.8.1, fbjs@^0.8.4: setimmediate "^1.0.5" ua-parser-js "^0.7.9" +fbjs@^0.8.9: + version "0.8.14" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.14.tgz#d1dbe2be254c35a91e09f31f9cd50a40b2a0ed1c" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + fd-slicer@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" @@ -2316,6 +2338,10 @@ https-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" +hyphenate-style-name@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b" + iconv-lite@~0.4.13: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" @@ -2369,6 +2395,13 @@ ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" +inline-style-prefixer@^3.0.6: + version "3.0.7" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-3.0.7.tgz#0ccc92e5902fe6e0d28d975c4258443f880615f8" + dependencies: + bowser "^1.6.0" + css-in-js-utils "^1.0.3" + inquirer@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" @@ -2597,6 +2630,10 @@ js-tokens@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" +js-tokens@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + js-yaml@^3.5.1: version "3.7.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" @@ -2799,6 +2836,12 @@ loose-envify@^1.0.0, loose-envify@^1.1.0: dependencies: js-tokens "^2.0.0" +loose-envify@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -3628,6 +3671,13 @@ promise@^7.1.1: dependencies: asap "~2.0.3" +prop-types@^15.5.10, prop-types@^15.5.4: + version "15.5.10" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + proxy-addr@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.2.tgz#b4cc5f22610d9535824c123aef9d3cf73c40ba37" @@ -3731,6 +3781,20 @@ react-redux@^4.4.5: lodash "^4.2.0" loose-envify "^1.1.0" +react-split-pane: + version "0.1.66" + resolved "https://registry.yarnpkg.com/react-split-pane/-/react-split-pane-0.1.66.tgz#369085dd07ec1237bda123e73813dcc7dc6502c1" + dependencies: + inline-style-prefixer "^3.0.6" + prop-types "^15.5.10" + react-style-proptype "^3.0.0" + +react-style-proptype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/react-style-proptype/-/react-style-proptype-3.0.0.tgz#89e0b646f266c656abb0f0dd8202dbd5036c31e6" + dependencies: + prop-types "^15.5.4" + react@^15.3.1: version "15.4.1" resolved "https://registry.yarnpkg.com/react/-/react-15.4.1.tgz#498e918602677a3983cd0fd206dfe700389a0dd6"