UNPKG

reactjs-query-builder

Version:
361 lines (328 loc) 11.4 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Select, InputNumber, Input, Radio, Popover, Icon, Switch, DatePicker } from 'antd'; import last from 'lodash/last'; import keys from 'lodash/keys'; import clone from 'clone'; import moment from 'moment'; import shallowCompare from 'react-addons-shallow-compare'; import { getFieldConfig, getValueSourcesForFieldOp, } from '../../utils/configUtils'; import { truncateString, } from '../../utils/stuff'; import FieldFunctionValueSrc from '../FieldFunctionValueSrc'; import FieldConstantValueSrc from '../FieldConstantValueSrc'; import { DATA_TYPE, VALUE_SOURCE_FUNCTION, } from '../../constants'; const { Option, } = Select; const RadioButton = Radio.Button; const RadioGroup = Radio.Group; export default class ValueFunction extends Component { static propTypes = { setValue: PropTypes.func.isRequired, config: PropTypes.object.isRequired, field: PropTypes.string.isRequired, value: PropTypes.object, operator: PropTypes.string, customProps: PropTypes.object }; shouldComponentUpdate = shallowCompare; initDataForParams = (functionSelected) => { const { params } = functionSelected; return params.map(item => { switch (item) { case DATA_TYPE.TEXT: return ''; case DATA_TYPE.NUMBER: return 0; case DATA_TYPE.BOOL: return false; case DATA_TYPE.DATE: return moment().toISOString(); default: return null; } }); } /** * @key key function * Handle change select */ handleFieldSelect = (key) => { const functionSelected = this.props.config.functions[key]; this.props.setValue({ parameters: this.initDataForParams(functionSelected), functionSelected: functionSelected.functionName, valueSrc: [], key }); } /** * @functions info of functions (type, key, params,...) * Render list function in select */ buildOptionItems = (functions, path = null) => { const functionSeparator = this.props.config.settings.fieldSeparator; const maxLabelsLength = this.props.config.settings.maxLabelsLength || 100; const prefix = path ? path.join(functionSeparator) + functionSeparator : ''; if (!functions) { return null; } return keys(functions).map(functionKey => { const functionSelect = functions[functionKey]; let label = functionSelect.functionName || last(functionKey.split(functionSeparator)); label = truncateString(label, maxLabelsLength); return ( <Option key={prefix + functions[functionKey].key} value={prefix + functions[functionKey].key} > {label} </Option> ); }); } /** * @value onchange field input * @index position of field input * @dataType data type of field input */ handleChange = (value, index, dataType) => { let valueChange = value; const valueFunctionSelect = clone(this.props.value); if (dataType === DATA_TYPE.TEXT) { valueChange = value.target.value; } if (dataType === DATA_TYPE.DATE && valueChange) { valueChange = value.toISOString(); } valueFunctionSelect.parameters[index] = valueChange; this.props.setValue({ ...valueFunctionSelect }); } /** * @value value of group button * @index position of popover * Handle change popover value source */ handleChangePopover = ({ target }, index) => { const valueFunctionSelect = clone(this.props.value); valueFunctionSelect.valueSrc[index] = target.value; valueFunctionSelect.parameters[index] = ''; this.props.setValue({ ...valueFunctionSelect }); } /** * @index position of radio group button * Render popover */ renderValueSources = (index) => { const { config, field, operator, value } = this.props; const valueSourcesInfo = config.settings.valueSourcesInfo; const valueSourcesPopupTitle = config.settings.valueSourcesPopupTitle; const valueSources = getValueSourcesForFieldOp(config, field, operator); let valueSrc = (value && value.valueSrc && value.valueSrc[index]) || null; if (!valueSources) { return null; } let content = ( <RadioGroup value={valueSrc || 'value'} size={this.props.config.settings.renderSize || 'small'} onChange={(value) => this.handleChangePopover(value, index)} > {valueSources.filter((valueSource) => valueSource !== 'function').map(srcKey => ( <RadioButton key={srcKey} value={srcKey} >{valueSourcesInfo[srcKey].label} </RadioButton> ))} </RadioGroup> ); return ( <Popover className="popover-function" title={valueSourcesPopupTitle} content={content} > <Icon type="ellipsis" /> </Popover> ); } /** * @index position index of params, valueSrc * @dataTypeOfParam data type of param * Render control antd by valueSrc */ renderValueSourceParam = (index, dataTypeOfParam) => { const { config, value, operator, field } = this.props; const valueSource = value && value.valueSrc[index] || 'value'; switch (valueSource) { case VALUE_SOURCE_FUNCTION.FIELD: return ( <FieldFunctionValueSrc value={value} field={field} config={config} operator={operator} dataTypeOfParam={dataTypeOfParam} valueSelected={this.props.value.parameters[index]} handleChangeValue={(value) => this.handleChange(value, index, VALUE_SOURCE_FUNCTION.FIELD)} /> ); case VALUE_SOURCE_FUNCTION.CONSTANT: return ( <FieldConstantValueSrc field={field} config={config} value={this.props.value.parameters[index]} handleChangeValueConstant={(value) => this.handleChange(value, index, VALUE_SOURCE_FUNCTION.CONSTANT)} /> ); case VALUE_SOURCE_FUNCTION.VALUE: return this.filterUIForValueSource(dataTypeOfParam, index); default: return this.filterUIForValueSource(dataTypeOfParam, index); } } /** * @dataType Data type of parameter * @index Position of parameter * Return UI of parameter */ filterUIForValueSource = (dataType, index) => { switch (dataType) { case DATA_TYPE.TEXT: return ( <Input value={this.props.value && this.props.value.parameters[index] || ''} size={this.props.config.settings.renderSize || 'small'} onChange={(value) => this.handleChange(value, index, dataType)} style={{ marginLeft: '8px', width: '134px' }} placeholder="Input value" /> ); case DATA_TYPE.NUMBER: return ( <InputNumber key={index} value={this.props.value && this.props.value.parameters[index] || 0} size={this.props.config.settings.renderSize || 'small'} onChange={(value) => this.handleChange(value, index, dataType)} style={{ marginLeft: '8px' }} placeholder="Input value" /> ); case DATA_TYPE.BOOL: return ( <Switch checked={this.props.value && this.props.value.parameters[index] || false} defaultChecked={false} style={{ marginLeft: '8px' }} onChange={(value) => this.handleChange(value, index, dataType)} /> ); case DATA_TYPE.DATE: return ( <DatePicker style={{ marginLeft: '8px' }} value={this.props.value && moment(this.props.value.parameters[index]) || undefined} onChange={(value) => this.handleChange(value, index, dataType)} allowClear={true} /> ); default: return ( <Input value={this.props.value && this.props.value.parameters[index] || ''} size={this.props.config.settings.renderSize || 'small'} onChange={(value) => this.handleChange(value, index, dataType)} style={{ marginLeft: '8px', width: '134px' }} placeholder="Input value" /> ); } } renderFunctionParams = (functionSelected) => { if (!functionSelected) { return; } const { key, params } = functionSelected; return params.map((dataTypeOfParam, index) => ( <div className="widget--function" key={key + '--' + index}> {this.renderValueSources(index)} {this.renderValueSourceParam(index, dataTypeOfParam)} </div> )); } /** * @config in file config * @leftFieldFullkey info of field * @operator operator of field * Filter function by type */ filterFunctions = (config, leftFieldFullkey) => { const leftFieldConfig = getFieldConfig(leftFieldFullkey, config); const { functions } = config; // Get functions of field config const { type } = leftFieldConfig; const functionsOfField = Object.keys(functions).map(key => { return functions[key].type === type ? functions[key] : undefined; }).filter(func => func); if (!functionsOfField.length) { return []; } return functionsOfField; } /** * @input value search * @option element select */ filterOption = (input, option) => { return option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0; } /** * @functionKey key of function selected * @config info in file config */ getFunctionInit = (functionKey, config) => { const { functions } = config; const result = functions[functionKey] ? functions[functionKey] : null; const renderParams = this.renderFunctionParams(result); return renderParams; } testRender = (type) => { if (type === 'field') { return ( <label>asdasd</label> ); } if (type === 'value') { return ( <input /> ); } } /** * Render select functions */ renderAsSelect = () => { const { value, config, field } = this.props; const placeholder = this.props.config.settings.functionPlaceholder; let fieldOptions = this.filterFunctions(config, field); const customProps = this.props.customProps || {}; const buildOptionItems = this.buildOptionItems(fieldOptions); const initParamsInput = this.props.value && this.getFunctionInit(value.key, config); return ( <div className="widget--valuesrc--function"> <Select value={value && value.key || undefined} style={{ width: '200px' }} ref="function" placeholder={placeholder} size={this.props.config.settings.renderSize || 'small'} onChange={this.handleFieldSelect} filterOption={this.filterOption} {...customProps} > {buildOptionItems} </Select> {this.testRender(value.valueSrc[0] || 'value')} </div> ); } render() { return this.renderAsSelect(); } }