reactjs-query-builder
Version:
296 lines (269 loc) • 9.82 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import shallowCompare from 'react-addons-shallow-compare';
import map from 'lodash/map';
import startsWith from 'lodash/startsWith'
import GroupContainer from './containers/GroupContainer';
import { Row, Col, Icon, Button, Radio, Modal } from 'antd';
const { confirm } = Modal;
const ButtonGroup = Button.Group;
const RadioButton = Radio.Button;
const RadioGroup = Radio.Group;
const classNames = require('classnames');
import Immutable from 'immutable';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import { Provider, Connector, connect } from 'react-redux';
import Item from './Item';
export const groupActionsPositionList = {
topLeft: 'group--actions--tl',
topCenter: 'group--actions--tc',
topRight: 'group--actions--tr',
bottomLeft: 'group--actions--bl',
bottomCenter: 'group--actions--bc',
bottomRight: 'group--actions--br'
}
const defaultPosition = 'topRight'
class Group extends Component {
static propTypes = {
isForDrag: PropTypes.bool,
//tree: PropTypes.instanceOf(Immutable.Map).isRequired,
treeNodesCnt: PropTypes.number,
conjunctionOptions: PropTypes.object.isRequired,
allowFurtherNesting: PropTypes.bool.isRequired,
isRoot: PropTypes.bool.isRequired,
not: PropTypes.bool,
selectedConjunction: PropTypes.string,
config: PropTypes.object.isRequired,
id: PropTypes.string.isRequired,
path: PropTypes.any, //instanceOf(Immutable.List)
onDragStart: PropTypes.func,
children1: PropTypes.any, //instanceOf(Immutable.OrderedMap)
//actions
addRule: PropTypes.func.isRequired,
addGroup: PropTypes.func.isRequired,
removeSelf: PropTypes.func.isRequired,
setConjunction: PropTypes.func.isRequired,
setNot: PropTypes.func.isRequired,
actions: PropTypes.object.isRequired,
//connected:
dragging: PropTypes.object, //{id, x, y, w, h}
};
pureShouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
shouldComponentUpdate = this.pureShouldComponentUpdate;
constructor(props) {
super(props);
this._setConjunctionHandlers = {};
}
_getSetConjunctionHandler = (itemKey = null) => {
const k = '' + itemKey;
let h = this._setConjunctionHandlers[k];
if (!h) {
h = this._setConjunction.bind(this, itemKey)
this._setConjunctionHandlers[k] = h;
}
return h;
}
_setConjunction = (itemKey, e) => {
this.props.setConjunction(e, itemKey);
}
handleDraggerMouseDown = (e) => {
var nodeId = this.props.id;
var dom = this.refs.group;
if (this.props.onDragStart) {
this.props.onDragStart(nodeId, dom, e);
}
}
getGroupPositionClass = () => {
const { groupActionsPosition } = this.props.config.settings
return groupActionsPositionList[groupActionsPosition] || groupActionsPositionList[defaultPosition]
}
isGroupTopPosition = () => {
return startsWith(this.props.config.settings.groupActionsPosition || defaultPosition, 'top')
}
getRenderType(props) {
let renderType;
if (props.dragging && props.dragging.id == props.id) {
renderType = props.isForDrag ? 'dragging' : 'placeholder';
} else {
renderType = props.isForDrag ? null : 'normal';
}
return renderType;
}
removeSelf = () => {
const confirmOptions = this.props.config.settings.removeGroupConfirmOptions;
const doRemove = () => {
this.props.removeSelf();
};
if (confirmOptions && !this.isEmptyCurrentGroup()) {
confirm({...confirmOptions,
onOk: doRemove,
onCancel: null
});
} else {
doRemove();
}
}
isEmptyCurrentGroup = () => {
const children = this.props.children1;
return children.size == 0 ||
children.size == 1 && this.isEmpty(children.first());
}
isEmpty = (item) => {
return item.get("type") == "group" ? this.isEmptyGroup(item) : this.isEmptyRule(item);
}
isEmptyGroup = (group) => {
const children = group.get("children1");
return children.size == 0 ||
children.size == 1 && this.isEmpty(children.first());
}
isEmptyRule = (rule) => {
const properties = rule.get('properties');
console.log(rule.toJS(), properties.toJS());
return !(
properties.get("field") !== null &&
properties.get("operator") !== null &&
properties.get("value").filter((val) => val !== undefined).size > 0
);
}
renderGroup = (position) => {
return (
<div className={`group--actions ${position}`}>
<ButtonGroup
size={this.props.config.settings.renderSize || "small"}
>{!this.props.config.settings.readonlyMode &&
<Button
icon="plus"
className="action action--ADD-RULE"
onClick={this.props.addRule}
>{this.props.config.settings.addRuleLabel || "Add rule"}</Button>
}
{!this.props.config.settings.readonlyMode && this.props.allowFurtherNesting ? (
<Button
className="action action--ADD-GROUP"
icon="plus-circle-o"
onClick={this.props.addGroup}
>{this.props.config.settings.addGroupLabel || "Add group"}</Button>
) : null}
{!this.props.config.settings.readonlyMode && !this.props.isRoot ? (
<Button
type="danger"
icon="delete"
className="action action--ADD-DELETE"
onClick={this.removeSelf}
>{this.props.config.settings.delGroupLabel !== undefined ? this.props.config.settings.delGroupLabel : "Delete"}</Button>
) : null}
</ButtonGroup>
</div>
)
}
renderChildren = () => {
let props = this.props;
return props.children1 ? props.children1.map((item) => (
<Item
key={item.get('id')}
id={item.get('id')}
//path={props.path.push(item.get('id'))}
path={item.get('path')}
type={item.get('type')}
properties={item.get('properties')}
config={props.config}
actions={props.actions}
children1={item.get('children1')}
//tree={props.tree}
treeNodesCnt={props.treeNodesCnt}
onDragStart={props.onDragStart}
/>
)).toList() : null;
}
renderHeader = () => {
let renderConjsAsRadios = false;
return (
<div className={classNames(
"group--conjunctions",
// this.props.children1.size < 2 && this.props.config.settings.hideConjForOne ? 'hide--conj' : ''
)}>
{this.props.config.settings.renderConjsAsRadios ?
<RadioGroup
disabled={this.props.children1.size < 2}
value={this.props.selectedConjunction}
size={this.props.config.settings.renderSize || "small"}
onChange={this.props.setConjunction}
>
{map(this.props.conjunctionOptions, (item, index) => (
<RadioButton
key={item.id}
value={item.key}
//checked={item.checked}
>{item.label}</RadioButton>
))}
</RadioGroup>
:
<ButtonGroup
size={this.props.config.settings.renderSize || "small"}
disabled={this.props.children1.size < 2}
>
{this.props.config.settings.showNot &&
<Button
onClick={(ev) => this.props.setNot(ev, !this.props.not)}
type={this.props.not ? "primary" : null}
>{this.props.config.settings.notLabel}</Button>
}
{map(this.props.conjunctionOptions, (item, index) => (
<Button
disabled={this.props.children1.size < 2}
key={item.id}
type={item.checked ? "primary" : null}
onClick={this._getSetConjunctionHandler(item.key)}
>{item.label}</Button>
))}
</ButtonGroup>
}
{this.props.config.settings.canReorder && this.props.treeNodesCnt > 2 && !this.props.isRoot &&
<span className={"qb-drag-handler"} onMouseDown={this.handleDraggerMouseDown} > <Icon type="bars" /> </span>
}
</div>
);
}
render() {
let renderType = this.getRenderType(this.props);
if (!renderType)
return null;
let styles = {};
if (renderType == 'dragging') {
styles = {
top: this.props.dragging.y,
left: this.props.dragging.x,
width: this.props.dragging.w
};
}
return (
<div
className={classNames("group", "group-or-rule",
renderType == 'placeholder' ? 'qb-placeholder' : null,
renderType == 'dragging' ? 'qb-draggable' : null,
)}
style={styles}
ref="group"
data-id={this.props.id}
>
<div className="group--header">
{this.renderHeader()}
{this.isGroupTopPosition() && this.renderGroup(this.getGroupPositionClass())}
</div>
{this.props.children1 ? (
<div className={classNames(
"group--children",
this.props.children1.size < 2 && this.props.config.settings.hideConjForOne ? 'hide--line' : ''
)}>{this.renderChildren()}</div>
) : null}
{!this.isGroupTopPosition() && (
<div className='group--footer'>
{this.renderGroup(this.getGroupPositionClass())}
</div>
)}
</div>
);
}
}
export default Group;