diff --git a/frontend/package.json b/frontend/package.json index 33830e2bc6..fb008f2ff7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,6 +40,8 @@ "normalize.css": "^5.0.0", "react": "^15.3.1", "react-addons-css-transition-group": "^15.3.1", + "react-dnd": "^2.1.4", + "react-dnd-html5-backend": "^2.1.2", "react-dom": "^15.3.1", "react-mdl": "^1.9.0", "react-modal": "^1.6.4", diff --git a/frontend/src/component/feature/form-add-container.jsx b/frontend/src/component/feature/form-add-container.jsx index 3afb20b3c2..e153aee10c 100644 --- a/frontend/src/component/feature/form-add-container.jsx +++ b/frontend/src/component/feature/form-add-container.jsx @@ -39,6 +39,10 @@ const prepare = (methods, dispatch) => { methods.updateInList('strategies', index, n); }; + methods.moveStrategy = (index, toIndex) => { + methods.moveItem('strategies', index, toIndex); + }; + methods.removeStrategy = (index) => { methods.removeFromList('strategies', index); }; diff --git a/frontend/src/component/feature/form-edit-container.jsx b/frontend/src/component/feature/form-edit-container.jsx index 7f592804f8..7605b994af 100644 --- a/frontend/src/component/feature/form-edit-container.jsx +++ b/frontend/src/component/feature/form-edit-container.jsx @@ -45,6 +45,10 @@ const prepare = (methods, dispatch) => { methods.removeFromList('strategies', index); }; + methods.moveStrategy = (index, toIndex) => { + methods.moveItem('strategies', index, toIndex); + }; + methods.updateStrategy = (index, n) => { methods.updateInList('strategies', index, n); }; diff --git a/frontend/src/component/feature/form/index.jsx b/frontend/src/component/feature/form/index.jsx index ca570a41e2..40417d4e1a 100644 --- a/frontend/src/component/feature/form/index.jsx +++ b/frontend/src/component/feature/form/index.jsx @@ -29,6 +29,7 @@ class AddFeatureToggleComponent extends Component { addStrategy, removeStrategy, updateStrategy, + moveStrategy, onSubmit, onCancel, editmode = false, @@ -81,6 +82,7 @@ class AddFeatureToggleComponent extends Component { configuredStrategies={configuredStrategies} addStrategy={addStrategy} updateStrategy={updateStrategy} + moveStrategy={moveStrategy} removeStrategy={removeStrategy} />
diff --git a/frontend/src/component/feature/form/strategies-list.jsx b/frontend/src/component/feature/form/strategies-list.jsx index 1c4b04a576..3bf2d8d84a 100644 --- a/frontend/src/component/feature/form/strategies-list.jsx +++ b/frontend/src/component/feature/form/strategies-list.jsx @@ -1,6 +1,9 @@ import React, { PropTypes } from 'react'; import ConfigureStrategy from './strategy-configure'; +import { DragDropContext } from 'react-dnd'; +import HTML5Backend from 'react-dnd-html5-backend'; +@DragDropContext(HTML5Backend) // eslint-disable-line new-cap class StrategiesList extends React.Component { static propTypes () { @@ -9,6 +12,7 @@ class StrategiesList extends React.Component { configuredStrategies: PropTypes.array.isRequired, updateStrategy: PropTypes.func.isRequired, removeStrategy: PropTypes.func.isRequired, + moveStrategy: PropTypes.func.isRequired, }; } @@ -16,6 +20,9 @@ class StrategiesList extends React.Component { const { strategies, configuredStrategies, + moveStrategy, + removeStrategy, + updateStrategy, } = this.props; if (!configuredStrategies || configuredStrategies.length === 0) { @@ -24,10 +31,12 @@ class StrategiesList extends React.Component { const blocks = configuredStrategies.map((strategy, i) => ( s.name === strategy.name)} /> )); return ( diff --git a/frontend/src/component/feature/form/strategy-configure.jsx b/frontend/src/component/feature/form/strategy-configure.jsx index 3da5d7bc18..12e09944a2 100644 --- a/frontend/src/component/feature/form/strategy-configure.jsx +++ b/frontend/src/component/feature/form/strategy-configure.jsx @@ -3,7 +3,8 @@ import { Textfield, Button, Card, CardTitle, CardText, CardActions, CardMenu, IconButton, Icon, -} from 'react-mdl'; +} from 'react-mdl'; +import { DragSource, DropTarget } from 'react-dnd'; import { Link } from 'react-router'; import StrategyInputPercentage from './strategy-input-percentage'; import StrategyInputList from './strategy-input-list'; @@ -13,7 +14,6 @@ const style = { minWidth: '300px', maxWidth: '100%', margin: '5px 20px 15px 0px', - background: '#f2f9fc', }; const helpText = { @@ -21,6 +21,34 @@ const helpText = { fontSize: '12px', lineHeight: '14px', }; + + +const dragSource = { + beginDrag (props) { + return { + index: props.index, + }; + }, +}; + +const dragTarget = { + drop (props, monitor) { + const dragIndex = monitor.getItem().index; + const toIndex = props.index; + if (dragIndex !== toIndex) { + props.moveStrategy(dragIndex, toIndex); + } + }, +}; + +@DropTarget('strategy', dragTarget, connect => ({ // eslint-disable-line new-cap + connectDropTarget: connect.dropTarget(), +})) +@DragSource('strategy', dragSource, (connect, monitor) => ({ // eslint-disable-line new-cap + connectDragSource: connect.dragSource(), + connectDragPreview: connect.dragPreview(), + isDragging: monitor.isDragging(), +})) class StrategyConfigure extends React.Component { static propTypes () { @@ -29,13 +57,14 @@ class StrategyConfigure extends React.Component { strategyDefinition: PropTypes.object.isRequired, updateStrategy: PropTypes.func.isRequired, removeStrategy: PropTypes.func.isRequired, + moveStrategy: PropTypes.func.isRequired, + isDragging: PropTypes.bool.isRequired, + connectDragPreview: PropTypes.func.isRequired, + connectDragSource: PropTypes.func.isRequired, + connectDropTarget: PropTypes.func.isRequired, }; } - // shouldComponentUpdate (props, nextProps) { - // console.log({ props, nextProps }); - // } - handleConfigChange = (key, e) => { this.setConfig(key, e.target.value); }; @@ -98,7 +127,7 @@ class StrategyConfigure extends React.Component { label={name} onChange={this.handleConfigChange.bind(this, name)} value={value} - /> + /> {description &&

{description}

} ); @@ -114,7 +143,7 @@ class StrategyConfigure extends React.Component { label={name} onChange={this.handleConfigChange.bind(this, name)} value={value} - /> + /> {description &&

{description}

} ); @@ -125,9 +154,46 @@ class StrategyConfigure extends React.Component { } render () { - if (!this.props.strategyDefinition) { + const { isDragging, connectDragPreview, connectDragSource, connectDropTarget } = this.props; + + let item; + if (this.props.strategyDefinition) { + const inputFields = this.renderInputFields(this.props.strategyDefinition); const { name } = this.props.strategy; - return ( + item = ( + + +  {name} + + + {this.props.strategyDefinition.description} + + { + inputFields && + {inputFields} + + } + + + + + + + {connectDragSource( + )} + + + ); + } else { + const { name } = this.props.strategy; + item = ( "{name}" deleted? @@ -142,35 +208,9 @@ class StrategyConfigure extends React.Component { ); } - const inputFields = this.renderInputFields(this.props.strategyDefinition); - - const { name } = this.props.strategy; - - return ( - - -  { name } - - - {this.props.strategyDefinition.description} - - { - inputFields && - {inputFields} - - } - - - - - - - - - ); + return (connectDropTarget(connectDragPreview( +
{item}
+ ))); } } diff --git a/frontend/src/component/input-helpers.js b/frontend/src/component/input-helpers.js index c0b24790bc..d3c734422c 100644 --- a/frontend/src/component/input-helpers.js +++ b/frontend/src/component/input-helpers.js @@ -6,6 +6,7 @@ import { createPush, createUp, createInit, + createMove, } from '../store/input-actions'; function getId (id, ownProps) { @@ -57,6 +58,10 @@ export function createActions ({ id, prepare = (v) => v }) { dispatch(createPop({ id: getId(id, ownProps), key, index })); }, + moveItem (key, index, toIndex) { + dispatch(createMove({ id: getId(id, ownProps), key, index, toIndex })); + }, + updateInList (key, index, newValue, merge = false) { dispatch(createUp({ id: getId(id, ownProps), key, index, newValue, merge })); }, diff --git a/frontend/src/store/input-actions.js b/frontend/src/store/input-actions.js index 5b6f5bead6..39dbd3d707 100644 --- a/frontend/src/store/input-actions.js +++ b/frontend/src/store/input-actions.js @@ -6,6 +6,7 @@ export const actions = { LIST_UP: 'LIST_UP', CLEAR: 'CLEAR', INIT: 'INIT', + MOVE: 'MOVE', }; export const createInit = ({ id, value }) => ({ type: actions.INIT, id, value }); @@ -13,6 +14,7 @@ export const createInc = ({ id, key }) => ({ type: actions.INCREMENT_VALUE, id, export const createSet = ({ id, key, value }) => ({ type: actions.SET_VALUE, id, key, value }); export const createPush = ({ id, key, value }) => ({ type: actions.LIST_PUSH, id, key, value }); export const createPop = ({ id, key, index }) => ({ type: actions.LIST_POP, id, key, index }); +export const createMove = ({ id, key, index, toIndex }) => ({ type: actions.MOVE, id, key, index, toIndex }); export const createUp = ({ id, key, index, newValue, merge }) => ({ type: actions.LIST_UP, id, key, index, newValue, merge }); export const createClear = ({ id }) => ({ type: actions.CLEAR, id }); diff --git a/frontend/src/store/input-store.js b/frontend/src/store/input-store.js index 743c7c570f..cc0a3c7075 100644 --- a/frontend/src/store/input-store.js +++ b/frontend/src/store/input-store.js @@ -69,6 +69,13 @@ function removeFromList (state, { id, key, index }) { return state.updateIn(id.concat([key]), (list) => list.remove(index)); } +function move (state, { id, key, index, toIndex }) { + return state.updateIn(id.concat([key]), list => { + const olditem = list.get(index); + return list.delete(index).insert(toIndex, olditem); + }); +} + const inputState = (state = getInitState(), action) => { if (!action.id) { return state; @@ -88,6 +95,8 @@ const inputState = (state = getInitState(), action) => { return addToList(state, action); case actions.LIST_POP: return removeFromList(state, action); + case actions.MOVE: + return move(state, action); case actions.LIST_UP: return updateInList(state, action); case actions.CLEAR: diff --git a/frontend/typings.json b/frontend/typings.json index a00f27177e..2f1203b9cc 100644 --- a/frontend/typings.json +++ b/frontend/typings.json @@ -1,5 +1,6 @@ { "globalDependencies": { + "react-dnd": "registry:dt/react-dnd#2.0.2+20161111212335", "react-mdl": "registry:dt/react-mdl#0.0.0+20160830142027" } }