├── .babelrc ├── .gitignore ├── README.md ├── css └── main.css ├── package.json ├── src ├── ActionCreators.js ├── components │ ├── DecrementButton.jsx │ ├── GoldenLayoutWrapper.jsx │ ├── IncrementButton.jsx │ └── TestComponent.jsx ├── core.js ├── index.html ├── index.jsx └── reducer.js └── webpack.config.babel.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015", "react" ], 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golden-layout-react-redux 2 | Minimal example showing how to link redux state to react components embedded within golden-layout. 3 | 4 | Run `npm install`, then `npm run dev` and point your browser to `localhost:8080` to run the example. 5 | 6 | The example consists of three golden-layout tabs, each with a single React component. There are two buttons and one label which displays a count stored in the Redux state. The buttons increment or decrement this count. 7 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | .root { 2 | height: 100vh; 3 | } 4 | 5 | body { 6 | height: 100vh; 7 | } 8 | 9 | .lm_root { 10 | height: 100vh; 11 | } 12 | 13 | .goldenLayout { 14 | height: 100vh; 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "golden-layout-react-redux", 3 | "version": "1.0.0", 4 | "description": "example showing how to use react-redux with golden-layout", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack", 9 | "dev": "webpack-dev-server" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/andrewcapodieci/golden-layout-react-redux.git" 14 | }, 15 | "keywords": [ 16 | "react", 17 | "redux", 18 | "react-redux", 19 | "golden-layout" 20 | ], 21 | "author": "Andrew Capodieci ", 22 | "license": "UNLICENSED", 23 | "bugs": { 24 | "url": "https://github.com/andrewcapodieci/golden-layout-react-redux/issues" 25 | }, 26 | "homepage": "https://github.com/andrewcapodieci/golden-layout-react-redux#readme", 27 | "dependencies": { 28 | "babel": "^6.23.0", 29 | "babel-cli": "^6.24.1", 30 | "babel-core": "^6.24.1", 31 | "babel-loader": "^7.0.0", 32 | "babel-preset-env": "^1.5.1", 33 | "babel-preset-es2015": "^6.24.1", 34 | "babel-preset-react": "^6.24.1", 35 | "golden-layout": "^1.5.8", 36 | "html-webpack-plugin": "^2.28.0", 37 | "immutable": "^3.8.1", 38 | "prop-types": "^15.5.10", 39 | "react": "^15.5.4", 40 | "react-dom": "^15.5.4", 41 | "react-redux": "^5.0.5", 42 | "redux": "^3.6.0", 43 | "webpack": "^3.5.5", 44 | "webpack-dev-server": "^2.4.5" 45 | }, 46 | "devDependencies": { 47 | "css-loader": "^0.28.4", 48 | "extract-text-webpack-plugin": "^3.0.0", 49 | "style-loader": "^0.18.2" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ActionCreators.js: -------------------------------------------------------------------------------- 1 | export function setState(state) { 2 | return { 3 | type: 'SET_STATE', 4 | state 5 | }; 6 | } 7 | 8 | export function incrementCount() { 9 | return { 10 | type: 'INCREMENT_COUNT' 11 | }; 12 | } 13 | 14 | export function decrementCount() { 15 | return { 16 | type: 'DECREMENT_COUNT' 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/components/DecrementButton.jsx: -------------------------------------------------------------------------------- 1 | import {decrementCount} from '../ActionCreators'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | 5 | class DecrementButton extends React.Component { 6 | render() { 7 | return ( 8 | 9 | ); 10 | } 11 | } 12 | 13 | DecrementButton.PropTypes = { 14 | decrementCount: PropTypes.func.isRequired 15 | } 16 | 17 | function mapDispatchToProps(dispatch) { 18 | return { 19 | decrementCount: () => dispatch(decrementCount()) 20 | }; 21 | } 22 | 23 | export const DecrementButtonContainer = connect( 24 | null, 25 | mapDispatchToProps 26 | )(DecrementButton); 27 | -------------------------------------------------------------------------------- /src/components/GoldenLayoutWrapper.jsx: -------------------------------------------------------------------------------- 1 | import GoldenLayout from 'golden-layout'; 2 | import {Provider} from 'react-redux'; 3 | import {IncrementButtonContainer} from './IncrementButton'; 4 | import {DecrementButtonContainer} from './DecrementButton'; 5 | import {TestComponentContainer} from './TestComponent'; 6 | 7 | class GoldenLayoutWrapper extends React.Component { 8 | componentDidMount() { 9 | // Build basic golden-layout config 10 | const config = { 11 | content: [{ 12 | type: 'row', 13 | content: [{ 14 | type: 'react-component', 15 | component: 'TestComponentContainer' 16 | },{ 17 | type: 'react-component', 18 | component: 'IncrementButtonContainer' 19 | },{ 20 | type: 'react-component', 21 | component: 'DecrementButtonContainer' 22 | }] 23 | }] 24 | }; 25 | 26 | function wrapComponent(Component, store) { 27 | class Wrapped extends React.Component { 28 | render() { 29 | return ( 30 | 31 | 32 | 33 | ); 34 | } 35 | } 36 | return Wrapped; 37 | }; 38 | 39 | var layout = new GoldenLayout(config, this.layout); 40 | layout.registerComponent('IncrementButtonContainer', 41 | wrapComponent(IncrementButtonContainer, this.context.store) 42 | ); 43 | layout.registerComponent('DecrementButtonContainer', 44 | wrapComponent(DecrementButtonContainer, this.context.store) 45 | ); 46 | layout.registerComponent('TestComponentContainer', 47 | wrapComponent(TestComponentContainer, this.context.store) 48 | ); 49 | layout.init(); 50 | 51 | window.addEventListener('resize', () => { 52 | layout.updateSize(); 53 | }); 54 | } 55 | 56 | render() { 57 | return ( 58 |
this.layout = input}/> 59 | ); 60 | } 61 | } 62 | 63 | // ContextTypes must be defined in order to pass the redux store to exist in 64 | // "this.context". The redux store is given to GoldenLayoutWrapper from its 65 | // surrounding in index.jsx. 66 | GoldenLayoutWrapper.contextTypes = { 67 | store: React.PropTypes.object.isRequired 68 | }; 69 | 70 | 71 | export default GoldenLayoutWrapper; 72 | -------------------------------------------------------------------------------- /src/components/IncrementButton.jsx: -------------------------------------------------------------------------------- 1 | import {incrementCount} from '../ActionCreators'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | 5 | class IncrementButton extends React.Component { 6 | render() { 7 | return ( 8 | 9 | ); 10 | } 11 | } 12 | 13 | IncrementButton.PropTypes = { 14 | incrementCount: PropTypes.func.isRequired 15 | } 16 | 17 | function mapDispatchToProps(dispatch) { 18 | return { 19 | incrementCount: () => dispatch(incrementCount()) 20 | }; 21 | } 22 | 23 | export const IncrementButtonContainer = connect( 24 | null, 25 | mapDispatchToProps 26 | )(IncrementButton); 27 | -------------------------------------------------------------------------------- /src/components/TestComponent.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import {connect} from 'react-redux'; 3 | 4 | // Pure react component. Should not be connected to redux store; its container 5 | // should be connected to the store. 6 | export class TestComponent extends React.Component { 7 | render() { 8 | return ( 9 |

