// Copyright (c) 2022 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import React, {Component, ComponentType} from 'react';
import styled from 'styled-components';
import {createSelector} from 'reselect';
import ItemSelector from './item-selector/item-selector';
import {classList} from './item-selector/dropdown-list';
import {toArray} from 'utils/utils';
import {notNullorUndefined} from 'utils/data-utils';
import FieldTokenFactory from '../common/field-token';
import {Field} from 'utils/table-utils/kepler-table';
import {FIELD_TYPES_AGGREGATORS, FIELD_AGGREGATORS} from 'constants/default-settings';
const defaultDisplayOption = d => {
const optionName = d.displayName || d.name;
const aggregator = d.aggregator;
const aggegatorDisplay = FIELD_AGGREGATORS.find(a => a.id === aggregator)?.displayName;
if (aggegatorDisplay) {
return `${aggegatorDisplay}(${optionName})`;
}
return optionName;
};
const defaultValueOption = d => d.name;
const StyledToken = styled.div`
display: inline-block;
margin: 0 0 0 auto;
`;
const StyledFieldListItem = styled.div`
display: flex;
flex: 1 1 auto;
margin-right: 10px;
line-height: 0;
align-items: center;
`;
FieldListItemFactoryFactory.deps = [FieldTokenFactory];
// custom list Item
export function FieldListItemFactoryFactory(FieldToken) {
const FieldListItemFactory = (showToken = true) => {
const FieldListItem = ({value, displayOption = defaultDisplayOption}) => {
return (
{displayOption(value)}
{showToken && value.type ? (
) : null}
);
};
return FieldListItem;
};
return FieldListItemFactory;
}
const SuggestedFieldHeader = () =>
Suggested Field
;
type FieldType =
| string
| string[]
| {
name: string;
format: string | null;
}[]
| {
format?: string;
id?: string;
name?: string;
fieldIdx?: number;
type?: number;
}
| Field;
interface FieldSelectorFactoryProps {
fields?: FieldType[];
onSelect: (
items:
| ReadonlyArray
| string
| number
| boolean
| object
| null
) => void;
placement?: string;
value?: FieldType | null;
filterFieldTypes?: FieldType | FieldType[];
inputTheme?: string;
placeholder?: string;
erasable?: boolean;
error?: boolean;
multiSelect?: boolean;
closeOnSelect?: boolean;
showToken?: boolean;
suggested?: ReadonlyArray | null;
CustomChickletComponent?: ComponentType;
size?: string;
isLoading?: boolean;
aggregator?: any,
withAggregator?: any
onBlur?: any,
onFocus?: any,
}
function FieldSelectorFactory(
FieldListItemFactory: ReturnType
): ComponentType {
class FieldSelector extends Component {
static defaultProps = {
erasable: true,
error: false,
fields: [],
onSelect: () => {},
placement: 'bottom',
value: null,
aggregator: null,
withAggregator: false,
multiSelect: false,
closeOnSelect: true,
showToken: true,
placeholder: 'placeholder.selectField'
};
fieldsSelector = props => props.fields;
filteredFieldsSelector = props =>
props.withAggregator
? props.fields
: props.fields.filter(
field =>
!toArray(props.value).find(d => (d.name ? d.name === field.name : d === field.name))
);
valueSelector = props => props.value;
aggregatorSelector = props => props.aggregator;
filterFieldTypesSelector = props => props.filterFieldTypes;
showTokenSelector = props => props.showToken;
withAggregatorSelector = props => props.withAggregator;
selectedItemsSelector = createSelector(
this.fieldsSelector,
this.valueSelector,
this.aggregatorSelector,
(fields, value, aggregator) =>
toArray(value)
.map(d => {
const field = fields.find(f =>
notNullorUndefined(d) && d.name
? d.name === defaultValueOption(f)
: d === defaultValueOption(f)
);
if (field && aggregator) {
return {...field, aggregator};
}
return field;
})
.filter(d => d)
);
fieldOptionsSelector = createSelector(
this.filteredFieldsSelector,
this.valueSelector,
this.filterFieldTypesSelector,
this.aggregatorSelector,
this.withAggregatorSelector,
(fields, value, filterFieldTypes, aggregator, withAggregator) => {
const fieldsWithAggregators = fields.map(field => {
return {
...field,
...(withAggregator && {
aggregators: FIELD_TYPES_AGGREGATORS[field.type]?.filter(agg => {
return field.id === value ? agg !== aggregator : true;
})
})
};
});
if (!filterFieldTypes) {
return fieldsWithAggregators;
}
const filters = Array.isArray(filterFieldTypes) ? filterFieldTypes : [filterFieldTypes];
return fieldsWithAggregators.filter(f => filters.includes(f.type));
}
);
fieldListItemSelector = createSelector(this.showTokenSelector, FieldListItemFactory);
render() {
return (
d}
closeOnSelect={this.props.closeOnSelect}
displayOption={defaultDisplayOption}
filterOption="displayName"
fixedOptions={this.props.suggested}
inputTheme={this.props.inputTheme}
size={this.props.size}
isError={this.props.error}
isLoading={this.props.isLoading}
selectedItems={this.selectedItemsSelector(this.props)}
erasable={this.props.erasable}
options={this.fieldOptionsSelector(this.props)}
multiSelect={this.props.multiSelect}
placeholder={this.props.placeholder}
placement={this.props.placement}
onChange={this.props.onSelect}
DropDownLineItemRenderComponent={this.fieldListItemSelector(this.props)}
DropdownHeaderComponent={this.props.suggested ? SuggestedFieldHeader : null}
CustomChickletComponent={this.props.CustomChickletComponent}
withSubOptions={this.props.withAggregator}
onBlur={this.props.onBlur}
onFocus={this.props.onFocus}
/>
);
}
}
return FieldSelector;
}
FieldSelectorFactory.deps = [FieldListItemFactoryFactory];
export default FieldSelectorFactory;