UNPKG

@react-awesome-query-builder/antd

Version:
185 lines (166 loc) 5.74 kB
import React, { Component } from "react"; import { Tooltip, TreeSelect } from "antd"; import {BUILT_IN_PLACEMENTS, SELECT_WIDTH_OFFSET_RIGHT, calcTextWidth} from "../../utils/domUtils"; import PropTypes from "prop-types"; import { Utils } from "@react-awesome-query-builder/ui"; const { useOnPropsChanged } = Utils.ReactUtils; const mapFieldItemToOptionKeys = { key: "_value2", path: "value", label: "label", altLabel: "altLabel", tooltip: "_tooltip", grouplabel: "_grouplabel", fullLabel: "fullLabel", }; export default class FieldTreeSelect extends Component { static propTypes = { config: PropTypes.object.isRequired, customProps: PropTypes.object, errorText: PropTypes.string, items: PropTypes.array.isRequired, placeholder: PropTypes.string, selectedKey: PropTypes.string, selectedKeys: PropTypes.array, selectedPath: PropTypes.array, selectedLabel: PropTypes.string, selectedAltLabel: PropTypes.string, selectedFullLabel: PropTypes.string, selectedOpts: PropTypes.object, readonly: PropTypes.bool, //actions setField: PropTypes.func.isRequired, }; constructor(props) { super(props); useOnPropsChanged(this); this.onPropsChanged(props); this.state = { open: false }; } onPropsChanged(nextProps) { const { items, config: {settings: {fieldSeparator}}} = nextProps; let optionsMaxWidth = 0; const initialOffset = 24; // arrow + checkbox for leftmost item const offset = 20; const padding = 5 * 2; this.treeData = this.getTreeData(items, ({label, path}) => { optionsMaxWidth = Math.max(optionsMaxWidth, calcTextWidth(label, null) + padding + (path.split(fieldSeparator).length - 1) * offset + initialOffset ); }); if (!isNaN(optionsMaxWidth) && optionsMaxWidth) { this.optionsMaxWidth = optionsMaxWidth; } } setOpen = (newOpen) => { this.setState({ open: newOpen }); }; getTreeData(fields, fn = null) { return fields.map(field => { const {items, key, path, label, fullLabel, altLabel, tooltip, disabled, grouplabel, matchesType} = field; if (fn) fn(field); const pathKey = path || key; const optionText = matchesType ? <b>{label}</b> : label; const option = tooltip ? <Tooltip title={tooltip}>{optionText}</Tooltip> : optionText; if (items) { return { value: pathKey, title: option, children: this.getTreeData(items, fn), selectable: false, altLabel: altLabel, fullLabel: fullLabel, label: label, disabled: disabled, _value2: key, _tooltip: tooltip, _grouplabel: grouplabel, }; } else { return { value: pathKey, title: option, altLabel: altLabel, fullLabel: fullLabel, label: label, disabled: disabled, _value2: key, _tooltip: tooltip, _grouplabel: grouplabel, }; } }); } onChange = (key) => { this.props.setField(key); }; filterTreeNode = (input, option) => { const { config } = this.props; const keysForFilter = config.settings.fieldItemKeysForSearch .map(k => mapFieldItemToOptionKeys[k]); const valueForFilter = keysForFilter .map(k => (typeof option[k] == "string" ? option[k] : "")) .join("\0"); const matches = valueForFilter.toLowerCase().indexOf(input.toLowerCase()) >= 0; return matches; }; render() { const { config, customProps = {}, placeholder, errorText, selectedKey, selectedLabel, selectedOpts, selectedAltLabel, selectedFullLabel, readonly, } = this.props; const { open } = this.state; const { renderSize, fieldSeparator } = config.settings; let tooltipText = selectedAltLabel || selectedFullLabel; if (tooltipText == selectedLabel) tooltipText = null; const selectedPath = selectedKey ? selectedKey.split(fieldSeparator) : null; const treeDefaultExpandedKeys = selectedPath && selectedPath.length > 1 ? selectedPath.slice(0, -1).map((_key, i) => (selectedPath.slice(0, i+1).join(fieldSeparator))) : null; const placeholderWidth = calcTextWidth(placeholder); const isFieldSelected = !!selectedKey; const minWidth = placeholderWidth ? placeholderWidth + SELECT_WIDTH_OFFSET_RIGHT + 6 : null; const dropdownMinWidth = 100; const dropdownMaxWidth = 800; const useAutoWidth = true || !this.optionsMaxWidth; //tip: "auto" is good, but width will jump on expand/collapse const dropdownWidth = Math.max(dropdownMinWidth, Math.min(dropdownMaxWidth, this.optionsMaxWidth)); let res = ( <TreeSelect open={open} onDropdownVisibleChange={this.setOpen} status={errorText && "error"} onChange={this.onChange} value={selectedKey || undefined} style={{ minWidth: minWidth, width: isFieldSelected ? null : minWidth, }} dropdownStyle={{ width: useAutoWidth ? "auto" : dropdownWidth + 20, paddingRight: "10px" }} multiple={false} treeCheckable={false} treeDataSimpleMode={false} treeData={this.treeData} size={renderSize} placeholder={placeholder} filterTreeNode={this.filterTreeNode} treeDefaultExpandedKeys={treeDefaultExpandedKeys} popupMatchSelectWidth={false} disabled={readonly} {...customProps} /> ); if (tooltipText && !selectedOpts.tooltip) { res = <Tooltip title={!open ? tooltipText : null}>{res}</Tooltip>; } return res; } }