{this.props.label}

10 | ); 11 | } 12 | } 13 | 14 | TestComponent.PropTypes = { 15 | label: PropTypes.number.isRequired 16 | } 17 | 18 | function mapStateToProps(state) { 19 | return { 20 | label: state.get('count') 21 | } 22 | } 23 | 24 | export const TestComponentContainer = connect(mapStateToProps)(TestComponent); 25 | -------------------------------------------------------------------------------- /src/core.js: -------------------------------------------------------------------------------- 1 | import {Map} from 'immutable'; 2 | 3 | export function incrementCount(state) 4 | { 5 | return state.update('count', count => count+1); 6 | } 7 | 8 | export function decrementCount(state) 9 | { 10 | return state.update('count', count => count-1); 11 | } 12 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | React-Redux with GoldenLayout 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | import GoldenLayoutWrapper from './components/GoldenLayoutWrapper'; 2 | import {createStore} from 'redux'; 3 | import {Provider} from 'react-redux'; 4 | import reducer from './reducer'; 5 | import {setState} from './ActionCreators'; 6 | 7 | import '../css/main.css'; 8 | 9 | const store = createStore(reducer); 10 | store.dispatch(setState({ 'count': 10 })); 11 | 12 | ReactDOM.render( 13 | 14 | 15 | , 16 | document.getElementById('root') 17 | ); 18 | -------------------------------------------------------------------------------- /src/reducer.js: -------------------------------------------------------------------------------- 1 | import {Map} from 'immutable'; 2 | import { 3 | incrementCount, 4 | decrementCount 5 | } from './core'; 6 | 7 | function setState(state, newState) { 8 | return state.merge(newState); 9 | } 10 | 11 | export default function(state = Map(), action) { 12 | switch(action.type) { 13 | case 'SET_STATE': 14 | return setState(state, action.state); 15 | case 'INCREMENT_COUNT': 16 | return incrementCount(state); 17 | case 'DECREMENT_COUNT': 18 | return decrementCount(state); 19 | } 20 | return state; 21 | } 22 | -------------------------------------------------------------------------------- /webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import HtmlWebpackPlugin from 'html-webpack-plugin'; 3 | import webpack from 'webpack'; 4 | import ExtractTextPlugin from 'extract-text-webpack-plugin'; 5 | 6 | export default () => ({ 7 | entry: [ 8 | 'webpack-dev-server/client?http://localhost:8080', // webpack dev server host and port 9 | path.join(__dirname, 'src/index.jsx'), // entry point of app 10 | ], 11 | output: { 12 | path: path.join(__dirname + '/dist'), 13 | filename: 'bundle.js', 14 | }, 15 | plugins: [ 16 | new HtmlWebpackPlugin({ 17 | filename: 'index.html', 18 | template: './src/index.html' 19 | }), 20 | // Necessary b/c golden-layout depends on all 3 of these libs via UMD globals 21 | new webpack.ProvidePlugin({ 22 | React: 'react', 23 | ReactDOM: 'react-dom', 24 | $: 'jquery', 25 | jQuery: 'jquery' 26 | }), 27 | new ExtractTextPlugin('styles.css') 28 | ], 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.jsx?$/, 33 | exclude: /node_modules/, 34 | include: path.join(__dirname, 'src'), 35 | use: [{ 36 | loader: 'babel-loader', 37 | options: { 38 | babelrc: false, // Tells webpack not to use the .babelrc file 39 | presets: [ 40 | ['babel-preset-env', { 41 | "targets": { "firefox": 52, "chrome": 55 }, 42 | "modules": false, 43 | "loose": true 44 | }], 45 | 'react' // Transform JSX into React.createElement calls 46 | ] 47 | }, 48 | }] 49 | }, 50 | { 51 | test: /\.css$/, 52 | exclude: /node_modules/, 53 | include: path.join(__dirname, 'css'), 54 | use: ExtractTextPlugin.extract({ 55 | fallback: 'style-loader', 56 | use: 'css-loader' 57 | }) 58 | } 59 | ] 60 | }, 61 | resolve: { 62 | extensions: ['.js', '.jsx'] 63 | }, 64 | devtool: 'source-map' 65 | }); 66 | --------------------------------------------------------------------------------