UNPKG

@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
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)));