react-awesome-query-builder-pd
Version:
User-friendly query builder for React. Demo: https://ukrbublik.github.io/react-awesome-query-builder
304 lines (264 loc) • 9.02 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,
path: PropTypes.any, //instanceOf(Immutable.List)
children1: PropTypes.any, //instanceOf(Immutable.OrderedMap)
isDraggingMe: PropTypes.bool,
isDraggingTempo: 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,
actions: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.removeSelf = this.removeSelf.bind(this);
this.renderItem = this.renderItem.bind(this);
}
isGroupTopPosition() {
return startsWith(this.props.config.settings.groupActionsPosition || defaultPosition, "top");
}
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.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.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.size < 2;
}
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} = this.props;
return <GroupActions
config={config}
addRule={addRule}
addGroup={addGroup}
canAddGroup={this.canAddGroup()}
canAddRule={this.canAddRule()}
canDeleteGroup={this.canDeleteGroup()}
removeSelf={this.removeSelf}
/>;
}
canAddGroup = () => {
return this.props.allowFurtherNesting;
}
canAddRule = () => {
const {maxNumberOfRules} = this.props.config.settings;
const {totalRulesCnt} = this.props;
if (maxNumberOfRules) {
return totalRulesCnt < maxNumberOfRules;
}
return true;
};
canDeleteGroup = () => !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} = 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")}
//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.reordableNodesCnt()}
totalRulesCnt={this.props.totalRulesCnt}
onDragStart={onDragStart}
isDraggingTempo={this.props.isDraggingTempo}
/>
);
}
extraPropsForItem(_item) {
return {};
}
reordableNodesCnt() {
return this.props.reordableNodesCnt;
}
showDragIcon() {
const { config, isRoot, reordableNodesCnt } = this.props;
return config.settings.canReorder && !isRoot && reordableNodesCnt > 1;
}
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
} = this.props;
const {immutableGroupsMode, renderConjs: Conjs, showNot: _showNot, notLabel} = config.settings;
const conjunctionOptions = this.conjunctionOptions();
if (!this.showConjs())
return null;
if (!children1.size)
return null;
const renderProps = {
disabled: this.isOneChild(),
readonly: immutableGroupsMode,
selectedConjunction: selectedConjunction,
setConjunction: immutableGroupsMode ? dummyFn : setConjunction,
conjunctionOptions: conjunctionOptions,
config: config,
not: not || false,
id: id,
setNot: immutableGroupsMode ? dummyFn : setNot,
notLabel: notLabel,
showNot: this.showNot(),
};
return <Conjs {...renderProps} />;
}
renderHeader() {
return (
<div className={"group--conjunctions"}>
{this.renderConjs()}
{this.renderDrag()}
</div>
);
}
}
export default GroupContainer(Draggable("group")(ConfirmFn(BasicGroup)));