Skip to content
This repository has been archived by the owner on Feb 20, 2019. It is now read-only.

Commit

Permalink
Initial ui decorator implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyhb committed Dec 8, 2015
1 parent 8e87bfc commit 2255e91
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"stage": 0
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
transpiled
node_modules
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
transpiled/
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import ui from 'redux-ui';
class Posts extends Component {

static propTypes = {
updateUi: PropTypes.func.isRequired,
updateUI: PropTypes.func.isRequired,
ui: PropTypes.object.isRequired
}

showForm = (evt) => () {
evt.preventDefault();
this.updateUi('isFormVisible', true);
this.props.updateUI('isFormVisible', true);
}

render() {
Expand All @@ -48,7 +48,7 @@ class Posts extends Component {
class NewPostForm extends Component {

static propTypes = {
updateUi: PropTypes.func.isRequired,
updateUI: PropTypes.func.isRequired,
ui: PropTypes.object.isRequired
}

Expand Down
29 changes: 29 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "redux-ui",
"version": "0.0.0",
"description": "UI state management for Redux and React",
"main": "transpiled/index.js",
"scripts": {
"prepublish": "$(npm bin)/babel -d transpiled src",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tonyhb/redux-ui.git"
},
"keywords": [
"react-component",
"redux",
"redux-ui",
"ui"
],
"author": "Tony Holdstock-Brown",
"license": "MIT",
"bugs": {
"url": "https://github.com/tonyhb/redux-ui/issues"
},
"homepage": "https://github.com/tonyhb/redux-ui#readme",
"devDependencies": {
"babel": "^5.8.34"
}
}
50 changes: 50 additions & 0 deletions src/action-reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

export const UPDATE_UI_STATE = '@@redux-ui/UPDATE_UI_STATE';
export const SET_DEFAULT_UI_STATE = '@@redux-ui/SET_DEFAULT_UI_STATE';

export default function(state = {}, action) {
const key = action.payload && (action.payload.key || '');

switch (action.type) {
case UPDATE_UI_STATE:
const { name, value } = action.payload;
return {
...state,
[key]: {
...state[key],
[name]: value
}
};

// Replace all UI under a key with the given values
case SET_DEFAULT_UI_STATE:
return {
...state,
[key]: action.payload.value
};
}

return state;
}

export function updateUI(key, name, value) {
return {
type: UPDATE_UI_STATE,
payload: {
key,
name,
value
}
};
};

export function setDefaultUI(key, value) {
return {
type: SET_DEFAULT_UI_STATE,
payload: {
key,
value
}
};
};
7 changes: 7 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

import ui from './ui';
import reducer from './action-reducer';

export default ui;
export { reducer };
119 changes: 119 additions & 0 deletions src/ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use strict';

import React, { Component, PropTypes } from 'react';
const { func, node, object, string } = PropTypes;
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import invariant from 'invariant';
import { updateUI, setDefaultUI } from './action-reducer';

// this connector
const connector = connect(
(state) => { return { ui: state.ui }; },
(dispatch) => bindActionCreators({ updateUI, setDefaultUI }, dispatch)
);

const partiallyApplyUpdateUI = (updateUI, k) => {
return (n, v) => updateUI(k, n, v);
}

export default function ui(key, opts = {}) {
return (WrappedComponent) => {
if (key !== undefined) {
// Return a parent UI class which scopes all UI state to the given key
return connector(
class UIParent extends Component {

static propTypes = {
// These are passed via react-redux connector
setDefaultUI: func.isRequired,
updateUI: func.isRequired,
ui: object.isRequired
}

static childContextTypes = {
uiKey: string,
updateUI: func,
resetUI: func
}

componentWillMount() {
if (this.props.ui[key] === undefined && opts.defaultState) {
this.props.setDefaultUI(key, opts.defaultState);
}
}

componentWillUnmount() {
// Blow away all UI state for this component key by setting the
// state for this key to undefined. This will get reset to the
// default state in componentWillMount in the future.
this.props.setDefaultUI(key);
}

// Pass the uiKey and partially applied updateUI function to all
// child components that are wrapped in a plain `@ui()` decorator
getChildContext() {
return {
uiKey: key,
updateUI: partiallyApplyUpdateUI(this.props.updateUI, key),
resetUI: ::this.resetUI
};
}

resetUI() {
this.props.setDefaultUI(key, opts.defaultState);
}

render() {
return (
<WrappedComponent
uiKey={ key }
ui={ this.props.ui[key] || {} }
resetUI={ ::this.resetUI }
updateUI={ partiallyApplyUpdateUI(this.props.updateUI, key) } />
);
}
}
);
}

// This is a child @ui component which updates all UI state within
// a parent's @ui scope
return connector(
class UIChild extends Component {
static propTypes = {
ui: object.isRequired
}

static contextTypes = {
uiKey: string,
updateUI: func.isRequired,
resetUI: func.isRequired
}

componentWillMount() {
invariant(
this.context.updateUI,
'Cannot find this.context.updateUI in "' +
(WrappedComponent.displayName || WrappedComponent.name) + '". ' +
'Set a UI key on this or one of its parents.'
);
}

render() {
const { component } = this.props;
const { uiKey, updateUI, resetUI } = this.context;
return (
<WrappedComponent
uiKey={ uiKey }
ui={ this.props.ui[key] || {} }
resetUI={ resetUI }
updateUI={ updateUI } />
);
}

}
);

}
}

0 comments on commit 2255e91

Please sign in to comment.