UNPKG

reactjs-query-builder

Version:
232 lines (202 loc) 8.48 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import shallowCompare from 'react-addons-shallow-compare'; import {getFieldConfig, getFieldPath, getFieldPathLabels} from "../utils/configUtils"; import {calcTextWidth, truncateString, BUILT_IN_PLACEMENTS} from "../utils/stuff"; import { Menu, Dropdown, Icon, Tooltip, Button, Select } from 'antd'; const { Option, OptGroup } = Select; const SubMenu = Menu.SubMenu; const MenuItem = Menu.Item; const DropdownButton = Dropdown.Button; import map from 'lodash/map'; import last from 'lodash/last'; import keys from 'lodash/keys'; import PureRenderMixin from 'react-addons-pure-render-mixin'; export default class Field extends Component { static propTypes = { config: PropTypes.object.isRequired, selectedField: PropTypes.string, renderAsDropdown: PropTypes.bool, customProps: PropTypes.object, //actions setField: PropTypes.func.isRequired, }; constructor(props) { super(props); } componentWillReceiveProps (nextProps) { //let prevProps = this.props; } shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); curField() { return this.props.selectedField ? getFieldConfig(this.props.selectedField, this.props.config) : null; } curFieldOpts() { return Object.assign({}, { label: this.props.selectedField, }, this.curField() || {} ); } handleFieldMenuSelect = ({key, keyPath}) => { this.props.setField(key); } handleFieldSelect = (key) => { this.props.setField(key); } filterOption = (input, option) => { const { value, groupLabel, children } = option.props; let isInChildren = false; if (typeof children === 'string') { isInChildren = children.toLowerCase().indexOf(input.toLowerCase()) >= 0; } let isInValue = false; if (typeof value === 'string') { isInValue = value.toLowerCase().indexOf(input.toLowerCase()) >= 0; } let isInGroupLabel = false; if (typeof groupLabel === 'string') { isInGroupLabel = groupLabel.toLowerCase().indexOf(input.toLowerCase()) >= 0; } return isInChildren || isInValue || isInGroupLabel; } getFieldDisplayLabel(field, fieldKey) { let fieldSeparator = this.props.config.settings.fieldSeparator; let maxLabelsLength = this.props.config.settings.maxLabelsLength || 100; let label = field.label || last(fieldKey.split(fieldSeparator)); label = truncateString(label, maxLabelsLength); return label; } buildMenuItems(fields, path = null) { let fieldSeparator = this.props.config.settings.fieldSeparator; if (!fields) return null; let prefix = path ? path.join(fieldSeparator) + fieldSeparator : ''; return keys(fields).map(fieldKey => { let field = fields[fieldKey]; let label = this.getFieldDisplayLabel(field, fieldKey); if (field.type == "!struct") { let subpath = (path ? path : []).concat(fieldKey); return <SubMenu key={prefix+fieldKey} title={<span>{label} &nbsp;&nbsp;&nbsp;&nbsp;</span>} > {this.buildMenuItems(field.subfields, subpath)} </SubMenu> } else { return <MenuItem key={prefix+fieldKey}>{label}</MenuItem>; } }); } buildSelectItems(fields, path = null, optGroupLabel = null) { let fieldSeparator = this.props.config.settings.fieldSeparator; if (!fields) return null; let prefix = path ? path.join(fieldSeparator) + fieldSeparator : ''; return keys(fields).map(fieldKey => { let field = fields[fieldKey]; let label = this.getFieldDisplayLabel(field, fieldKey); if (field.type == "!struct") { let subpath = (path ? path : []).concat(fieldKey); return <OptGroup key={prefix+fieldKey} label={label} > {this.buildSelectItems(field.subfields, subpath, label)} </OptGroup> } else { return <Option key={prefix+fieldKey} value={prefix+fieldKey} grouplabel={optGroupLabel} > {label} </Option>; } }); } buildMenuToggler(label, fullLabel, customLabel) { let btnLabel = customLabel ? customLabel : label; let maxLabelsLength = this.props.config.settings.maxLabelsLength || 100; btnLabel = truncateString(btnLabel, maxLabelsLength); var toggler = <Button size={this.props.config.settings.renderSize || "small"} > {btnLabel} <Icon type="down" /> </Button>; if (fullLabel && fullLabel != label) { toggler = <Tooltip placement="top" title={fullLabel} > {toggler} </Tooltip>; } return toggler; } render() { if (this.props.renderAsDropdown) return this.renderAsDropdown(); else return this.renderAsSelect(); } renderAsSelect() { let isFieldSelected = !!this.props.selectedField; let dropdownPlacement = this.props.config.settings.dropdownPlacement; let maxLabelsLength = this.props.config.settings.maxLabelsLength || 100; let fieldOptions = this.props.config.fields; let selectedFieldPartsLabels = getFieldPathLabels(this.props.selectedField, this.props.config); let selectedFieldFullLabel = selectedFieldPartsLabels ? selectedFieldPartsLabels.join(this.props.config.settings.fieldSeparatorDisplay) : null; let placeholder = !isFieldSelected ? this.props.config.settings.fieldPlaceholder : null; let fieldDisplayLabel = isFieldSelected ? this.getFieldDisplayLabel(this.curField(), this.props.selectedField) : null; let selectText = isFieldSelected ? fieldDisplayLabel : placeholder; selectText = truncateString(selectText, maxLabelsLength); let selectWidth = calcTextWidth(selectText, '14px'); //let tooltip = this.curFieldOpts().label2 || selectedFieldFullLabel || this.curFieldOpts().label; let fieldSelectItems = this.buildSelectItems(fieldOptions); let customProps = this.props.customProps || {}; let fieldSelect = ( <Select dropdownAlign={dropdownPlacement ? BUILT_IN_PLACEMENTS[dropdownPlacement] : undefined} dropdownMatchSelectWidth={false} style={{ width: isFieldSelected && !customProps.showSearch ? null : selectWidth + 48 }} ref="field" placeholder={placeholder} size={this.props.config.settings.renderSize || "small"} onChange={this.handleFieldSelect} value={this.props.selectedField || undefined} filterOption={this.filterOption} {...customProps} >{fieldSelectItems}</Select> ); return fieldSelect; } renderAsDropdown() { let fieldOptions = this.props.config.fields; let selectedFieldKeys = getFieldPath(this.props.selectedField, this.props.config); let selectedFieldPartsLabels = getFieldPathLabels(this.props.selectedField, this.props.config); let selectedFieldFullLabel = selectedFieldPartsLabels ? selectedFieldPartsLabels.join(this.props.config.settings.fieldSeparatorDisplay) : null; let placeholder = this.curFieldOpts().label || this.props.config.settings.fieldPlaceholder; let customProps = this.props.customProps || {}; let fieldMenuItems = this.buildMenuItems(fieldOptions); let fieldMenu = ( <Menu //size={this.props.config.settings.renderSize || "small"} selectedKeys={selectedFieldKeys} onClick={this.handleFieldMenuSelect} {...customProps} >{fieldMenuItems}</Menu> ); let fieldToggler = this.buildMenuToggler(placeholder, selectedFieldFullLabel, this.curFieldOpts().label2); return ( <Dropdown overlay={fieldMenu} trigger={['click']} placement={this.props.config.settings.dropdownPlacement} > {fieldToggler} </Dropdown> ); } }