From a2f0a036716b4fad121f94b23923d955da6287a6 Mon Sep 17 00:00:00 2001 From: William Petit Date: Wed, 16 Sep 2015 17:26:56 +0200 Subject: [PATCH] Refactoring store --- css/style.css | 2 +- js/components/edit/desktop-app-list.jsx | 2 +- js/components/edit/edit-view.jsx | 19 +++++--- js/components/edit/item-form.jsx | 61 +++++++++++++++++++++++++ js/components/edit/profile-tree.jsx | 35 ++++++++++++-- js/components/edit/tree-item.jsx | 22 ++++++--- js/store/actions/edit.js | 29 +++++++++++- js/store/index.js | 3 +- js/store/reducers/index.js | 1 + js/store/reducers/profile.js | 42 +++++++++++++++++ js/store/reducers/theme.js | 14 ++++++ 11 files changed, 208 insertions(+), 22 deletions(-) create mode 100644 js/store/reducers/theme.js diff --git a/css/style.css b/css/style.css index f6656b7..6674f17 100644 --- a/css/style.css +++ b/css/style.css @@ -126,6 +126,7 @@ html, body { display: flex; flex-direction: column; flex: 1; + overflow-y: auto; } .edit .item-form { @@ -141,7 +142,6 @@ html, body { .edit .apps-list ul.desktop-apps { list-style: none; padding: 0; - overflow-y: auto; height: 100%; margin: 10px 0 0 0; padding: 0 10px 0 0; diff --git a/js/components/edit/desktop-app-list.jsx b/js/components/edit/desktop-app-list.jsx index 3557c0f..f8c2ce1 100644 --- a/js/components/edit/desktop-app-list.jsx +++ b/js/components/edit/desktop-app-list.jsx @@ -11,7 +11,7 @@ var DesktopAppList = React.createClass({ var items = this.props.desktopApps.map(function(desktopApp, i) { var desktopEntry = desktopApp.content['Desktop Entry']; return ( - diff --git a/js/components/edit/edit-view.jsx b/js/components/edit/edit-view.jsx index a525d59..5ad5b7f 100644 --- a/js/components/edit/edit-view.jsx +++ b/js/components/edit/edit-view.jsx @@ -24,21 +24,21 @@ var EditView = React.createClass({
- + + onItemDropped={this.handleItemDrop} />
- +
); }, - onItemDropped: function(desktopEntry, targetItem) { + handleItemDrop: function(desktopEntry, targetItem) { var newProfileItem = { label: desktopEntry.Name, @@ -50,8 +50,12 @@ var EditView = React.createClass({ }, - onThemeSelected: function(theme) { - this.props.dispatch(actions.edit.selectTheme(theme)); + handleThemeSelect: function(theme) { + this.props.dispatch(actions.edit.useIconTheme(theme)); + }, + + handleItemChange: function(item, key, value) { + this.props.dispatch(actions.edit.updateProfileItem(item, key, value)); } }); @@ -60,7 +64,8 @@ function select(state) { return { desktopApps: state.desktopApps, profile: state.profile, - theme: state.theme + theme: state.theme, + selectedItem: state.selectedItem }; } diff --git a/js/components/edit/item-form.jsx b/js/components/edit/item-form.jsx index 1139744..58933a9 100644 --- a/js/components/edit/item-form.jsx +++ b/js/components/edit/item-form.jsx @@ -2,13 +2,74 @@ var React = require('react'); var ItemForm = React.createClass({ + getInitialState: function() { + return { + label: '', + icon: '', + exec: '' + }; + }, + + componentWillReceiveProps: function(props) { + + if(props.item) { + + this.setState({ + label: props.item.label, + icon: props.item.icon, + exec: props.item.exec + }); + } + + }, + render: function() { + var state = this.state; + return (
+
+
+ + +
+
+ + +
+
+ + +
+
); + }, + + handleChange: function(key, evt) { + + evt.preventDefault(); + + var newState = {}; + var value = evt.currentTarget.value; + + newState[key] = value; + this.setState(newState); + + if(typeof this.props.onItemChange === 'function') { + this.props.onItemChange(this.props.item, key, value); + } + } }); diff --git a/js/components/edit/profile-tree.jsx b/js/components/edit/profile-tree.jsx index 7cdd9eb..40f2ef1 100644 --- a/js/components/edit/profile-tree.jsx +++ b/js/components/edit/profile-tree.jsx @@ -13,13 +13,17 @@ var TreeNode = React.createClass({ var listElements = subItems.map(function(subItem, i) { return (
  • - +
  • ); }.bind(this)); var appEntry = data.icon || data.label ? - : + this.renderTreeItem(data): null ; @@ -32,6 +36,14 @@ var TreeNode = React.createClass({ ); + }, + + renderTreeItem: function(data) { + return ( + + ); } }); @@ -46,21 +58,36 @@ var ProfileTree = React.createClass({ return (
    - + {this.renderTreeNode(this.props.profile)}
    ); }, + renderTreeNode: function(data) { + return ( + + ); + }, + onItemMoved: function(movedItem, targetItem) { this.props.dispatch(actions.edit.moveProfileItem(movedItem, targetItem)); + }, + + onItemSelected: function(selectedItem) { + this.props.dispatch(actions.edit.selectProfileItem(selectedItem)); } }); function select(state) { return { - profile: state.profile + profile: state.profile, + theme: state.theme }; } diff --git a/js/components/edit/tree-item.jsx b/js/components/edit/tree-item.jsx index 8d37c8b..0be6d6f 100644 --- a/js/components/edit/tree-item.jsx +++ b/js/components/edit/tree-item.jsx @@ -10,20 +10,24 @@ var TreeItem = React.createClass({ render: function() { var data = this.props.data; - var appIcon = data.icon ? : null; + var appIcon = data.icon ? : null; var connectDragSource = this.props.connectDragSource; var connectDropTarget = this.props.connectDropTarget; var classes = classNames({ 'alert': true, - 'alert-default': !this.props.isDragging && !this.props.isOver, - 'alert-info': this.props.isDragging, - 'alert-success': this.props.isOver + 'alert-default': !this.props.isOver, + 'alert-info': this.props.isOver && this.props.canDrop, + 'alert-success': this.props.selected }); + var style = { + opacity: this.props.isDragging ? 0.5 : 1 + }; + return connectDropTarget(connectDragSource( -
    +
    {appIcon} {data.label}
    @@ -31,6 +35,11 @@ var TreeItem = React.createClass({ }, + handleClick: function(evt) { + evt.preventDefault(); + this.props.onItemClicked(this.props.data); + } + }); var dragSourceSpec = { @@ -76,7 +85,8 @@ function dragSourceCollect(connect, monitor) { function dropTargetCollect(connect, monitor) { return { connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver() + isOver: monitor.isOver(), + canDrop: monitor.canDrop() }; } diff --git a/js/store/actions/edit.js b/js/store/actions/edit.js index 0636527..4e97312 100644 --- a/js/store/actions/edit.js +++ b/js/store/actions/edit.js @@ -7,6 +7,9 @@ var LOAD_DESKTOP_APPS_SUCCESS = exports.LOAD_DESKTOP_APPS_SUCCESS = 'LOAD_DESKTO var LOAD_DESKTOP_APPS_FAILED = exports.LOAD_DESKTOP_APPS_FAILED = 'LOAD_DESKTOP_APPS_FAILED'; var MOVE_PROFILE_ITEM = exports.MOVE_PROFILE_ITEM = 'MOVE_PROFILE_ITEM'; var ADD_PROFILE_ITEM = exports.ADD_PROFILE_ITEM = 'ADD_PROFILE_ITEM'; +var USE_ICON_THEME = exports.USE_ICON_THEME = 'USE_ICON_THEME'; +var SELECT_PROFILE_ITEM = exports.SELECT_PROFILE_ITEM = 'SELECT_PROFILE_ITEM'; +var UPDATE_PROFILE_ITEM = exports.UPDATE_PROFILE_ITEM = 'UPDATE_PROFILE_ITEM'; // Actions creators @@ -31,10 +34,16 @@ exports.loadDesktopApps = function() { }; }; +exports.useIconTheme = function(theme) { + return { + type: USE_ICON_THEME, + theme: theme + }; +}; exports.moveProfileItem = function(movedItem, targetItem) { return { - type: 'MOVE_PROFILE_ITEM', + type: MOVE_PROFILE_ITEM, movedItem: movedItem, targetItem: targetItem }; @@ -42,8 +51,24 @@ exports.moveProfileItem = function(movedItem, targetItem) { exports.addProfileItem = function(newItem, targetItem) { return { - type: 'ADD_PROFILE_ITEM', + type: ADD_PROFILE_ITEM, newItem: newItem, targetItem: targetItem }; }; + +exports.selectProfileItem = function(item) { + return { + type: SELECT_PROFILE_ITEM, + item: item + }; +}; + +exports.updateProfileItem = function(item, key, value) { + return { + type: UPDATE_PROFILE_ITEM, + item: item, + key: key, + value: value + }; +}; diff --git a/js/store/index.js b/js/store/index.js index 0223dfa..87b7fd6 100644 --- a/js/store/index.js +++ b/js/store/index.js @@ -12,7 +12,8 @@ var createStore = redux.applyMiddleware( var appReducer = redux.combineReducers({ profile: reducers.profile, processOpts: reducers.processOpts, - desktopApps: reducers.desktopApps + desktopApps: reducers.desktopApps, + theme: reducers.theme }); module.exports = createStore(appReducer); diff --git a/js/store/reducers/index.js b/js/store/reducers/index.js index 4927732..7a61a24 100644 --- a/js/store/reducers/index.js +++ b/js/store/reducers/index.js @@ -1,3 +1,4 @@ exports.desktopApps = require('./desktop-apps'); exports.profile = require('./profile'); exports.processOpts = require('./process-opts'); +exports.theme = require('./theme'); diff --git a/js/store/reducers/profile.js b/js/store/reducers/profile.js index 0c6374f..d1713df 100644 --- a/js/store/reducers/profile.js +++ b/js/store/reducers/profile.js @@ -14,6 +14,12 @@ module.exports = function(oldProfile, action) { case actions.edit.ADD_PROFILE_ITEM: return addProfileItem(oldProfile, action.newItem, action.targetItem); + case actions.edit.UPDATE_PROFILE_ITEM: + return updateProfileItem(oldProfile, action.item, action.key, action.value); + + case actions.edit.SELECT_PROFILE_ITEM: + return selectProfileItem(oldProfile, action.item); + default: return oldProfile || null; @@ -21,6 +27,19 @@ module.exports = function(oldProfile, action) { }; +function selectProfileItem(oldProfile, item) { + var newProfile = _.cloneDeep(oldProfile); + + return newProfile; +} + +function updateProfileItem(oldProfile, targetItem, key, value) { + var newProfile = _.cloneDeep(oldProfile); + var result = treeFind(newProfile, targetItem); + result.item[key] = value; + return newProfile; +} + function moveProfileItem(oldProfile, movedItem, targetItem) { @@ -50,6 +69,29 @@ function addProfileItem(oldProfile, newItem, targetItem) { return newProfile; } +// Tree manipulation helpers + +function treeWalk(branch, func) { + + var items = branch.items; + + if(!items) return; + + for( var i = 0, item = items[i]; (item = items[i]); i++ ) { + + var breakHere = func(item, parent); + + if(breakHere) return breakHere; + + if(item.items) { + breakHere = treeWalk(item, func); + if(breakHere) return breakHere; + } + + } + +} + function treeFind(branch, obj) { var items = branch.items; diff --git a/js/store/reducers/theme.js b/js/store/reducers/theme.js new file mode 100644 index 0000000..32f39ac --- /dev/null +++ b/js/store/reducers/theme.js @@ -0,0 +1,14 @@ +var actions = require('../actions'); + +module.exports = function(currentTheme, action) { + + switch(action.type) { + + case actions.edit.USE_ICON_THEME: + return action.theme; + + default: + return currentTheme || null; + } + +};