keystone
Version:
Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose
161 lines (140 loc) • 5.06 kB
JavaScript
import React from 'react';
import classnames from 'classnames';
import ListControl from '../ListControl';
import { Columns } from 'FieldTypes';
import { DropTarget, DragSource } from 'react-dnd';
import {
setDragBase,
resetItems,
reorderItems,
setRowAlert,
moveItem,
} from '../../actions';
const ItemsRow = React.createClass({
propTypes: {
columns: React.PropTypes.array,
id: React.PropTypes.any,
index: React.PropTypes.number,
items: React.PropTypes.object,
list: React.PropTypes.object,
// Injected by React DnD:
isDragging: React.PropTypes.bool, // eslint-disable-line react/sort-prop-types
connectDragSource: React.PropTypes.func, // eslint-disable-line react/sort-prop-types
connectDropTarget: React.PropTypes.func, // eslint-disable-line react/sort-prop-types
connectDragPreview: React.PropTypes.func, // eslint-disable-line react/sort-prop-types
},
renderRow (item) {
const itemId = item.id;
const rowClassname = classnames({
'ItemList__row--dragging': this.props.isDragging,
'ItemList__row--selected': this.props.checkedItems[itemId],
'ItemList__row--manage': this.props.manageMode,
'ItemList__row--success': this.props.rowAlert.success === itemId,
'ItemList__row--failure': this.props.rowAlert.fail === itemId,
});
// item fields
var cells = this.props.columns.map((col, i) => {
var ColumnType = Columns[col.type] || Columns.__unrecognised__;
var linkTo = !i ? `${Keystone.adminPath}/${this.props.list.path}/${itemId}` : undefined;
return <ColumnType key={col.path} list={this.props.list} col={col} data={item} linkTo={linkTo} />;
});
// add sortable icon when applicable
if (this.props.list.sortable) {
cells.unshift(<ListControl key="_sort" type="sortable" dragSource={this.props.connectDragSource} />);
}
// add delete/check icon when applicable
if (!this.props.list.nodelete) {
cells.unshift(this.props.manageMode ? (
<ListControl key="_check" type="check" active={this.props.checkedItems[itemId]} />
) : (
<ListControl key="_delete" onClick={(e) => this.props.deleteTableItem(item, e)} type="delete" />
));
}
var addRow = (<tr key={'i' + item.id} onClick={this.props.manageMode ? (e) => this.props.checkTableItem(item, e) : null} className={rowClassname}>{cells}</tr>);
if (this.props.list.sortable) {
return (
// we could add a preview container/image
// this.props.connectDragPreview(this.props.connectDropTarget(addRow))
this.props.connectDropTarget(addRow)
);
} else {
return (addRow);
}
},
render () {
return this.renderRow(this.props.item);
},
});
module.exports = exports = ItemsRow;
// Expose Sortable
/**
* Implements drag source.
*/
const dragItem = {
beginDrag (props) {
const send = { ...props };
props.dispatch(setDragBase(props.item, props.index));
return { ...send };
},
endDrag (props, monitor, component) {
if (!monitor.didDrop()) {
props.dispatch(resetItems(props.id));
return;
}
const page = props.currentPage;
const pageSize = props.pageSize;
// If we were dropped onto a page change target, then droppedOn.prevSortOrder etc will be
// set by that target, and we should use those values. If we were just dropped onto a new row
// then we need to calculate these values ourselves.
const droppedOn = monitor.getDropResult();
const prevSortOrder = droppedOn.prevSortOrder || props.sortOrder;
// To explain the following line, suppose we are on page 3 and there are 10 items per page.
// Previous to this page, there are (3 - 1)*10 = 20 items before us. If we have index 6
// on this page, then we're the 7th item to display (index starts from 0), and so we
// want to update the display order to 20 + 7 = 27.
const newSortOrder = droppedOn.newSortOrder || (page - 1) * pageSize + droppedOn.index + 1;
// If we were dropped on a page change target, then droppedOn.gotToPage will be set, and we should
// pass this to reorderItems, which will then change the page for the user.
props.dispatch(reorderItems(props.item, prevSortOrder, newSortOrder, Number(droppedOn.goToPage)));
},
};
/**
* Implements drag target.
*/
const dropItem = {
drop (props, monitor, component) {
return { ...props };
},
hover (props, monitor, component) {
// reset row alerts
if (props.rowAlert.success || props.rowAlert.fail) {
props.dispatch(setRowAlert({
reset: true,
}));
}
const dragged = monitor.getItem().index;
const over = props.index;
// self
if (dragged === over) {
return;
}
props.dispatch(moveItem(dragged, over, props));
monitor.getItem().index = over;
},
};
/**
* Specifies the props to inject into your component.
*/
function dragProps (connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
connectDragPreview: connect.dragPreview(),
};
}
function dropProps (connect) {
return {
connectDropTarget: connect.dropTarget(),
};
};
exports.Sortable = DragSource('item', dragItem, dragProps)(DropTarget('item', dropItem, dropProps)(ItemsRow));