/* eslint-disable */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import classnames from 'classnames';
import { themr } from 'react-css-themr';
import { AUTOCOMPLETE } from '../identifiers.js';
import InjectChip from '../chip/Chip.js';
import InjectInput from '../input/Input.js';
import events from '../utils/events.js';
import Portal from '../hoc/Portal';
import TransitionGroup from 'react-transition-group/TransitionGroup';
import Transition from 'react-transition-group/Transition';
const POSITION = {
AUTO: 'auto',
DOWN: 'down',
UP: 'up',
};
const factory = (Chip, Input) => {
class Autocomplete extends Component {
static propTypes = {
allowClear: PropTypes.bool,
clearTooltip: PropTypes.string,
className: PropTypes.string,
direction: PropTypes.oneOf(['auto', 'up', 'down']),
disabled: PropTypes.bool,
error: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
]),
keepFocusOnChange: PropTypes.bool,
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node,
]),
labelKey: PropTypes.string,
valueKey: PropTypes.string,
multiple: PropTypes.bool,
onBlur: PropTypes.func,
onChange: PropTypes.func,
onFocus: PropTypes.func,
onKeyDown: PropTypes.func,
onKeyUp: PropTypes.func,
onQueryChange: PropTypes.func,
query: PropTypes.string,
selectedPosition: PropTypes.oneOf(['above', 'below', 'none']),
source: PropTypes.any,
minWidth: PropTypes.number,
more: PropTypes.string,
suggestionMatch: PropTypes.oneOf(['disabled', 'start', 'anywhere', 'word', 'none']),
theme: PropTypes.shape({
active: PropTypes.string,
autocomplete: PropTypes.string,
focus: PropTypes.string,
input: PropTypes.string,
suggestion: PropTypes.string,
suggestions: PropTypes.string,
up: PropTypes.string,
value: PropTypes.string,
values: PropTypes.string,
}),
value: PropTypes.any,
inputProps: PropTypes.object,
};
static defaultProps = {
allowClear: false,
clearTooltip: 'Clear',
className: '',
direction: 'auto',
keepFocusOnChange: false,
multiple: true,
selectedPosition: 'above',
source: {},
suggestionMatch: 'start',
};
state = {
focus: false,
query: this.props.query,
};
componentWillReceiveProps(nextProps) {
if (this.props.query !== nextProps.query) {
this.setState({ query: nextProps.query });
}
}
shouldComponentUpdate(nextProps, nextState) {
if (!this.state.focus && nextState.focus) {
this.calculateDirection();
}
return true;
}
handleChange = (value, event) => {
if (this.props.onChange) {
this.props.onChange(value, event);
}
if (!this.props.keepFocusOnChange) {
this.updateQuery(undefined);
this.setState({ query: undefined, focus: false }, () => {
ReactDOM.findDOMNode(this).querySelector('input').blur();
});
}
};
handleMouseDown = (event) => {
this.selectOrCreateActiveItem(event);
};
handleQueryBlur = (event) => {
if (this.state.focus) this.setState({ focus: false });
if (this.props.onBlur) this.props.onBlur(event, this.state.active);
};
updateQuery = (query) => {
if (this.props.onQueryChange) this.props.onQueryChange(query);
this.setState({ query });
};
handleQueryChange = (value, event) => {
if (value === '' && !this.props.multiple &&
this.props.allowClear && this.props.onChange) {
this.props.onChange(null, event);
}
this.updateQuery(value);
this.setState({ active: null });
};
handleQueryFocus = (event) => {
event.target.scrollTop = 0;
this.setState({ active: null, focus: true });
if (this.props.onFocus) this.props.onFocus(event);
};
handleQueryKeyDown = (event) => {
if (event.which === 13) {
this.selectOrCreateActiveItem(event);
}
if(this.props.onKeyDown) this.props.onKeyDown(event);
};
handleQueryKeyUp = (event) => {
if (event.which === 27) ReactDOM.findDOMNode(this).querySelector('input').blur();
if ([40, 38].indexOf(event.which) !== -1) {
const suggestionsKeys = [...this.suggestions().keys()];
let index = suggestionsKeys.indexOf(this.state.active) + (event.which === 40 ? +1 : -1);
if (index < 0) index = suggestionsKeys.length - 1;
if (index >= suggestionsKeys.length) index = 0;
this.setState({ active: suggestionsKeys[index] });
}
if(this.props.onKeyUp) this.props.onKeyUp(event);
};
handleSuggestionHover = (event) => {
let t = event.target;
while (t && !t.id) {
t = t.parentNode;
}
this.setState({ active: t && t.id || '' });
};
calculateDirection() {
const client = ReactDOM.findDOMNode(this.inputNode).getBoundingClientRect();
const screen_width = window.innerWidth || document.documentElement.offsetWidth;
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
let direction = this.props.direction;
if (this.props.direction === 'auto') {
const up = client.top > ((screen_height / 2) + client.height);
direction = up ? 'up' : 'down';
}
let top = client.top
+ (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop)
- (document.documentElement.clientTop || document.body.clientTop || 0);
top = direction == 'down' ? top + client.height + 'px' : top + 'px';
const bottom = direction == 'up' ? '0px' : undefined;
let left = (client.left
+ (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft)
- (document.documentElement.clientLeft || document.body.clientLeft || 0));
let width = (this.props.minWidth && client.width < this.props.minWidth ? this.props.minWidth : client.width);
if (left + width >= screen_width) {
left = screen_width - width;
}
let maxHeight = direction == 'down' ? (screen_height-client.top-client.height) : client.top;
if (maxHeight > screen_height*0.45) {
maxHeight = Math.floor(screen_height*0.45);
}
left = left+'px';
width = width+'px';
maxHeight = maxHeight+'px';
if (this.state.top !== top || this.state.left !== left ||
this.state.width !== width || this.state.bottom !== bottom ||
this.state.maxHeight !== maxHeight) {
this.setState({ top, bottom, left, width, maxHeight });
}
}
selectOrCreateActiveItem(event) {
let target = this.state.active;
if (target == null) {
for (let k of this.suggestions().keys()) {
this.setState({ active: k });
break;
}
} else {
this.select(event, target);
}
}
normalise(value) {
const sdiak = 'áâäąáâäąččććççĉĉďđďđééěëēėęéěëēėęĝĝğğġġģģĥĥħħíîíîĩĩīīĭĭįįi̇ıĵĵķķĸĺĺļļŀŀłłĺľĺľňńņŋŋņňńʼnóöôőøōōóöőôøřřŕŕŗŗššśśŝŝşşţţťťŧŧũũūūŭŭůůűűúüúüűųųŵŵýyŷŷýyžžźźżżß';
const bdiak = 'AAAAAAAACCCCCCCCDDDDEEEEEEEEEEEEEGGGGGGGGHHHHIIIIIIIIIIIIIIJJKKKLLLLLLLLLLLLNNNNNNNNNOOOOOOOOOOOORRRRRRSSSSSSSSTTTTTTUUUUUUUUUUUUUUUUUWWYYYYYYZZZZZZS';
let normalised = '';
for (let p = 0; p < value.length; p++) {
if (sdiak.indexOf(value.charAt(p)) !== -1) {
normalised += bdiak.charAt(sdiak.indexOf(value.charAt(p)));
} else {
normalised += value.charAt(p);
}
}
return normalised.toLowerCase().trim();
}
suggestions() {
let suggest = new Map();
const source = this.source();
const query = this.normalise(this.state.query == null ? '' : this.state.query+'');
if (query !== '' && this.props.suggestionMatch !== 'disabled' && this.props.suggestionMatch !== 'none') {
for (const [key, value] of source) {
if (this.matches(this.normalise(value), query)) {
suggest.set(''+key, value);
}
}
} else {
suggest = this.props.multiple ? new Map(source) : source;
}
if (this.props.multiple) {
const values = this.isValueAnObject() ? Object.keys(this.props.value) : this.props.value||[];
for (const k of values) {
suggest.delete(''+k);
}
}
return suggest;
}
matches(value, query) {
const { suggestionMatch } = this.props;
if (suggestionMatch === 'start') {
return value.startsWith(query);
} else if (suggestionMatch === 'anywhere') {
return value.includes(query);
} else if (suggestionMatch === 'word') {
const re = new RegExp(`\\b${query}`, 'g');
return re.test(value);
}
return false;
}
source() {
const src = this.props.source;
if (this._cachedSource != src) {
this._cachedSource = src;
const valueKey = this.props.valueKey || 'value';
const labelKey = this.props.labelKey || 'label';
if (src.hasOwnProperty('length')) {
this._source = new Map(src.map(item => {
if (Array.isArray(item)) {
return [''+item[0], item[1]];
} else if (typeof item != 'object') {
return [''+item, item];
} else {
return [''+item[valueKey], item[labelKey]];
}
}));
} else {
this._source = new Map(Object.keys(src).map(key => [`${key}`, src[key]]));
}
}
return this._source;
}
select = (event, target) => {
events.pauseEvent(event);
let newValue = target === void 0 ? event.target.id : target;
if (this.isValueAnObject()) {
newValue = { ...(this.props.value||{}), [newValue]: true };
} else if (this.props.multiple) {
newValue = [ ...(this.props.value||[]), newValue ];
}
this.handleChange(newValue, event);
}
unselect(key, event) {
if (!this.props.disabled) {
let newValue;
if (this.isValueAnObject()) {
newValue = { ...this.props.value };
delete newValue[key];
} else if (this.props.multiple) {
newValue = (this.props.value||[]).filter(v => v != key);
}
this.handleChange(newValue, event);
}
}
isValueAnObject() {
return this.props.value && !Array.isArray(this.props.value) && typeof this.props.value === 'object';
}
mapToObject(array) {
return array.reduce((obj, k) => {
obj[k] = true;
return obj;
}, {});
}
renderSelected() {
if (this.props.multiple) {
const values = this.isValueAnObject() ? Object.keys(this.props.value) : this.props.value||[];
const source = this.source();
const selectedItems = values.map(key => (