@coocoon/react-awesome-query-builder
Version:
User-friendly query builder for React. Demo: https://ukrbublik.github.io/react-awesome-query-builder
337 lines (293 loc) • 9.67 kB
JSX
import React, { Component, PureComponent } from "react";
import PropTypes from "prop-types";
import startsWith from "lodash/startsWith";
import GroupContainer from "../containers/GroupContainer";
import Draggable from "../containers/Draggable";
const classNames = require("classnames");
import { Item } from "./Item";
import {GroupActions} from "./GroupActions";
import {ConfirmFn, DragIcon, dummyFn} from "../utils";
const defaultPosition = "topRight";
export class BasicGroup extends PureComponent {
static propTypes = {
//tree: PropTypes.instanceOf(Immutable.Map).isRequired,
reordableNodesCnt: 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,
groupId: PropTypes.string,
path: PropTypes.any, //instanceOf(Immutable.List)
children1: PropTypes.any, //instanceOf(Immutable.OrderedMap)
isDraggingMe: PropTypes.bool,
isDraggingTempo: PropTypes.bool,
isLocked: PropTypes.bool,
isTrueLocked: PropTypes.bool,
//actions
handleDraggerMouseDown: PropTypes.func,
onDragStart: PropTypes.func,
addRule: PropTypes.func.isRequired,
addGroup: PropTypes.func.isRequired,
removeSelf: PropTypes.func.isRequired,
setConjunction: PropTypes.func.isRequired,
setNot: PropTypes.func.isRequired,
setLock: PropTypes.func.isRequired,
actions: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.removeSelf = this.removeSelf.bind(this);
this.setLock = this.setLock.bind(this);
this.renderItem = this.renderItem.bind(this);
}
isGroupTopPosition() {
return startsWith(this.props.config.settings.groupActionsPosition || defaultPosition, "top");
}
setLock(lock) {
this.props.setLock(lock);
}
removeSelf() {
const {confirmFn} = this.props;
const {renderConfirm, removeGroupConfirmOptions: confirmOptions} = this.props.config.settings;
const doRemove = () => {
this.props.removeSelf();
};
if (confirmOptions && !this.isEmptyCurrentGroup()) {
renderConfirm({...confirmOptions,
onOk: doRemove,
onCancel: null,
confirmFn: confirmFn
});
} else {
doRemove();
}
}
isEmptyCurrentGroup() {
const children = this.props.children1;
return !children || children.size == 0
|| children.size == 1 && this.isEmpty(children.first());
}
isEmpty(item) {
const isGroup = (item.get("type") == "group" || item.get("type") == "rule_group");
return isGroup ? this.isEmptyGroup(item) : this.isEmptyRule(item);
}
isEmptyGroup(group) {
const children = group.get("children1");
return !children || children.size == 0
|| children.size == 1 && this.isEmpty(children.first());
}
isEmptyRule(rule) {
const properties = rule.get("properties");
return !(
properties.get("field") !== null
&& properties.get("operator") !== null
&& properties.get("value").filter((val) => val !== undefined).size > 0
);
}
render() {
return <>
{this.renderHeaderWrapper()}
{this.renderChildrenWrapper()}
{this.renderFooterWrapper()}
</>;
}
showNot() {
const {config} = this.props;
return config.settings.showNot;
}
// show conjs for 2+ children?
showConjs() {
const {conjunctionOptions, children1, config} = this.props;
const conjunctionCount = Object.keys(conjunctionOptions).length;
return conjunctionCount > 1 || this.showNot();
}
isOneChild() {
const {children1} = this.props;
return children1 ? children1.size < 2 : true;
}
renderChildrenWrapper() {
const {children1} = this.props;
return children1 && (
<div key="group-children" className={classNames(
"group--children",
!this.showConjs() ? "hide--conjs" : "",
this.isOneChild() ? "hide--line" : "",
this.isOneChild() ? "one--child" : "",
this.childrenClassName()
)}>{this.renderChildren()}</div>
);
}
childrenClassName = () => "";
renderHeaderWrapper() {
const isGroupTopPosition = this.isGroupTopPosition();
return (
<div key="group-header" className={classNames(
"group--header",
this.isOneChild() ? "one--child" : "",
)}>
{this.renderHeader()}
{isGroupTopPosition && this.renderBeforeActions()}
{isGroupTopPosition && this.renderActions()}
{isGroupTopPosition && this.renderAfterActions()}
</div>
);
}
renderFooterWrapper() {
const isGroupTopPosition = this.isGroupTopPosition();
return !isGroupTopPosition && (
<div key="group-footer" className='group--footer'>
{this.renderBeforeActions()}
{this.renderActions()}
{this.renderAfterActions()}
</div>
);
}
renderBeforeActions = () => {
const BeforeActions = this.props.config.settings.renderBeforeActions;
if (BeforeActions == undefined)
return null;
return typeof BeforeActions === "function" ? <BeforeActions {...this.props}/> : BeforeActions;
};
renderAfterActions = () => {
const AfterActions = this.props.config.settings.renderAfterActions;
if (AfterActions == undefined)
return null;
return typeof AfterActions === "function" ? <AfterActions {...this.props}/> : AfterActions;
};
renderActions() {
const {config, addRule, addGroup, isLocked, isTrueLocked, id} = this.props;
return <GroupActions
config={config}
addRule={addRule}
addGroup={addGroup}
canAddGroup={this.canAddGroup()}
canAddRule={this.canAddRule()}
canDeleteGroup={this.canDeleteGroup()}
removeSelf={this.removeSelf}
setLock={this.setLock}
isLocked={isLocked}
isTrueLocked={isTrueLocked}
id={id}
/>;
}
canAddGroup() {
return this.props.allowFurtherNesting;
}
canAddRule() {
const maxNumberOfRules = this.props.config.settings.maxNumberOfRules;
const totalRulesCnt = this.props.totalRulesCnt;
if (maxNumberOfRules) {
return totalRulesCnt < maxNumberOfRules;
}
return true;
}
canDeleteGroup() {
return !this.props.isRoot;
}
renderChildren() {
const {children1} = this.props;
return children1 ? children1.map(this.renderItem).toList() : null;
}
renderItem(item) {
const props = this.props;
const {config, actions, onDragStart, isLocked} = props;
const isRuleGroup = item.get("type") == "group" && item.getIn(["properties", "field"]) != null;
const type = isRuleGroup ? "rule_group" : item.get("type");
return (
<Item
{...this.extraPropsForItem(item)}
key={item.get("id")}
id={item.get("id")}
groupId={props.id}
//path={props.path.push(item.get('id'))}
path={item.get("path")}
type={type}
properties={item.get("properties")}
config={config}
actions={actions}
children1={item.get("children1")}
//tree={props.tree}
reordableNodesCnt={this.reordableNodesCntForItem(item)}
totalRulesCnt={this.totalRulesCntForItem(item)}
parentReordableNodesCnt={this.reordableNodesCnt()}
onDragStart={onDragStart}
isDraggingTempo={this.props.isDraggingTempo}
isParentLocked={isLocked}
/>
);
}
extraPropsForItem(_item) {
return {};
}
reordableNodesCnt() {
if (this.props.isLocked)
return 0;
return this.props.reordableNodesCnt;
}
totalRulesCntForItem(_item) {
return this.props.totalRulesCnt;
}
reordableNodesCntForItem(_item) {
if (this.props.isLocked)
return 0;
return this.reordableNodesCnt();
}
showDragIcon() {
const { config, isRoot, isLocked } = this.props;
const reordableNodesCnt = this.reordableNodesCnt();
return config.settings.canReorder && !isRoot && reordableNodesCnt > 1 && !isLocked;
}
renderDrag() {
const { handleDraggerMouseDown } = this.props;
const drag = this.showDragIcon()
&& <span
key="group-drag-icon"
className={"qb-drag-handler group--drag-handler"}
onMouseDown={handleDraggerMouseDown}
><DragIcon /> </span>;
return drag;
}
conjunctionOptions() {
const { conjunctionOptions } = this.props;
return conjunctionOptions;
}
renderConjs() {
const {
config, children1, id,
selectedConjunction, setConjunction, not, setNot, isLocked
} = this.props;
const {immutableGroupsMode, renderConjs: Conjs, showNot: _showNot, notLabel} = config.settings;
const conjunctionOptions = this.conjunctionOptions();
if (!this.showConjs())
return null;
if (!children1 || !children1.size)
return null;
const renderProps = {
disabled: this.isOneChild(),
readonly: immutableGroupsMode || isLocked,
selectedConjunction: selectedConjunction,
setConjunction: immutableGroupsMode ? dummyFn : setConjunction,
conjunctionOptions: conjunctionOptions,
config: config,
not: not || false,
id: id,
setNot: immutableGroupsMode ? dummyFn : setNot,
notLabel: notLabel,
showNot: this.showNot(),
isLocked: isLocked
};
return <Conjs {...renderProps} />;
}
renderHeader() {
return (
<div className={"group--conjunctions"}>
{this.renderConjs()}
{this.renderDrag()}
</div>
);
}
}
export default GroupContainer(Draggable("group")(ConfirmFn(BasicGroup)));