diff --git a/README.md b/README.md index 8b5a757..08e419b 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,15 @@ import { SpecificQueryModeResponse, FetchType } from 'react-dadata-box'; type AbstractResponseType = CountryResponseType | PartyResponseType | BankResponseType | EmailResponseType | FioResponseType | FmsUnitResponseType // type of 'suggestion' wiil be infered automaticaliy from fetch type and it will be SpecificQueryModeResponse[] -onChange ? : (suggestion: SpecificQueryModeResponse) => void; +onChange?: (suggestion: SpecificQueryModeResponse) => void; +``` +___ +#### onBlur ![](https://img.shields.io/badge/optional-green) +handler called on blur event of react-dadata-box input (also at closing list after user selection or outside click) +this handler extended by second argument that contains current query text + +```typescript +onBlur?: (event: SyntheticEvent, query: string) => void; ``` ___ #### onIdleOut ![](https://img.shields.io/badge/optional-green) @@ -259,7 +267,8 @@ exported bulit-in types accordingly to type parameter ___ #### forceOpenList ![](https://img.shields.io/badge/optional-green) -this property force the suggestions list will be permanently open (usually needed for debug) +this property force the already opened suggestions list will be permanently open, prevents close list by Blur event or user select value Action (it also is way may to debug suggestions) +Attention! if query is empty and not declared any customActions - list is truthy empty of elements (empty list couldn't be open even if forceOpenList === true) ```typescript forceOpenList?: boolean; ``` diff --git a/README.ru.md b/README.ru.md index af63dc2..f4f83b5 100644 --- a/README.ru.md +++ b/README.ru.md @@ -158,6 +158,14 @@ type AbstractResponseType = CountryResponseType | PartyResponseType | BankRespon onChange?: (suggestion: SpecificQueryModeResponse) => void; ``` ___ +#### onBlur ![](https://img.shields.io/badge/optional-green) +обработчик события вызываемого при потере фокуса уомпонентом react-dadata-box (в т.ч. скрытие списка после пользовательского выбора и/или клика за пределы раскрытого списка) +стандартный обработчик расширен вторым аргументом в который передается текущее занчение query (строки поиска, занчения в поле ввода) + +```typescript +onBlur?: (event: SyntheticEvent, query: string) => void; +``` +___ #### onIdleOut ![](https://img.shields.io/badge/optional-green) обработчик события вызываемого в случаях когда по текущему запросу пользователя сервис не возвращает подсказок, принимает в качестве аргумента текущую строку запроса. ```typescript @@ -262,7 +270,8 @@ import { SpecificQueryModeResponse } from 'react-dadata-box'; | 'fms_unit' | FmsUnitResponseType | ___ #### forceOpenList ![](https://img.shields.io/badge/optional-green) -свойство определяющее принудительное раскрытие списка подсказок (преимущественно необходимо для удобства отладки/разработки н/п customActions) +свойство определяющее поведение при котором раскрытый список саджестов не закрывается по событиям Blur и по действию выбора значения из списка (может использоваться для удобства отладки/разработки н/п customActions) +Внимание! если query пустое и не определено ни одного customActions - список истино пуст (истино пустой список не раскрывается даже при forceOpenList === true) ```typescript forceOpenList?: boolean; ``` diff --git a/dist/index.js b/dist/index.js index 666a346..1aa3c7f 100755 --- a/dist/index.js +++ b/dist/index.js @@ -195,11 +195,6 @@ var renderCustomActions = function renderCustomActions(_ref2, muteEventHandler, var actions = customActions instanceof Function ? customActions(suggestions) : customActions; - // ToDo: @remove in >= 1.3.5 - if (!(customActions instanceof Function)) { - console.warn('\x1b[31m' + '\n \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\n \u2502 react-dadata-box@1.3.4 \u2502\n \u2502 *** DEPRECATION WARNING **** \u2502\n \u2502 at v1.3.5 will be deprecated variant to place customActions \u2502\n \u2502 as React.Element it must be placed only as function that \u2502\n \u2502 returns React.Element and take suggestions as argument \u2502\n \u2502 see more in project README.md \u2502\n \u2502 https://github.com/orbita-center/react-dadata-box \u2502\n \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n ' + '\x1b[0m'); - } - actions = actions instanceof Array ? actions : actions ? [actions] : false; return actions && actions.length ? [React.createElement('hr', { key: 'custom-actions-line', className: 'actions-delimiter' })].concat(actions.map(function (node) { @@ -387,8 +382,13 @@ var _initialiseProps = function _initialiseProps() { } }; - this.onInputBlur = function () { + this.onInputBlur = function (e) { _this2.setState({ inputFocused: false }); + if (e && _this2.props.onBlur) { + e.preventDefault(); + e.stopPropagation(); + _this2.props.onBlur(e, _this2.state.query); + } }; this.debounce = function (func) { @@ -552,6 +552,7 @@ ReactDaDataBox.propTypes = { debounce: _propTypes2.default.number, forceOpenList: _propTypes2.default.bool, onChange: _propTypes2.default.func, + onBlur: _propTypes2.default.func, onIdleOut: _propTypes2.default.func, payloadModifier: _propTypes2.default.oneOfType([_propTypes2.default.object, _propTypes2.default.shape, _propTypes2.default.func]), placeholder: _propTypes2.default.string, diff --git a/index.d.ts b/index.d.ts index 84d0da8..bcd9117 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,6 @@ // tslint:disable:max-line-length import * as React from 'react'; -import { ReactElement } from 'react'; +import {ReactElement, SyntheticEvent} from 'react'; // types formed by description's from: // - https://dadata.ru/api/find-address/#response @@ -380,6 +380,8 @@ interface BasePayload { count?: number; } + + /** * @typedef { BasePayload } BasePayload * @property { string } className - [required] component CSS class name (default: native generated style with 'react-dadata__' prefix) @@ -404,32 +406,40 @@ interface BaseInputProps { } /** - * @typedef { SpecificModeByTypeProps } SpecificModeByTypeProps +* @typedef { CustomStylesObject } CustomStylesObject + * @property {React.CSSProperties | string} react-dadata__custom-action - [optional] classname or CSSProperties for Custom Action component (in Suggestions List) + * @property {React.CSSProperties | string} react-dadata__suggestion - [optional] CSS classname or CSSProperties for Suggestion Item component (in Suggestions List) + * @property {React.CSSProperties | string} react-dadata__suggestion-note - [optional] CSS classname or CSSProperties for Note component (of Suggestion Item) + * @property {React.CSSProperties | string} react-dadata__suggestions - [optional] CSS classname or CSSProperties for Suggestions List component +**/ + +/** + * @typedef { BaseProps } basic collect of props with predefined types independent of query mode type + * @property { boolean } allowClear - [optional] show/hide clear fieldd control * @property { boolean } autocomplete - [optional] property translated to native input tag; - * @property { customInput } customInput - [optional] function that fires with one argument { BaseInputProps } - * @property { customActions } customActions - [optional] adding custom action to base suggestions dropdown list - * @property { boolean | object } customEndpoint - [optional] optional uri to fetch suggestion's (to proxy scenario or local hosted DaData service), may be string: full or relative uri or object with 'host' and/or 'api' property * @property { boolean } city - [optional] optional to city-mode * @property { string } className - [optional] additional classname + * @property { CustomStylesObject } customStyles - [optional] customize styling of individual specific components (parts) * @property { number } count - [optional] single query limit (default: 10) - * @property { onChange } onChange - [optional] - onChange handler - * @property { onIdleOut } onIdleOut - [optional] - onIdleOut handler, fires with one argument - current query, when by this query has not returned suggestions - * @property { object | function } payloadModifier - [optional] - object to patch payload or function returned payload object to send, that has auto generated payload object as argument - * @property { debounce } onChange - [optional] - debounce to onChange handler (default: 350 ms). - * @property { string } placeholder - [optional] - placeholder - * @property { string } query - [optional] - query for search - * @property { React.CSSProperties } style - [optional] - custom styling - * @property { string } token - [required] - API authorization token - * @property { FetchType } type - [optional] specifics fetching by data type groups - * @property { boolean } allowClear - [optional] show/hide clear fieldd control + * @property { boolean | object } customEndpoint - [optional] optional uri to fetch suggestion's (to proxy scenario or local hosted DaData service), may be string: full or relative uri or object with 'host' and/or 'api' property + * @property { customInput } customInput - [optional] function that fires with one argument { BaseInputProps } + * @property { debounce } debounce - [optional] debounce to onChange handler (default: 350 ms). + * @property { boolean } forceOpenList - [optional] prevent to close opened suggestions list (by Blur event or user select value Action); it also is way may to debug suggestions + * @property { onIdleOut } onIdleOut - [optional] onIdleOut handler, fires with one argument - current query, when by this query has not returned suggestions + * @property { object | function } payloadModifier - [optional] object to patch payload or function returned payload object to send, that has auto generated payload object as argument + * @property { string } placeholder - [optional] placeholder + * @property { string } query - [optional] query for search * @property { boolean } showNote - [optional] show/hide note at suggestions list * @property { boolean } silentQuery - [optional] initial query that not showed on input but determine suggestion list that showed at first + * @property { React.CSSProperties } style - [optional] custom styling + * @property { string } token - [required] API authorization token + * @property { FetchType } type - [optional] specifics fetching by data type groups + * + * @property { customActions } customActions - [optional] adding custom action to base suggestions dropdown list + * @property { onChange } onChange - [optional] onChange handler * @property { function } silentInit - [optional] function that may be used to autoselect from preventive fetched (by placed query or silentQuery), it called with list of fetched suggestions, and if it will return index, appropriate suggestion will be selected (all handlers fire as at user select) */ -/** - * @typedef { BaseProps } basic collect of props with predefined types independent of query mode type - */ type BaseProps = { allowClear?: boolean; autocomplete?: 'on' | 'off'; @@ -446,6 +456,7 @@ type BaseProps = { customInput?: (props: BaseInputProps) => React.ReactNode; debounce?: number; forceOpenList?: boolean; + onBlur?: (event: SyntheticEvent, query: string) => void; onIdleOut?: (query: string) => void; payloadModifier?: object | ((payload: BasePayload) => BasePayload & object); placeholder?: string; diff --git a/package.json b/package.json index 3aa7b76..7044500 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-dadata-box", - "version": "1.3.7", + "version": "1.3.8", "description": "Компонент для работы с DaData", "main": "dist/index.js", "style": "dist/index.css", @@ -26,7 +26,7 @@ "react-dom": "^17.0.2" }, "dependencies": { - "react-highlight-words": "^0.12.0" + "react-highlight-words": "^0.18.0" }, "devDependencies": { "@types/react": "^18.0.17", diff --git a/src/index.js b/src/index.js index c1b99c5..b44710b 100755 --- a/src/index.js +++ b/src/index.js @@ -129,25 +129,6 @@ const renderCustomActions = ({ customActions, customStyles, suggestions }, muteE let actions = customActions instanceof Function ? customActions(suggestions) : customActions; - // ToDo: @remove in >= 1.3.5 - if (!(customActions instanceof Function)) { - console.warn( - '\x1b[31m' + - ` - ╭────────────────────────────────────────────────────────────────╮ - │ react-dadata-box@1.3.4 │ - │ *** DEPRECATION WARNING **** │ - │ at v1.3.5 will be deprecated variant to place customActions │ - │ as React.Element it must be placed only as function that │ - │ returns React.Element and take suggestions as argument │ - │ see more in project README.md │ - │ https://github.com/orbita-center/react-dadata-box │ - ╰────────────────────────────────────────────────────────────────╯ - ` + - '\x1b[0m' - ); - } - actions = actions instanceof Array ? actions : actions ? [actions] : false; return actions && actions.length @@ -261,8 +242,13 @@ class ReactDaDataBox extends React.PureComponent { } }; - onInputBlur = () => { + onInputBlur = (e) => { this.setState({ inputFocused: false }); + if (e && this.props.onBlur) { + e.preventDefault(); + e.stopPropagation(); + this.props.onBlur(e, this.state.query); + } }; debounce = (func, cooldown = 350) => { @@ -478,6 +464,7 @@ ReactDaDataBox.propTypes = { debounce: PropTypes.number, forceOpenList: PropTypes.bool, onChange: PropTypes.func, + onBlur: PropTypes.func, onIdleOut: PropTypes.func, payloadModifier: PropTypes.oneOfType([PropTypes.object, PropTypes.shape, PropTypes.func]), placeholder: PropTypes.string,