react-dadata
Version:
React-компонент для подсказок адресов, организаций и банков с помощью сервиса DaData.ru
322 lines (321 loc) • 15.3 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import { debounce } from 'debounce';
import { nanoid } from 'nanoid';
import React from 'react';
import shallowEqual from 'shallowequal';
import { DefaultHttpCache, HttpCache } from './http-cache';
import { makeRequest } from './request';
var BaseSuggestions = /** @class */ (function (_super) {
__extends(BaseSuggestions, _super);
function BaseSuggestions(props) {
var _this = _super.call(this, props) || this;
/**
* URL для загрузки подсказок, переопределяется в конкретном компоненте
*/
_this.loadSuggestionsUrl = '';
_this.dontPerformBlurHandler = false;
_this.getSuggestionsUrl = function () {
var url = _this.props.url;
return url || _this.loadSuggestionsUrl;
};
_this.setupDebounce = function (delay) {
if (typeof delay === 'number' && delay > 0) {
_this.fetchSuggestions = debounce(_this.performFetchSuggestions, delay);
}
else {
_this.fetchSuggestions = _this.performFetchSuggestions;
}
};
_this.fetchSuggestions = function () {
//
};
_this.handleInputFocus = function (event) {
_this.setState({ isFocused: true });
var suggestions = _this.state.suggestions;
if (suggestions.length === 0) {
_this.fetchSuggestions();
}
var inputProps = _this.props.inputProps;
if (inputProps === null || inputProps === void 0 ? void 0 : inputProps.onFocus) {
inputProps.onFocus(event);
}
};
_this.handleInputBlur = function (event) {
var _a = _this.state, suggestions = _a.suggestions, suggestionIndex = _a.suggestionIndex;
var _b = _this.props, selectOnBlur = _b.selectOnBlur, inputProps = _b.inputProps;
_this.setState({ isFocused: false });
if (suggestions.length === 0) {
_this.fetchSuggestions();
}
if (selectOnBlur && !_this.dontPerformBlurHandler) {
if (suggestions.length > 0) {
var suggestionIndexToSelect = suggestionIndex >= 0 && suggestionIndex < suggestions.length ? suggestionIndex : 0;
_this.selectSuggestion(suggestionIndexToSelect, true);
}
}
_this.dontPerformBlurHandler = false;
if (inputProps === null || inputProps === void 0 ? void 0 : inputProps.onBlur) {
inputProps.onBlur(event);
}
};
_this.handleInputChange = function (event) {
var value = event.target.value;
var inputProps = _this.props.inputProps;
if (_this.didMount) {
_this.setState({ query: value, inputQuery: value, displaySuggestions: !!value }, function () {
_this.fetchSuggestions();
});
}
if (inputProps === null || inputProps === void 0 ? void 0 : inputProps.onChange) {
inputProps.onChange(event);
}
};
_this.handleInputKeyDown = function (event) {
_this.handleKeyboard(event);
var inputProps = _this.props.inputProps;
if (inputProps === null || inputProps === void 0 ? void 0 : inputProps.onKeyDown) {
inputProps.onKeyDown(event);
}
};
_this.handleInputKeyPress = function (event) {
_this.handleKeyboard(event);
var inputProps = _this.props.inputProps;
if (inputProps === null || inputProps === void 0 ? void 0 : inputProps.onKeyPress) {
inputProps.onKeyPress(event);
}
};
_this.handleKeyboard = function (event) {
var _a = _this.state, suggestions = _a.suggestions, suggestionIndex = _a.suggestionIndex, inputQuery = _a.inputQuery;
if (event.key === 'ArrowDown') {
// Arrow down
event.preventDefault();
if (suggestionIndex < suggestions.length - 1) {
var newSuggestionIndex = suggestionIndex + 1;
var newInputQuery = suggestions[newSuggestionIndex].value;
if (_this.didMount) {
_this.setState({
suggestionIndex: newSuggestionIndex,
query: newInputQuery,
});
}
}
}
else if (event.key === 'ArrowUp') {
// Arrow up
event.preventDefault();
if (suggestionIndex >= 0) {
var newSuggestionIndex = suggestionIndex - 1;
var newInputQuery = newSuggestionIndex === -1 ? inputQuery : suggestions[newSuggestionIndex].value;
if (_this.didMount) {
_this.setState({
suggestionIndex: newSuggestionIndex,
query: newInputQuery,
});
}
}
}
else if (event.key === 'Enter') {
// Enter
event.preventDefault();
if (suggestionIndex >= 0) {
_this.selectSuggestion(suggestionIndex);
}
}
};
_this.performFetchSuggestions = function () {
var _a = _this.props, minChars = _a.minChars, token = _a.token;
var query = _this.state.query;
// Проверяем на минимальное количество символов для отправки
if (typeof minChars === 'number' && minChars > 0 && query.length < minChars) {
_this.setState({ suggestions: [], suggestionIndex: -1 });
return;
}
makeRequest('POST', _this.getSuggestionsUrl(), {
headers: {
Accept: 'application/json',
Authorization: "Token ".concat(token),
'Content-Type': 'application/json',
},
json: _this.getLoadSuggestionsData(),
}, _this.httpCache, function (suggestions) {
if (_this.didMount) {
_this.setState({ suggestions: suggestions, suggestionIndex: -1 });
}
});
};
_this.onSuggestionClick = function (index, event) {
event.stopPropagation();
_this.selectSuggestion(index);
};
_this.selectSuggestion = function (index, isSilent) {
if (isSilent === void 0) { isSilent = false; }
var suggestions = _this.state.suggestions;
var _a = _this.props, selectOnBlur = _a.selectOnBlur, onChange = _a.onChange;
if (suggestions.length >= index - 1) {
var suggestion = suggestions[index];
if (selectOnBlur) {
_this.dontPerformBlurHandler = true;
}
_this.setState({
query: suggestion.value,
inputQuery: suggestion.value,
displaySuggestions: false,
}, function () {
if (!isSilent) {
_this.fetchSuggestions();
setTimeout(function () { return _this.setCursorToEnd(_this.textInput); });
}
});
if (onChange) {
onChange(suggestion);
}
}
};
_this.setCursorToEnd = function (element) {
if (element) {
var valueLength = element.value.length;
if (element.selectionStart || element.selectionStart === 0) {
element.selectionStart = valueLength;
element.selectionEnd = valueLength;
element.focus();
}
}
};
_this.getHighlightWords = function () {
var inputQuery = _this.state.inputQuery;
var wordsToPass = ['г', 'респ', 'ул', 'р-н', 'село', 'деревня', 'поселок', 'пр-д', 'пл', 'к', 'кв', 'обл', 'д'];
var words = inputQuery.replace(',', '').split(' ');
words = words.filter(function (word) {
return wordsToPass.indexOf(word) < 0;
});
return words;
};
/**
* Функция, которая вернет уникальный key для списка React
* @param suggestion
*/
_this.getSuggestionKey = function (suggestion) { return suggestion.value; };
_this.focus = function () {
if (_this.textInput) {
_this.textInput.focus();
}
};
_this.setInputValue = function (value) {
_this.setState({ query: value || '', inputQuery: value || '' });
};
_this.didMount = false;
var _a = _this.props, defaultQuery = _a.defaultQuery, value = _a.value, delay = _a.delay;
var valueQuery = value ? value.value : undefined;
_this.setupDebounce(delay);
_this.state = {
query: defaultQuery || valueQuery || '',
inputQuery: defaultQuery || valueQuery || '',
isFocused: false,
displaySuggestions: true,
suggestions: [],
suggestionIndex: -1,
};
return _this;
}
BaseSuggestions.prototype.componentDidMount = function () {
this.didMount = true;
};
BaseSuggestions.prototype.componentDidUpdate = function (prevProps) {
var _a = this.props, value = _a.value, delay = _a.delay;
var _b = this.state, query = _b.query, inputQuery = _b.inputQuery;
if (!shallowEqual(prevProps.value, value)) {
var newQuery = value ? value.value : '';
if (query !== newQuery || inputQuery !== newQuery) {
this.setState({ query: newQuery, inputQuery: newQuery });
}
}
if (delay !== prevProps.delay) {
this.setupDebounce(delay);
}
};
BaseSuggestions.prototype.componentWillUnmount = function () {
this.didMount = false;
};
Object.defineProperty(BaseSuggestions.prototype, "uid", {
get: function () {
if (this.props.uid) {
return this.props.uid;
}
if (!this._uid) {
this._uid = nanoid();
}
return this._uid;
},
enumerable: false,
configurable: true
});
Object.defineProperty(BaseSuggestions.prototype, "httpCache", {
get: function () {
var _a = this.props, cacheProp = _a.httpCache, ttl = _a.httpCacheTtl;
if (!cacheProp) {
return null;
}
if (cacheProp instanceof HttpCache) {
return cacheProp;
}
var cache = DefaultHttpCache.shared;
if (typeof ttl === 'number') {
cache.ttl = ttl;
}
return cache;
},
enumerable: false,
configurable: true
});
BaseSuggestions.prototype.render = function () {
var _this = this;
var _a = this.props, inputProps = _a.inputProps, hintText = _a.hintText, containerClassName = _a.containerClassName, hintClassName = _a.hintClassName, suggestionsClassName = _a.suggestionsClassName, suggestionClassName = _a.suggestionClassName, currentSuggestionClassName = _a.currentSuggestionClassName, customInput = _a.customInput, children = _a.children;
var _b = this.state, query = _b.query, isFocused = _b.isFocused, suggestions = _b.suggestions, suggestionIndex = _b.suggestionIndex, displaySuggestions = _b.displaySuggestions;
var Component = typeof customInput !== 'undefined' ? customInput : 'input';
var optionsExpanded = isFocused && suggestions && displaySuggestions && suggestions.length > 0;
return (React.createElement("div", { role: "combobox", "aria-expanded": optionsExpanded ? 'true' : 'false', "aria-owns": this.uid, "aria-controls": this.uid, "aria-haspopup": "listbox", className: containerClassName || 'react-dadata react-dadata__container' },
React.createElement("div", null,
React.createElement(Component, __assign({ autoComplete: "off", className: "react-dadata__input" }, inputProps, { value: query, ref: function (input) {
_this.textInput = input;
}, onChange: this.handleInputChange, onKeyPress: this.handleInputKeyPress, onKeyDown: this.handleInputKeyDown, onFocus: this.handleInputFocus, onBlur: this.handleInputBlur }))),
optionsExpanded && (React.createElement("ul", { id: this.uid, "aria-expanded": true,
// biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: <explanation>
role: "listbox", className: suggestionsClassName || 'react-dadata__suggestions' },
typeof hintText !== 'undefined' && (React.createElement("div", { className: hintClassName || 'react-dadata__suggestion-note' }, hintText)),
suggestions.map(function (suggestion, index) {
var suggestionClass = suggestionClassName || 'react-dadata__suggestion';
if (index === suggestionIndex) {
suggestionClass += " ".concat(currentSuggestionClassName || 'react-dadata__suggestion--current');
}
return (React.createElement("button", { role: "option", type: "button", "aria-selected": index === suggestionIndex ? 'true' : 'false', key: _this.getSuggestionKey(suggestion), onMouseDown: _this.onSuggestionClick.bind(_this, index), className: suggestionClass }, _this.renderOption(suggestion)));
}))),
children));
};
return BaseSuggestions;
}(React.PureComponent));
export { BaseSuggestions };