@coocoon/react-awesome-query-builder
Version:
User-friendly query builder for React. Demo: https://ukrbublik.github.io/react-awesome-query-builder
143 lines (127 loc) • 4.24 kB
JSX
import React, { useMemo } from "react";
import { Select, Spin, Divider } from "antd";
import { calcTextWidth, SELECT_WIDTH_OFFSET_RIGHT } from "../../../../utils/domUtils";
import { mapListValues } from "../../../../utils/stuff";
import { useOnPropsChanged } from "../../../../utils/reactUtils";
import omit from "lodash/omit";
import useListValuesAutocomplete from "../../../../hooks/useListValuesAutocomplete";
const Option = Select.Option;
export default (props) => {
const { config, placeholder, allowCustomValues, customProps, value, readonly, multiple, useAsyncSearch } = props;
// hook
const {
open,
onDropdownVisibleChange,
onChange,
isSpecialValue,
onSearch,
inputValue,
options,
isInitialLoading,
isLoading,
aPlaceholder,
extendOptions,
getOptionDisabled,
getOptionLabel,
} = useListValuesAutocomplete(props, {
debounceTimeout: 100,
multiple
});
const filteredOptions = extendOptions(options);
const optionsMaxWidth = useMemo(() => {
return filteredOptions.reduce((max, option) => {
return Math.max(max, calcTextWidth(option.title, null));
}, 0);
}, [options]);
const { defaultSelectWidth, defaultSearchWidth, renderSize } = config.settings;
const placeholderWidth = calcTextWidth(placeholder);
const aValue = value && value.length ? value : undefined;
const width = aValue ? null : placeholderWidth + SELECT_WIDTH_OFFSET_RIGHT;
const dropdownWidth = optionsMaxWidth + SELECT_WIDTH_OFFSET_RIGHT;
const minWidth = width || defaultSelectWidth;
const style = {
width: (multiple ? undefined : minWidth),
minWidth: minWidth
};
const dropdownStyle = {
width: dropdownWidth,
};
const mode = !multiple ? undefined : (allowCustomValues ? "tags" : "multiple");
const dynamicPlaceholder = !readonly ? aPlaceholder : "";
// rendering special 'Load more' option has side effect: on change rc-select will save its title as internal value in own state
const renderedOptions = filteredOptions?.filter(option => !option.specialValue).map((option) => (
<Option
key={option.value}
value={option.value}
disabled={getOptionDisabled(option)}
>
{getOptionLabel(option)}
</Option>
));
const onSpecialClick = (specialValue) => () => {
const option = filteredOptions.find(opt => opt.specialValue == specialValue);
onChange(null, option);
};
const specialOptions = filteredOptions?.filter(option => !!option.specialValue).map((option) => (
<a
style={{ padding: "5px 10px", display: "block", cursor: "pointer" }}
key={option.specialValue}
disabled={getOptionDisabled(option)}
onClick={onSpecialClick(option.specialValue)}
>
{getOptionLabel(option)}
</a>
));
const aOnSelect = async (label, option) => {
if (isSpecialValue(option)) {
await onChange(label, option);
}
};
const aOnChange = async (label, option) => {
if (!isSpecialValue(option)) {
await onChange(label, option);
}
};
const dropdownRender = (menu) => (
<div>
{menu}
{specialOptions.length > 0
&& <>
<Divider style={{ margin: "0px" }}/>
<div style={{ display: "flex", flexDirection: "column" }}>
{specialOptions}
</div>
</>
}
</div>
);
return (
<Select
filterOption={useAsyncSearch ? false : true}
dropdownRender={dropdownRender}
allowClear={true}
notFoundContent={isLoading ? "Loading..." : null}
disabled={readonly}
mode={mode}
style={customProps?.style || style}
dropdownStyle={customProps?.dropdownStyle || dropdownStyle}
key={"widget-autocomplete"}
dropdownMatchSelectWidth={customProps?.dropdownMatchSelectWidth || false}
placeholder={customProps?.placeholder || dynamicPlaceholder}
onDropdownVisibleChange={onDropdownVisibleChange}
onChange={aOnChange}
onSelect={aOnSelect}
onSearch={onSearch}
showArrow
showSearch
size={renderSize}
loading={isLoading}
value={aValue}
//searchValue={inputValue}
open={open}
{...customProps}
>
{renderedOptions}
</Select>
);
};