@hh.ru/magritte-ui-suggest 3.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Suggest.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { ReactElement, ForwardedRef } from 'react';
2
+ import { SuggestProps, SuggestInputComponent } from './types';
3
+ declare const Suggest: (<K, T extends SuggestInputComponent>(props: SuggestProps<K, T> & {
4
+ ref?: ForwardedRef<HTMLElement> | undefined;
5
+ }) => ReactElement | null) & {
6
+ displayName: string;
7
+ };
8
+ export { Suggest };
package/Suggest.js ADDED
@@ -0,0 +1,115 @@
1
+ import './index.css';
2
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
+ import { forwardRef, useState, useRef, useCallback, useEffect } from 'react';
4
+ import { useMultipleRefs } from '@hh.ru/magritte-common-use-multiple-refs';
5
+ import { useBreakpoint } from '@hh.ru/magritte-ui-breakpoint';
6
+ import { SuggestPicker } from './SuggestPicker.js';
7
+ import { useSuggestKeyboardNav } from './useSuggestKeyboardNav.js';
8
+ import { s as styles } from './sectionRenderers-859b79db.js';
9
+ import 'classnames';
10
+ import '@hh.ru/magritte-ui-bottom-sheet';
11
+ import '@hh.ru/magritte-ui-drop';
12
+ import '@hh.ru/magritte-ui-navigation-bar';
13
+ import '@hh.ru/magritte-common-keyboard';
14
+ import '@hh.ru/magritte-ui-card';
15
+ import '@hh.ru/magritte-ui-cell';
16
+ import '@hh.ru/magritte-ui-chips';
17
+ import '@hh.ru/magritte-ui-typography';
18
+
19
+ const SuggestComponent = function ({ dataProvider, input, inputValue, onSelectValidator = () => true, navigationBarProps, maxHeight = 460, itemsGap = 4, dropHost, onBlur, }, ref) {
20
+ const { component: InputComponent, props: inputProps } = input;
21
+ const [inputValueState, setInputValueState] = useState(inputProps.value || '');
22
+ const { onChange: onInputChange, onFocus: onInputFocus, onKeyDown: onInputKeyDown } = inputProps;
23
+ const [requestResult, setRequestResult] = useState(null);
24
+ const [valueSelectedFromSuggest, setValueSelectedFromSuggest] = useState(false);
25
+ const inputRef = useRef(null);
26
+ const wrapperInputRef = useRef(null);
27
+ const suggestContainerRef = useRef(null);
28
+ const inputContainerRef = useRef(null);
29
+ const inputRefMulti = useMultipleRefs(inputRef, ref);
30
+ const onBlurRef = useRef(onBlur);
31
+ onBlurRef.current = onBlur; // update actual callback
32
+ const { isMobile } = useBreakpoint();
33
+ const currentRequestRef = useRef();
34
+ const actualInputValue = inputValue === undefined ? inputValueState : inputValue;
35
+ const { focused, onKeyDown, onFocus, onBottomSheetClose } = useSuggestKeyboardNav(suggestContainerRef, inputContainerRef, setValueSelectedFromSuggest, actualInputValue, inputRef, onBlurRef);
36
+ const onChangeHandler = useCallback((value) => {
37
+ if (inputValue === undefined) {
38
+ setInputValueState(value);
39
+ }
40
+ onInputChange?.(value);
41
+ }, [inputValue, setInputValueState, onInputChange]);
42
+ const requestData = useCallback((query) => {
43
+ currentRequestRef.current?.cancel();
44
+ const request = dataProvider(query);
45
+ request.then((result) => {
46
+ setRequestResult({ query, result });
47
+ }, (error) => {
48
+ setRequestResult(null);
49
+ if (error) {
50
+ throw error;
51
+ }
52
+ });
53
+ currentRequestRef.current = request;
54
+ }, [dataProvider]);
55
+ useEffect(() => {
56
+ if (valueSelectedFromSuggest !== false && requestResult?.query !== actualInputValue) {
57
+ setRequestResult(null);
58
+ }
59
+ if (valueSelectedFromSuggest !== actualInputValue.trim()) {
60
+ setValueSelectedFromSuggest(false);
61
+ if (focused) {
62
+ requestData(actualInputValue);
63
+ }
64
+ }
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, [actualInputValue, requestData, focused]);
67
+ const onFocusHandler = useCallback((event) => {
68
+ setValueSelectedFromSuggest(false);
69
+ if (actualInputValue !== requestResult?.query) {
70
+ setRequestResult(null);
71
+ requestData(actualInputValue);
72
+ }
73
+ onInputFocus?.(event);
74
+ onFocus(event);
75
+ }, [actualInputValue, requestResult?.query, onInputFocus, onFocus, requestData]);
76
+ const onValueSelectHandler = useCallback((value, data) => {
77
+ if (!onSelectValidator(value, data)) {
78
+ return;
79
+ }
80
+ if (!isMobile) {
81
+ inputRef.current?.focus();
82
+ }
83
+ onChangeHandler(value);
84
+ setValueSelectedFromSuggest(value);
85
+ }, [onSelectValidator, isMobile, onChangeHandler]);
86
+ const onKeyDownHandler = useCallback((event) => {
87
+ onKeyDown(event);
88
+ onInputKeyDown?.(event);
89
+ }, [onInputKeyDown, onKeyDown]);
90
+ const actualInputProps = {
91
+ ...inputProps,
92
+ value: actualInputValue,
93
+ onFocus: onFocusHandler,
94
+ onChange: onChangeHandler,
95
+ onKeyDown: onKeyDownHandler,
96
+ ref: inputRefMulti,
97
+ wrapperRef: wrapperInputRef,
98
+ autoComplete: 'off',
99
+ };
100
+ return (jsxs(Fragment, { children: [jsx("div", { ref: inputContainerRef, className: styles.suggestInputContainer, children: jsx(InputComponent, { ...actualInputProps }) }), jsx(SuggestPicker, { data: requestResult?.result, activatorRef: wrapperInputRef, inputRef: inputRef, inputFocused: focused, dropHost: dropHost, currentInputValueSelectedFromSuggest: valueSelectedFromSuggest !== false, onValueSelect: onValueSelectHandler, maxHeight: maxHeight, navigationBarProps: navigationBarProps, suggestContainerRef: suggestContainerRef, itemsGap: itemsGap, onBottomSheetClose: onBottomSheetClose, inputValue: actualInputValue, input: {
101
+ component: InputComponent,
102
+ props: {
103
+ ...input.props,
104
+ onFocus: onFocusHandler,
105
+ onChange: onChangeHandler,
106
+ value: actualInputValue,
107
+ onKeyDown: onKeyDownHandler,
108
+ },
109
+ } })] }));
110
+ };
111
+ const Suggest = forwardRef(SuggestComponent);
112
+ Suggest.displayName = 'Suggest';
113
+
114
+ export { Suggest };
115
+ //# sourceMappingURL=Suggest.js.map
package/Suggest.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Suggest.js","sources":["../src/Suggest.tsx"],"sourcesContent":["import {\n FocusEventHandler,\n ReactElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n KeyboardEventHandler,\n forwardRef,\n ForwardedRef,\n} from 'react';\n\nimport { useMultipleRefs } from '@hh.ru/magritte-common-use-multiple-refs';\nimport { useBreakpoint } from '@hh.ru/magritte-ui-breakpoint';\nimport { SuggestPicker } from '@hh.ru/magritte-ui-suggest/SuggestPicker';\nimport {\n DataProviderRequest,\n DataProviderResult,\n SuggestProps,\n SuggestInputComponent,\n InputProp,\n ControlledInputProps,\n NativeFocusEventHandler,\n} from '@hh.ru/magritte-ui-suggest/types';\nimport { useSuggestKeyboardNav } from '@hh.ru/magritte-ui-suggest/useSuggestKeyboardNav';\n\nimport styles from './suggest.less';\n\nconst SuggestComponent = function <K, T extends SuggestInputComponent>(\n {\n dataProvider,\n input,\n inputValue,\n onSelectValidator = () => true,\n navigationBarProps,\n maxHeight = 460,\n itemsGap = 4,\n dropHost,\n onBlur,\n }: SuggestProps<K, T>,\n ref: ForwardedRef<HTMLElement>\n): ReactElement | null {\n const { component: InputComponent, props: inputProps } = input;\n const [inputValueState, setInputValueState] = useState(inputProps.value || '');\n const { onChange: onInputChange, onFocus: onInputFocus, onKeyDown: onInputKeyDown } = inputProps;\n const [requestResult, setRequestResult] = useState<{ query: string; result: DataProviderResult<K> } | null>(null);\n const [valueSelectedFromSuggest, setValueSelectedFromSuggest] = useState<string | false>(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const wrapperInputRef = useRef<HTMLInputElement>(null);\n const suggestContainerRef = useRef<HTMLElement>(null);\n const inputContainerRef = useRef<HTMLDivElement>(null);\n const inputRefMulti = useMultipleRefs(inputRef, ref);\n const onBlurRef = useRef<NativeFocusEventHandler | undefined>(onBlur);\n onBlurRef.current = onBlur; // update actual callback\n const { isMobile } = useBreakpoint();\n\n const currentRequestRef = useRef<DataProviderRequest>();\n const actualInputValue = inputValue === undefined ? inputValueState : inputValue;\n\n const { focused, onKeyDown, onFocus, onBottomSheetClose } = useSuggestKeyboardNav(\n suggestContainerRef,\n inputContainerRef,\n setValueSelectedFromSuggest,\n actualInputValue,\n inputRef,\n onBlurRef\n );\n\n const onChangeHandler = useCallback(\n (value: string) => {\n if (inputValue === undefined) {\n setInputValueState(value);\n }\n\n onInputChange?.(value);\n },\n [inputValue, setInputValueState, onInputChange]\n );\n\n const requestData = useCallback(\n (query: string) => {\n currentRequestRef.current?.cancel();\n const request = dataProvider(query);\n request.then(\n (result) => {\n setRequestResult({ query, result });\n },\n (error) => {\n setRequestResult(null);\n if (error) {\n throw error;\n }\n }\n );\n currentRequestRef.current = request;\n },\n [dataProvider]\n );\n\n useEffect(() => {\n if (valueSelectedFromSuggest !== false && requestResult?.query !== actualInputValue) {\n setRequestResult(null);\n }\n if (valueSelectedFromSuggest !== actualInputValue.trim()) {\n setValueSelectedFromSuggest(false);\n if (focused) {\n requestData(actualInputValue);\n }\n }\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [actualInputValue, requestData, focused]);\n\n const onFocusHandler = useCallback<FocusEventHandler<HTMLElement>>(\n (event) => {\n setValueSelectedFromSuggest(false);\n if (actualInputValue !== requestResult?.query) {\n setRequestResult(null);\n requestData(actualInputValue);\n }\n\n onInputFocus?.(event);\n onFocus(event);\n },\n [actualInputValue, requestResult?.query, onInputFocus, onFocus, requestData]\n );\n\n const onValueSelectHandler = useCallback(\n (value: string, data: K | undefined) => {\n if (!onSelectValidator(value, data)) {\n return;\n }\n if (!isMobile) {\n inputRef.current?.focus();\n }\n onChangeHandler(value);\n setValueSelectedFromSuggest(value);\n },\n [onSelectValidator, isMobile, onChangeHandler]\n );\n\n const onKeyDownHandler = useCallback<KeyboardEventHandler<HTMLInputElement>>(\n (event) => {\n onKeyDown(event);\n onInputKeyDown?.(event);\n },\n [onInputKeyDown, onKeyDown]\n );\n\n const actualInputProps = {\n ...inputProps,\n value: actualInputValue,\n onFocus: onFocusHandler,\n onChange: onChangeHandler,\n onKeyDown: onKeyDownHandler,\n ref: inputRefMulti,\n wrapperRef: wrapperInputRef,\n autoComplete: 'off',\n } as unknown as JSX.IntrinsicAttributes & JSX.LibraryManagedAttributes<T, ControlledInputProps>;\n\n return (\n <>\n <div ref={inputContainerRef} className={styles.suggestInputContainer}>\n <InputComponent {...actualInputProps} />\n </div>\n <SuggestPicker\n data={requestResult?.result}\n activatorRef={wrapperInputRef}\n inputRef={inputRef}\n inputFocused={focused}\n dropHost={dropHost}\n currentInputValueSelectedFromSuggest={valueSelectedFromSuggest !== false}\n onValueSelect={onValueSelectHandler}\n maxHeight={maxHeight}\n navigationBarProps={navigationBarProps}\n suggestContainerRef={suggestContainerRef}\n itemsGap={itemsGap}\n onBottomSheetClose={onBottomSheetClose}\n inputValue={actualInputValue}\n input={\n {\n component: InputComponent,\n props: {\n ...input.props,\n onFocus: onFocusHandler,\n onChange: onChangeHandler,\n value: actualInputValue,\n onKeyDown: onKeyDownHandler,\n },\n } as InputProp<T>\n }\n />\n </>\n );\n};\n\nconst Suggest = forwardRef(SuggestComponent) as (<K, T extends SuggestInputComponent>(\n props: SuggestProps<K, T> & { ref?: ForwardedRef<HTMLElement> }\n) => ReactElement | null) & { displayName: string };\n\nSuggest.displayName = 'Suggest';\n\nexport { Suggest };\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { RefObject, ReactElement } from 'react';
2
+ import { type TokenBaseCoreSpace } from '@hh.ru/magritte-design-tokens/types';
3
+ import { DataProviderResult, SuggestProps, SuggestInputComponent, InputProp } from './types';
4
+ interface SuggestPickerProps<K, P extends SuggestInputComponent = SuggestInputComponent> {
5
+ data: DataProviderResult<K> | null | undefined;
6
+ activatorRef: RefObject<HTMLElement>;
7
+ inputRef: RefObject<HTMLElement>;
8
+ onValueSelect: (value: string, data: K | undefined) => void;
9
+ maxHeight: number;
10
+ navigationBarProps: SuggestProps<unknown, SuggestInputComponent>['navigationBarProps'];
11
+ input: InputProp<P>;
12
+ inputFocused: boolean;
13
+ currentInputValueSelectedFromSuggest: boolean;
14
+ suggestContainerRef: RefObject<HTMLElement>;
15
+ itemsGap: TokenBaseCoreSpace;
16
+ onBottomSheetClose: VoidFunction;
17
+ dropHost?: RefObject<HTMLElement>;
18
+ inputValue: string;
19
+ }
20
+ export declare const SuggestPicker: {
21
+ <K, P extends SuggestInputComponent>({ data, activatorRef, inputRef, onValueSelect, maxHeight, navigationBarProps, input, inputFocused, currentInputValueSelectedFromSuggest, suggestContainerRef, itemsGap, onBottomSheetClose, dropHost, inputValue, }: SuggestPickerProps<K, P>): ReactElement | null;
22
+ displayName: string;
23
+ };
24
+ export {};
@@ -0,0 +1,87 @@
1
+ import './index.css';
2
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
+ import { useState, useRef, useEffect, useMemo } from 'react';
4
+ import classnames from 'classnames';
5
+ import { BottomSheet } from '@hh.ru/magritte-ui-bottom-sheet';
6
+ import { useBreakpoint } from '@hh.ru/magritte-ui-breakpoint';
7
+ import { Drop } from '@hh.ru/magritte-ui-drop';
8
+ import { NavigationBar } from '@hh.ru/magritte-ui-navigation-bar';
9
+ import { r as renderSection, s as styles } from './sectionRenderers-859b79db.js';
10
+ import '@hh.ru/magritte-ui-card';
11
+ import '@hh.ru/magritte-ui-cell';
12
+ import '@hh.ru/magritte-ui-chips';
13
+ import '@hh.ru/magritte-ui-typography';
14
+
15
+ const SuggestPicker = function ({ data, activatorRef, inputRef, onValueSelect, maxHeight, navigationBarProps, input, inputFocused, currentInputValueSelectedFromSuggest, suggestContainerRef, itemsGap, onBottomSheetClose, dropHost, inputValue, }) {
16
+ const { isMobile, breakpoint } = useBreakpoint();
17
+ const [showBottomSheet, setShowBottomSheet] = useState(false);
18
+ const [showDrop, setShowDrop] = useState(true);
19
+ const bottomSheetInputRef = useRef(null);
20
+ const [caretVisible, setCaretVisible] = useState(false);
21
+ useEffect(() => {
22
+ if (inputFocused) {
23
+ setShowBottomSheet(true);
24
+ }
25
+ else if (!isMobile) {
26
+ setShowBottomSheet(false);
27
+ }
28
+ // eslint-disable-next-line react-hooks/exhaustive-deps
29
+ }, [inputFocused, setShowBottomSheet]);
30
+ useEffect(() => {
31
+ if (currentInputValueSelectedFromSuggest) {
32
+ setShowBottomSheet(false);
33
+ if (isMobile) {
34
+ onBottomSheetClose();
35
+ }
36
+ }
37
+ }, [currentInputValueSelectedFromSuggest, onBottomSheetClose, isMobile]);
38
+ // если скрыли дроп из-за скрытия активатора под скроллом, то при следующем изменении данных нужно его показать
39
+ useEffect(() => {
40
+ setShowDrop(true);
41
+ }, [inputValue, inputFocused]);
42
+ const suggestContent = useMemo(() => {
43
+ if (!data) {
44
+ return null;
45
+ }
46
+ return data.reduce((result, section, index) => {
47
+ const renderResult = renderSection(section, index, onValueSelect, breakpoint);
48
+ result.push(...renderResult);
49
+ return result;
50
+ }, []);
51
+ }, [data, onValueSelect, breakpoint]);
52
+ const InputComponent = input.component;
53
+ const processInput = (ref) => (event) => {
54
+ const hasPrintableRepresentation = event.key.replace(/\s/g, '').length === 1;
55
+ if (!hasPrintableRepresentation) {
56
+ return;
57
+ }
58
+ ref.current?.focus();
59
+ const newEvent = new KeyboardEvent('keydown', event.nativeEvent);
60
+ ref.current?.dispatchEvent(newEvent);
61
+ };
62
+ return (jsxs(Fragment, { children: [jsx(BottomSheet, { visible: showBottomSheet, height: "full-screen", keyboardOverlaysContent: false, header: jsx(NavigationBar, { ...navigationBarProps, options: jsx("div", { className: classnames({
63
+ [styles.bottomSheetInputHideCaret]: !caretVisible,
64
+ }), children: jsx(InputComponent, { ...{
65
+ ...input.props,
66
+ invalid: false,
67
+ ref: bottomSheetInputRef,
68
+ autoComplete: 'off',
69
+ } }) }) }), onAppear: () => {
70
+ bottomSheetInputRef?.current?.focus();
71
+ setCaretVisible(true);
72
+ }, onBeforeExit: () => {
73
+ bottomSheetInputRef?.current?.blur();
74
+ setCaretVisible(false);
75
+ }, onClose: () => {
76
+ setShowBottomSheet(false);
77
+ onBottomSheetClose();
78
+ }, ref: suggestContainerRef, allowScrollWhileFocused: true, children: jsx("div", { onKeyDown: processInput(bottomSheetInputRef), className: styles.suggestItemsContainer, style: { gap: itemsGap }, children: suggestContent }) }), jsx(Drop, { direction: "bottom", alignment: "left", visible: suggestContent !== null &&
79
+ suggestContent.length > 0 &&
80
+ !currentInputValueSelectedFromSuggest &&
81
+ inputFocused &&
82
+ showDrop, onClose: () => setShowDrop(false), host: dropHost, activatorRef: activatorRef, role: "status", space: 400, widthEqualToActivator: true, forcePosition: true, arrowNavigation: true, "data-qa": "suggest-drop", ref: suggestContainerRef, children: jsx("div", { onKeyDown: processInput(inputRef), className: styles.suggestItemsContainer, style: { maxHeight }, children: jsx("div", { className: styles.suggestItemsContainerWrapper, style: { gap: itemsGap }, children: suggestContent }) }) })] }));
83
+ };
84
+ SuggestPicker.displayName = 'SuggestPicker';
85
+
86
+ export { SuggestPicker };
87
+ //# sourceMappingURL=SuggestPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SuggestPicker.js","sources":["../src/SuggestPicker.tsx"],"sourcesContent":["import { RefObject, useMemo, useEffect, useRef, useState, ReactElement, KeyboardEventHandler } from 'react';\nimport classnames from 'classnames';\n\nimport { type TokenBaseCoreSpace } from '@hh.ru/magritte-design-tokens/types';\nimport { BottomSheet } from '@hh.ru/magritte-ui-bottom-sheet';\nimport { useBreakpoint } from '@hh.ru/magritte-ui-breakpoint';\nimport { Drop } from '@hh.ru/magritte-ui-drop';\nimport { ControlledInputProps } from '@hh.ru/magritte-ui-input/src';\nimport { NavigationBar } from '@hh.ru/magritte-ui-navigation-bar';\nimport { renderSection } from '@hh.ru/magritte-ui-suggest/sectionRenderers';\nimport { DataProviderResult, SuggestProps, SuggestInputComponent, InputProp } from '@hh.ru/magritte-ui-suggest/types';\n\nimport styles from './suggest.less';\n\ninterface SuggestPickerProps<K, P extends SuggestInputComponent = SuggestInputComponent> {\n data: DataProviderResult<K> | null | undefined;\n activatorRef: RefObject<HTMLElement>;\n inputRef: RefObject<HTMLElement>;\n onValueSelect: (value: string, data: K | undefined) => void;\n maxHeight: number;\n navigationBarProps: SuggestProps<unknown, SuggestInputComponent>['navigationBarProps'];\n input: InputProp<P>;\n inputFocused: boolean;\n currentInputValueSelectedFromSuggest: boolean;\n suggestContainerRef: RefObject<HTMLElement>;\n itemsGap: TokenBaseCoreSpace;\n onBottomSheetClose: VoidFunction;\n dropHost?: RefObject<HTMLElement>;\n inputValue: string;\n}\n\nexport const SuggestPicker = function <K, P extends SuggestInputComponent>({\n data,\n activatorRef,\n inputRef,\n onValueSelect,\n maxHeight,\n navigationBarProps,\n input,\n inputFocused,\n currentInputValueSelectedFromSuggest,\n suggestContainerRef,\n itemsGap,\n onBottomSheetClose,\n dropHost,\n inputValue,\n}: SuggestPickerProps<K, P>): ReactElement | null {\n const { isMobile, breakpoint } = useBreakpoint();\n const [showBottomSheet, setShowBottomSheet] = useState(false);\n const [showDrop, setShowDrop] = useState(true);\n const bottomSheetInputRef = useRef<HTMLInputElement>(null);\n const [caretVisible, setCaretVisible] = useState(false);\n\n useEffect(() => {\n if (inputFocused) {\n setShowBottomSheet(true);\n } else if (!isMobile) {\n setShowBottomSheet(false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [inputFocused, setShowBottomSheet]);\n\n useEffect(() => {\n if (currentInputValueSelectedFromSuggest) {\n setShowBottomSheet(false);\n if (isMobile) {\n onBottomSheetClose();\n }\n }\n }, [currentInputValueSelectedFromSuggest, onBottomSheetClose, isMobile]);\n\n // если скрыли дроп из-за скрытия активатора под скроллом, то при следующем изменении данных нужно его показать\n useEffect(() => {\n setShowDrop(true);\n }, [inputValue, inputFocused]);\n\n const suggestContent = useMemo(() => {\n if (!data) {\n return null;\n }\n\n return data.reduce<Array<JSX.Element>>((result, section, index) => {\n const renderResult = renderSection(section, index, onValueSelect, breakpoint);\n result.push(...renderResult);\n return result;\n }, []);\n }, [data, onValueSelect, breakpoint]);\n\n const InputComponent = input.component;\n const processInput =\n (ref: RefObject<HTMLElement>): KeyboardEventHandler<HTMLElement> =>\n (event) => {\n const hasPrintableRepresentation = event.key.replace(/\\s/g, '').length === 1;\n if (!hasPrintableRepresentation) {\n return;\n }\n ref.current?.focus();\n const newEvent = new KeyboardEvent('keydown', event.nativeEvent);\n ref.current?.dispatchEvent(newEvent);\n };\n\n return (\n <>\n <BottomSheet\n visible={showBottomSheet}\n height=\"full-screen\"\n keyboardOverlaysContent={false}\n header={\n <NavigationBar\n {...navigationBarProps}\n options={\n <div\n className={classnames({\n [styles.bottomSheetInputHideCaret]: !caretVisible,\n })}\n >\n <InputComponent\n {...({\n ...input.props,\n invalid: false,\n ref: bottomSheetInputRef,\n autoComplete: 'off',\n } as unknown as JSX.IntrinsicAttributes &\n JSX.LibraryManagedAttributes<P, ControlledInputProps>)}\n />\n </div>\n }\n />\n }\n onAppear={() => {\n bottomSheetInputRef?.current?.focus();\n setCaretVisible(true);\n }}\n onBeforeExit={() => {\n bottomSheetInputRef?.current?.blur();\n setCaretVisible(false);\n }}\n onClose={() => {\n setShowBottomSheet(false);\n onBottomSheetClose();\n }}\n ref={suggestContainerRef}\n allowScrollWhileFocused\n >\n <div\n onKeyDown={processInput(bottomSheetInputRef)}\n className={styles.suggestItemsContainer}\n style={{ gap: itemsGap }}\n >\n {suggestContent}\n </div>\n </BottomSheet>\n <Drop\n direction=\"bottom\"\n alignment=\"left\"\n visible={\n suggestContent !== null &&\n suggestContent.length > 0 &&\n !currentInputValueSelectedFromSuggest &&\n inputFocused &&\n showDrop\n }\n onClose={() => setShowDrop(false)}\n host={dropHost}\n activatorRef={activatorRef}\n role=\"status\"\n space={400}\n widthEqualToActivator\n forcePosition\n arrowNavigation\n data-qa=\"suggest-drop\"\n ref={suggestContainerRef}\n >\n <div onKeyDown={processInput(inputRef)} className={styles.suggestItemsContainer} style={{ maxHeight }}>\n <div className={styles.suggestItemsContainerWrapper} style={{ gap: itemsGap }}>\n {suggestContent}\n </div>\n </div>\n </Drop>\n </>\n );\n};\n\nSuggestPicker.displayName = 'SuggestPicker';\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":";;;;;;;;;;;;;AA+BO,MAAM,aAAa,GAAG,UAA8C,EACvE,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,SAAS,EACT,kBAAkB,EAClB,KAAK,EACL,YAAY,EACZ,oCAAoC,EACpC,mBAAmB,EACnB,QAAQ,EACR,kBAAkB,EAClB,QAAQ,EACR,UAAU,GACa,EAAA;IACvB,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,aAAa,EAAE,CAAC;IACjD,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC/C,IAAA,MAAM,mBAAmB,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAC3D,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAExD,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,YAAY,EAAE;YACd,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAC5B,SAAA;aAAM,IAAI,CAAC,QAAQ,EAAE;YAClB,kBAAkB,CAAC,KAAK,CAAC,CAAC;AAC7B,SAAA;;AAEL,KAAC,EAAE,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAEvC,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,oCAAoC,EAAE;YACtC,kBAAkB,CAAC,KAAK,CAAC,CAAC;AAC1B,YAAA,IAAI,QAAQ,EAAE;AACV,gBAAA,kBAAkB,EAAE,CAAC;AACxB,aAAA;AACJ,SAAA;KACJ,EAAE,CAAC,oCAAoC,EAAE,kBAAkB,EAAE,QAAQ,CAAC,CAAC,CAAC;;IAGzE,SAAS,CAAC,MAAK;QACX,WAAW,CAAC,IAAI,CAAC,CAAC;AACtB,KAAC,EAAE,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;AAE/B,IAAA,MAAM,cAAc,GAAG,OAAO,CAAC,MAAK;QAChC,IAAI,CAAC,IAAI,EAAE;AACP,YAAA,OAAO,IAAI,CAAC;AACf,SAAA;QAED,OAAO,IAAI,CAAC,MAAM,CAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,KAAI;AAC9D,YAAA,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;AAC9E,YAAA,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;AAC7B,YAAA,OAAO,MAAM,CAAC;SACjB,EAAE,EAAE,CAAC,CAAC;KACV,EAAE,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;AAEtC,IAAA,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,CAAC;IACvC,MAAM,YAAY,GACd,CAAC,GAA2B,KAC5B,CAAC,KAAK,KAAI;AACN,QAAA,MAAM,0BAA0B,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;QAC7E,IAAI,CAAC,0BAA0B,EAAE;YAC7B,OAAO;AACV,SAAA;AACD,QAAA,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,IAAI,aAAa,CAAC,SAAS,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;AACjE,QAAA,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;AACzC,KAAC,CAAC;AAEN,IAAA,QACIA,IAAA,CAAAC,QAAA,EAAA,EAAA,QAAA,EAAA,CACIC,GAAC,CAAA,WAAW,EACR,EAAA,OAAO,EAAE,eAAe,EACxB,MAAM,EAAC,aAAa,EACpB,uBAAuB,EAAE,KAAK,EAC9B,MAAM,EACFA,GAAC,CAAA,aAAa,EACN,EAAA,GAAA,kBAAkB,EACtB,OAAO,EACHA,GAAA,CAAA,KAAA,EAAA,EACI,SAAS,EAAE,UAAU,CAAC;AAClB,4BAAA,CAAC,MAAM,CAAC,yBAAyB,GAAG,CAAC,YAAY;yBACpD,CAAC,EAAA,QAAA,EAEFA,GAAC,CAAA,cAAc,EACN,EAAA,GAAA;gCACD,GAAG,KAAK,CAAC,KAAK;AACd,gCAAA,OAAO,EAAE,KAAK;AACd,gCAAA,GAAG,EAAE,mBAAmB;AACxB,gCAAA,YAAY,EAAE,KAAK;AAEmC,6BAAA,EAAA,CAC5D,GACA,EAEZ,CAAA,EAEN,QAAQ,EAAE,MAAK;AACX,oBAAA,mBAAmB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;oBACtC,eAAe,CAAC,IAAI,CAAC,CAAC;AAC1B,iBAAC,EACD,YAAY,EAAE,MAAK;AACf,oBAAA,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;oBACrC,eAAe,CAAC,KAAK,CAAC,CAAC;AAC3B,iBAAC,EACD,OAAO,EAAE,MAAK;oBACV,kBAAkB,CAAC,KAAK,CAAC,CAAC;AAC1B,oBAAA,kBAAkB,EAAE,CAAC;iBACxB,EACD,GAAG,EAAE,mBAAmB,EACxB,uBAAuB,EAAA,IAAA,EAAA,QAAA,EAEvBA,GACI,CAAA,KAAA,EAAA,EAAA,SAAS,EAAE,YAAY,CAAC,mBAAmB,CAAC,EAC5C,SAAS,EAAE,MAAM,CAAC,qBAAqB,EACvC,KAAK,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAEvB,QAAA,EAAA,cAAc,GACb,EACI,CAAA,EACdA,GAAC,CAAA,IAAI,EACD,EAAA,SAAS,EAAC,QAAQ,EAClB,SAAS,EAAC,MAAM,EAChB,OAAO,EACH,cAAc,KAAK,IAAI;oBACvB,cAAc,CAAC,MAAM,GAAG,CAAC;AACzB,oBAAA,CAAC,oCAAoC;oBACrC,YAAY;AACZ,oBAAA,QAAQ,EAEZ,OAAO,EAAE,MAAM,WAAW,CAAC,KAAK,CAAC,EACjC,IAAI,EAAE,QAAQ,EACd,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,GAAG,EACV,qBAAqB,EAAA,IAAA,EACrB,aAAa,EAAA,IAAA,EACb,eAAe,EACP,IAAA,EAAA,SAAA,EAAA,cAAc,EACtB,GAAG,EAAE,mBAAmB,EAExB,QAAA,EAAAA,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,qBAAqB,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,YACjGA,GAAK,CAAA,KAAA,EAAA,EAAA,SAAS,EAAE,MAAM,CAAC,4BAA4B,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,YACxE,cAAc,EAAA,CACb,GACJ,EACH,CAAA,CAAA,EAAA,CACR,EACL;AACN,EAAE;AAEF,aAAa,CAAC,WAAW,GAAG,eAAe;;;;"}
@@ -0,0 +1,21 @@
1
+ import { DataProvider, DataProviderResult } from './types';
2
+ type CancelCallback = (callback: VoidFunction) => void;
3
+ export interface FetcherResultProducer<R = never, I = undefined> {
4
+ (data: I | null, query: string): DataProviderResult<R>;
5
+ }
6
+ export interface AsyncDataFetcher<R = never, T = DataProviderResult<R>> {
7
+ (url: string, onCancel: CancelCallback): Promise<T | null>;
8
+ }
9
+ export declare const decorateWithQueryTransofrmer: <K, T>(fetcher: AsyncDataFetcher<K, T>, decorator: (query: string) => string) => AsyncDataFetcher<K, T>;
10
+ export declare const decorateWithResponseFormatter: <T, F, K = never>(fetcher: AsyncDataFetcher<F, T>, formatter: FetcherResultProducer<K, T>) => AsyncDataFetcher<K, DataProviderResult<K>>;
11
+ export declare const combineProviders: <T>(providers: DataProvider<T>[]) => DataProvider<T>;
12
+ export declare const defaultFetcher: <T>(url: string, onCancel: CancelCallback) => Promise<T | null>;
13
+ export declare const createDataProvider: <K>({ fetcher, debounceTimeout, minCharsCount, }: {
14
+ fetcher: AsyncDataFetcher<K, DataProviderResult<K>>;
15
+ debounceTimeout?: number | undefined;
16
+ minCharsCount?: number | undefined;
17
+ }) => DataProvider<K>;
18
+ export declare const createStaticDataFetcher: <T extends {
19
+ text: string;
20
+ }>(data: T[]) => AsyncDataFetcher<never, T[]>;
21
+ export {};
@@ -0,0 +1,72 @@
1
+ import './index.css';
2
+ import axios from 'axios';
3
+ import { debounce } from '@hh.ru/magritte-common-func-utils';
4
+ import { match } from '@hh.ru/magritte-common-fuzzy-search';
5
+
6
+ const decorateWithQueryTransofrmer = (fetcher, decorator) => (query, onCancel) => fetcher(decorator(query), onCancel);
7
+ const decorateWithResponseFormatter = (fetcher, formatter) => async (url, onCancel) => formatter(await fetcher(url, onCancel), url);
8
+ const combineProviders = (providers) => {
9
+ const provider = (query) => {
10
+ const provider = providers.find((provider) => query.length >= provider.minCharsCount) || providers[0];
11
+ return provider(query);
12
+ };
13
+ provider.minCharsCount = Math.min(...providers.map((provider) => provider.minCharsCount));
14
+ return provider;
15
+ };
16
+ const createCancelablePromise = (callback) => {
17
+ let isCanceled = false;
18
+ let cancelHandler;
19
+ const promise = new Promise((resolve, reject) => {
20
+ cancelHandler = callback({
21
+ resolve: (data) => {
22
+ !isCanceled && resolve(data);
23
+ },
24
+ reject: (err) => !isCanceled && reject(err),
25
+ });
26
+ });
27
+ promise.cancel = () => {
28
+ isCanceled = true;
29
+ cancelHandler?.();
30
+ };
31
+ return promise;
32
+ };
33
+ const defaultFetcher = (url, onCancel) => {
34
+ const controller = new AbortController();
35
+ onCancel(() => controller.abort());
36
+ return axios
37
+ .get(url, { signal: controller.signal })
38
+ .then((response) => response.data)
39
+ .catch((error) => {
40
+ if (!axios.isCancel(error)) {
41
+ throw error;
42
+ }
43
+ return null;
44
+ });
45
+ };
46
+ const createDataProvider = ({ fetcher, debounceTimeout = 300, minCharsCount = 3, }) => {
47
+ const makeQuery = debounce(({ query, resolve, reject, cancelation, }) => {
48
+ if (query.length < minCharsCount || cancelation.isCanceled) {
49
+ reject();
50
+ return;
51
+ }
52
+ fetcher(query, cancelation.onCancel).then(resolve, reject);
53
+ }, debounceTimeout);
54
+ const provider = (query) => createCancelablePromise(({ resolve, reject }) => {
55
+ const cancelCallbacks = [];
56
+ const cancelation = {
57
+ isCanceled: false,
58
+ onCancel: (callback) => cancelCallbacks.push(callback),
59
+ };
60
+ makeQuery({ query, resolve, reject, cancelation });
61
+ return () => {
62
+ cancelation.isCanceled = true;
63
+ cancelCallbacks.forEach((callback) => callback());
64
+ };
65
+ });
66
+ provider.minCharsCount = minCharsCount;
67
+ return provider;
68
+ };
69
+ const createStaticDataFetcher = (data) => (query) => Promise.resolve(data.filter(({ text }) => match(query, text)));
70
+
71
+ export { combineProviders, createDataProvider, createStaticDataFetcher, decorateWithQueryTransofrmer, decorateWithResponseFormatter, defaultFetcher };
72
+ //# sourceMappingURL=dataProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataProvider.js","sources":["../src/dataProvider.ts"],"sourcesContent":["import axios from 'axios';\n\nimport { debounce } from '@hh.ru/magritte-common-func-utils';\nimport { match } from '@hh.ru/magritte-common-fuzzy-search';\nimport { DataProvider, DataProviderResult } from '@hh.ru/magritte-ui-suggest/types';\n\ntype CancelCallback = (callback: VoidFunction) => void;\n\nexport interface FetcherResultProducer<R = never, I = undefined> {\n (data: I | null, query: string): DataProviderResult<R>;\n}\nexport interface AsyncDataFetcher<R = never, T = DataProviderResult<R>> {\n (url: string, onCancel: CancelCallback): Promise<T | null>;\n}\n\nexport const decorateWithQueryTransofrmer =\n <K, T>(fetcher: AsyncDataFetcher<K, T>, decorator: (query: string) => string): AsyncDataFetcher<K, T> =>\n (query, onCancel) =>\n fetcher(decorator(query), onCancel);\n\nexport const decorateWithResponseFormatter =\n <T, F, K = never>(fetcher: AsyncDataFetcher<F, T>, formatter: FetcherResultProducer<K, T>): AsyncDataFetcher<K> =>\n async (url, onCancel) =>\n formatter(await fetcher(url, onCancel), url);\n\nexport const combineProviders = <T>(providers: Array<DataProvider<T>>): DataProvider<T> => {\n const provider = (query: string) => {\n const provider = providers.find((provider) => query.length >= provider.minCharsCount) || providers[0];\n return provider(query);\n };\n\n provider.minCharsCount = Math.min(...providers.map((provider) => provider.minCharsCount));\n return provider;\n};\n\ninterface ResolverCallback<T> {\n (arg: { resolve: (result: T | null) => void; reject: (err?: unknown) => void }): VoidFunction | void;\n}\n\nconst createCancelablePromise = <T>(callback: ResolverCallback<T>) => {\n let isCanceled = false;\n let cancelHandler: VoidFunction | void;\n\n const promise = new Promise<T | null>((resolve, reject) => {\n cancelHandler = callback({\n resolve: (data: T | null) => {\n !isCanceled && resolve(data);\n },\n reject: (err: unknown) => !isCanceled && reject(err),\n });\n }) as Promise<T> & { cancel: VoidFunction };\n\n promise.cancel = () => {\n isCanceled = true;\n cancelHandler?.();\n };\n return promise;\n};\n\nexport const defaultFetcher = <T>(url: string, onCancel: CancelCallback): Promise<T | null> => {\n const controller = new AbortController();\n onCancel(() => controller.abort());\n\n return axios\n .get<T>(url, { signal: controller.signal })\n .then((response) => response.data)\n .catch((error) => {\n if (!axios.isCancel(error)) {\n throw error;\n }\n return null;\n });\n};\n\nexport const createDataProvider = <K>({\n fetcher,\n debounceTimeout = 300,\n minCharsCount = 3,\n}: {\n fetcher: AsyncDataFetcher<K>;\n debounceTimeout?: number;\n minCharsCount?: number;\n}): DataProvider<K> => {\n const makeQuery = debounce(\n ({\n query,\n resolve,\n reject,\n cancelation,\n }: Parameters<ResolverCallback<DataProviderResult<K>>>[0] & {\n query: string;\n cancelation: { isCanceled: boolean; onCancel: CancelCallback };\n }) => {\n if (query.length < minCharsCount || cancelation.isCanceled) {\n reject();\n return;\n }\n fetcher(query, cancelation.onCancel).then(resolve, reject);\n },\n debounceTimeout\n );\n\n const provider = (query: string) =>\n createCancelablePromise<DataProviderResult<K>>(({ resolve, reject }) => {\n const cancelCallbacks: VoidFunction[] = [];\n const cancelation = {\n isCanceled: false,\n onCancel: (callback: VoidFunction) => cancelCallbacks.push(callback),\n };\n makeQuery({ query, resolve, reject, cancelation });\n return () => {\n cancelation.isCanceled = true;\n cancelCallbacks.forEach((callback) => callback());\n };\n });\n\n provider.minCharsCount = minCharsCount;\n return provider;\n};\n\nexport const createStaticDataFetcher =\n <T extends { text: string }>(data: Array<T>): AsyncDataFetcher<never, T[]> =>\n (query) =>\n Promise.resolve(data.filter(({ text }) => match(query, text)));\n"],"names":[],"mappings":";;;;AAea,MAAA,4BAA4B,GACrC,CAAO,OAA+B,EAAE,SAAoC,KAC5E,CAAC,KAAK,EAAE,QAAQ,KACZ,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE;AAErC,MAAM,6BAA6B,GACtC,CAAkB,OAA+B,EAAE,SAAsC,KACzF,OAAO,GAAG,EAAE,QAAQ,KAChB,SAAS,CAAC,MAAM,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE;AAExC,MAAA,gBAAgB,GAAG,CAAI,SAAiC,KAAqB;AACtF,IAAA,MAAM,QAAQ,GAAG,CAAC,KAAa,KAAI;QAC/B,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC,aAAa,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC;AACtG,QAAA,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3B,KAAC,CAAC;IAEF,QAAQ,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;AAC1F,IAAA,OAAO,QAAQ,CAAC;AACpB,EAAE;AAMF,MAAM,uBAAuB,GAAG,CAAI,QAA6B,KAAI;IACjE,IAAI,UAAU,GAAG,KAAK,CAAC;AACvB,IAAA,IAAI,aAAkC,CAAC;IAEvC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAW,CAAC,OAAO,EAAE,MAAM,KAAI;QACtD,aAAa,GAAG,QAAQ,CAAC;AACrB,YAAA,OAAO,EAAE,CAAC,IAAc,KAAI;AACxB,gBAAA,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;aAChC;AACD,YAAA,MAAM,EAAE,CAAC,GAAY,KAAK,CAAC,UAAU,IAAI,MAAM,CAAC,GAAG,CAAC;AACvD,SAAA,CAAC,CAAC;AACP,KAAC,CAA0C,CAAC;AAE5C,IAAA,OAAO,CAAC,MAAM,GAAG,MAAK;QAClB,UAAU,GAAG,IAAI,CAAC;QAClB,aAAa,IAAI,CAAC;AACtB,KAAC,CAAC;AACF,IAAA,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC;MAEW,cAAc,GAAG,CAAI,GAAW,EAAE,QAAwB,KAAuB;AAC1F,IAAA,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,QAAQ,CAAC,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;AAEnC,IAAA,OAAO,KAAK;SACP,GAAG,CAAI,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;SAC1C,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,IAAI,CAAC;AACjC,SAAA,KAAK,CAAC,CAAC,KAAK,KAAI;AACb,QAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AACxB,YAAA,MAAM,KAAK,CAAC;AACf,SAAA;AACD,QAAA,OAAO,IAAI,CAAC;AAChB,KAAC,CAAC,CAAC;AACX,EAAE;AAEW,MAAA,kBAAkB,GAAG,CAAI,EAClC,OAAO,EACP,eAAe,GAAG,GAAG,EACrB,aAAa,GAAG,CAAC,GAKpB,KAAqB;AAClB,IAAA,MAAM,SAAS,GAAG,QAAQ,CACtB,CAAC,EACG,KAAK,EACL,OAAO,EACP,MAAM,EACN,WAAW,GAId,KAAI;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,aAAa,IAAI,WAAW,CAAC,UAAU,EAAE;AACxD,YAAA,MAAM,EAAE,CAAC;YACT,OAAO;AACV,SAAA;AACD,QAAA,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;KAC9D,EACD,eAAe,CAClB,CAAC;AAEF,IAAA,MAAM,QAAQ,GAAG,CAAC,KAAa,KAC3B,uBAAuB,CAAwB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAI;QACnE,MAAM,eAAe,GAAmB,EAAE,CAAC;AAC3C,QAAA,MAAM,WAAW,GAAG;AAChB,YAAA,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,CAAC,QAAsB,KAAK,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC;SACvE,CAAC;QACF,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;AACnD,QAAA,OAAO,MAAK;AACR,YAAA,WAAW,CAAC,UAAU,GAAG,IAAI,CAAC;YAC9B,eAAe,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC,CAAC;AACtD,SAAC,CAAC;AACN,KAAC,CAAC,CAAC;AAEP,IAAA,QAAQ,CAAC,aAAa,GAAG,aAAa,CAAC;AACvC,IAAA,OAAO,QAAQ,CAAC;AACpB,EAAE;AAEW,MAAA,uBAAuB,GAChC,CAA6B,IAAc,KAC3C,CAAC,KAAK,KACF,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;;;;"}
package/index.css ADDED
@@ -0,0 +1,48 @@
1
+ :root{
2
+ --magritte-color-text-primary-v18-1-1:#000000;
3
+ }
4
+ :root{
5
+ --magritte-static-space-400-v18-1-1:16px;
6
+ }
7
+ .magritte-night-theme{
8
+ --magritte-color-text-primary-v18-1-1:#ffffff;
9
+ }
10
+ .magritte-chips-section-container___Z7N-E_3-1-20{
11
+ display:flex;
12
+ gap:12px;
13
+ padding-bottom:16px;
14
+ flex:1 1;
15
+ flex-wrap:wrap;
16
+ }
17
+ .magritte-suggest-items-container___SKj-g_3-1-20{
18
+ display:flex;
19
+ flex-direction:column;
20
+ }
21
+ .magritte-suggest-items-container___SKj-g_3-1-20 .focus-visible{
22
+ z-index:1;
23
+ position:relative;
24
+ }
25
+ .magritte-suggest-items-container-wrapper___T2L5U_3-1-20{
26
+ padding:var(--magritte-static-space-400-v18-1-1);
27
+ margin:calc(-1 * var(--magritte-static-space-400-v18-1-1));
28
+ display:flex;
29
+ flex-direction:column;
30
+ }
31
+ .magritte-suggest-input-container___l3OkM_3-1-20{
32
+ --magritte-ui-input-caret-color-override:transparent;
33
+ display:inline-block;
34
+ width:100%;
35
+ }
36
+ @media (min-width: 700px){
37
+ body.magritte-old-layout .magritte-suggest-input-container___l3OkM_3-1-20{
38
+ --magritte-ui-input-caret-color-override:var(--magritte-color-text-primary-v18-1-1);
39
+ }
40
+ }
41
+ @media (min-width: 600px){
42
+ body:not(.magritte-old-layout) .magritte-suggest-input-container___l3OkM_3-1-20{
43
+ --magritte-ui-input-caret-color-override:var(--magritte-color-text-primary-v18-1-1);
44
+ }
45
+ }
46
+ .magritte-bottom-sheet-input-hide-caret___ikoig_3-1-20{
47
+ --magritte-ui-input-caret-color-override:transparent;
48
+ }
package/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from '@hh.ru/magritte-ui-theme-provider';
2
+ export * from './Suggest';
3
+ export { createDataProvider, decorateWithQueryTransofrmer, decorateWithResponseFormatter, defaultFetcher, createStaticDataFetcher, combineProviders, type AsyncDataFetcher, } from './dataProvider';
4
+ export type { DataSection, DataSectionOfType, DataProviderResult, DataProvider, SuggestInputProps, } from './types';
package/index.js ADDED
@@ -0,0 +1,24 @@
1
+ import './index.css';
2
+ export * from '@hh.ru/magritte-ui-theme-provider';
3
+ export { Suggest } from './Suggest.js';
4
+ export { combineProviders, createDataProvider, createStaticDataFetcher, decorateWithQueryTransofrmer, decorateWithResponseFormatter, defaultFetcher } from './dataProvider.js';
5
+ import 'react/jsx-runtime';
6
+ import 'react';
7
+ import '@hh.ru/magritte-common-use-multiple-refs';
8
+ import '@hh.ru/magritte-ui-breakpoint';
9
+ import './SuggestPicker.js';
10
+ import 'classnames';
11
+ import '@hh.ru/magritte-ui-bottom-sheet';
12
+ import '@hh.ru/magritte-ui-drop';
13
+ import '@hh.ru/magritte-ui-navigation-bar';
14
+ import './sectionRenderers-859b79db.js';
15
+ import '@hh.ru/magritte-ui-card';
16
+ import '@hh.ru/magritte-ui-cell';
17
+ import '@hh.ru/magritte-ui-chips';
18
+ import '@hh.ru/magritte-ui-typography';
19
+ import './useSuggestKeyboardNav.js';
20
+ import '@hh.ru/magritte-common-keyboard';
21
+ import 'axios';
22
+ import '@hh.ru/magritte-common-func-utils';
23
+ import '@hh.ru/magritte-common-fuzzy-search';
24
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,6 @@
1
+ import { ForwardRefExoticComponent } from 'react';
2
+ export { type AsyncDataFetcher } from './dataProvider';
3
+ export type { DataSection, DataSectionOfType, DataProviderResult, DataProvider, SuggestInputProps, } from './types';
4
+ declare const ThemeProvider: any;
5
+ export { ThemeProvider };
6
+ export declare const Input: ForwardRefExoticComponent<Record<string, unknown>>;
package/index.mock.js ADDED
@@ -0,0 +1,11 @@
1
+ import './index.css';
2
+ import { mockComponent } from '@hh.ru/magritte-ui-mock-component';
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
5
+ const { ThemeProvider } = jest.requireActual('@hh.ru/magritte-ui-theme-provider/index');
6
+ const Input = mockComponent('Suggest', undefined, {
7
+ withChildren: false,
8
+ });
9
+
10
+ export { Input, ThemeProvider };
11
+ //# sourceMappingURL=index.mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mock.js","sources":["../src/index.mock.ts"],"sourcesContent":["import { ForwardRefExoticComponent } from 'react';\n\nimport { mockComponent } from '@hh.ru/magritte-ui-mock-component';\n\nexport { type AsyncDataFetcher } from '@hh.ru/magritte-ui-suggest/dataProvider';\n\nexport type {\n DataSection,\n DataSectionOfType,\n DataProviderResult,\n DataProvider,\n SuggestInputProps,\n} from '@hh.ru/magritte-ui-suggest/types';\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nconst { ThemeProvider } = jest.requireActual('@hh.ru/magritte-ui-theme-provider/index');\n\nexport { ThemeProvider };\n\nexport const Input: ForwardRefExoticComponent<Record<string, unknown>> = mockComponent('Suggest', undefined, {\n withChildren: false,\n});\n"],"names":[],"mappings":";;AAcA;AACM,MAAA,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,yCAAyC,EAAE;MAI3E,KAAK,GAAuD,aAAa,CAAC,SAAS,EAAE,SAAS,EAAE;AACzG,IAAA,YAAY,EAAE,KAAK;AACtB,CAAA;;;;"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@hh.ru/magritte-ui-suggest",
3
+ "version": "3.1.20",
4
+ "main": "index.js",
5
+ "types": "index.d.ts",
6
+ "sideEffects": [
7
+ "index.css"
8
+ ],
9
+ "scripts": {
10
+ "build": "yarn root:build $(pwd)",
11
+ "build-test-branch": "yarn root:build-test-branch $(pwd)",
12
+ "changelog": "yarn root:changelog $(pwd)",
13
+ "prepack": "yarn root:prepack $(pwd)",
14
+ "postpublish": "yarn root:postpublish $(pwd)",
15
+ "stylelint-test": "yarn root:stylelint-test $(pwd)",
16
+ "eslint-test": "yarn root:eslint-test $(pwd)",
17
+ "ts-config": "yarn root:ts-config $(pwd)",
18
+ "ts-check": "yarn root:ts-check $(pwd)",
19
+ "test": "yarn root:test $(pwd)"
20
+ },
21
+ "dependencies": {
22
+ "@hh.ru/magritte-common-func-utils": "1.3.6",
23
+ "@hh.ru/magritte-common-fuzzy-search": "1.0.4",
24
+ "@hh.ru/magritte-common-keyboard": "3.0.2",
25
+ "@hh.ru/magritte-common-use-multiple-refs": "1.1.2",
26
+ "@hh.ru/magritte-design-tokens": "18.1.1",
27
+ "@hh.ru/magritte-ui-bottom-sheet": "4.1.22",
28
+ "@hh.ru/magritte-ui-breakpoint": "4.0.1",
29
+ "@hh.ru/magritte-ui-card": "6.0.2",
30
+ "@hh.ru/magritte-ui-cell": "2.2.4",
31
+ "@hh.ru/magritte-ui-chips": "3.0.23",
32
+ "@hh.ru/magritte-ui-drop": "5.0.8",
33
+ "@hh.ru/magritte-ui-input": "5.0.12",
34
+ "@hh.ru/magritte-ui-mock-component": "1.0.10",
35
+ "@hh.ru/magritte-ui-navigation-bar": "4.1.8",
36
+ "@hh.ru/magritte-ui-theme-provider": "1.1.21",
37
+ "@hh.ru/magritte-ui-typography": "3.0.8"
38
+ },
39
+ "peerDependencies": {
40
+ "axios": ">=0.26.0",
41
+ "classnames": ">=2.3.2",
42
+ "react": ">=18.2.0"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "gitHead": "b660dd64fca3eaad5ec44ea9a73f92bb0d9694aa"
48
+ }
@@ -0,0 +1,44 @@
1
+ import './index.css';
2
+ import { createElement } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import { Breakpoint } from '@hh.ru/magritte-ui-breakpoint';
5
+ import { Card } from '@hh.ru/magritte-ui-card';
6
+ import { Cell } from '@hh.ru/magritte-ui-cell';
7
+ import { CustomChip } from '@hh.ru/magritte-ui-chips';
8
+ import { Text } from '@hh.ru/magritte-ui-typography';
9
+
10
+ var styles = {"chips-section-container":"magritte-chips-section-container___Z7N-E_3-1-20","chipsSectionContainer":"magritte-chips-section-container___Z7N-E_3-1-20","suggest-items-container":"magritte-suggest-items-container___SKj-g_3-1-20","suggestItemsContainer":"magritte-suggest-items-container___SKj-g_3-1-20","suggest-items-container-wrapper":"magritte-suggest-items-container-wrapper___T2L5U_3-1-20","suggestItemsContainerWrapper":"magritte-suggest-items-container-wrapper___T2L5U_3-1-20","suggest-input-container":"magritte-suggest-input-container___l3OkM_3-1-20","suggestInputContainer":"magritte-suggest-input-container___l3OkM_3-1-20","bottom-sheet-input-hide-caret":"magritte-bottom-sheet-input-hide-caret___ikoig_3-1-20","bottomSheetInputHideCaret":"magritte-bottom-sheet-input-hide-caret___ikoig_3-1-20"};
11
+
12
+ const cellSectionRenderer = (section, sectionId, onValueSelect, breakpoint) => {
13
+ return section.items.map((item) => {
14
+ return (jsx(Card, { style: "blank", stretched: true, hoverStyle: "neutral", onMouseDown: (event) => {
15
+ event.preventDefault();
16
+ }, onClick: (event) => {
17
+ event.preventDefault();
18
+ onValueSelect(item.value, item.data);
19
+ }, actionCard: true, padding: [Breakpoint.XS, Breakpoint.S].includes(breakpoint) ? 0 : 16, paddingTop: 12, paddingBottom: 12, borderRadius: 12, "data-qa": "suggest-item-cell", children: jsx(Cell, { ...item.componentProps, onClick: undefined }) }, `cell_${sectionId}_${item.value}`));
20
+ });
21
+ };
22
+ const chipsSectionRenderer = (section, sectionId, onValueSelect) => {
23
+ return (jsx("div", { className: styles.chipsSectionContainer, children: section.items.map((item) => {
24
+ return (createElement(CustomChip, { ...(item.componentProps || {}), key: `chips_${sectionId}_${item.value}`, onMouseDown: (event) => event.preventDefault(), onClick: () => onValueSelect(item.value, item.data), "data-qa": "suggest-item-chips" }, item.value));
25
+ }) }, `chips_section_${sectionId}`));
26
+ };
27
+ const delimiterSectionRenderer = (section, _, __, breakpoint) => {
28
+ return [
29
+ jsx(Card, { style: "blank", stretched: true, padding: [Breakpoint.XS, Breakpoint.S].includes(breakpoint) ? 0 : 16, paddingTop: 8, paddingBottom: 8, "data-qa": "suggest-item-delimiter", children: jsx(Text, { style: "secondary", typography: "label-3-regular", children: section.text }) }, `delimiter_${section.text}`),
30
+ ];
31
+ };
32
+ const SECTION_TYPE_TO_RENDERER_MAP = {
33
+ cells: cellSectionRenderer,
34
+ chips: chipsSectionRenderer,
35
+ delimiter: delimiterSectionRenderer,
36
+ };
37
+ const renderSection = (section, sectionId, onValueSelect, breakpoint) => {
38
+ const renderer = SECTION_TYPE_TO_RENDERER_MAP[section.type];
39
+ const result = renderer(section, sectionId, onValueSelect, breakpoint);
40
+ return Array.isArray(result) ? result : [result];
41
+ };
42
+
43
+ export { renderSection as r, styles as s };
44
+ //# sourceMappingURL=sectionRenderers-859b79db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sectionRenderers-859b79db.js","sources":["../src/sectionRenderers.tsx"],"sourcesContent":["import { Breakpoint } from '@hh.ru/magritte-ui-breakpoint';\nimport { Card } from '@hh.ru/magritte-ui-card';\nimport { Cell } from '@hh.ru/magritte-ui-cell';\nimport { CustomChip } from '@hh.ru/magritte-ui-chips';\nimport { DataSection, DataSectionOfType } from '@hh.ru/magritte-ui-suggest/types';\nimport { Text } from '@hh.ru/magritte-ui-typography';\n\nimport styles from './suggest.less';\n\ninterface ValueSelectHandler<K> {\n (value: string, data: K | undefined): void;\n}\n\ninterface SectionRenderer<T extends DataSection['type'], K = unknown> {\n (section: DataSectionOfType<T>, sectionId: number, onValueSelect: ValueSelectHandler<K>, breakpoint: Breakpoint):\n | JSX.Element[]\n | JSX.Element;\n}\n\nconst cellSectionRenderer: SectionRenderer<'cells'> = (section, sectionId, onValueSelect, breakpoint) => {\n return section.items.map((item) => {\n return (\n <Card\n style=\"blank\"\n stretched\n hoverStyle=\"neutral\"\n onMouseDown={(event) => {\n event.preventDefault();\n }}\n onClick={(event) => {\n event.preventDefault();\n onValueSelect(item.value, item.data);\n }}\n actionCard\n padding={[Breakpoint.XS, Breakpoint.S].includes(breakpoint) ? 0 : 16}\n paddingTop={12}\n paddingBottom={12}\n borderRadius={12}\n key={`cell_${sectionId}_${item.value}`}\n data-qa=\"suggest-item-cell\"\n >\n <Cell {...item.componentProps} onClick={undefined} />\n </Card>\n );\n });\n};\n\nconst chipsSectionRenderer: SectionRenderer<'chips'> = (section, sectionId, onValueSelect) => {\n return (\n <div className={styles.chipsSectionContainer} key={`chips_section_${sectionId}`}>\n {section.items.map((item) => {\n return (\n <CustomChip\n {...(item.componentProps || {})}\n key={`chips_${sectionId}_${item.value}`}\n onMouseDown={(event) => event.preventDefault()}\n onClick={() => onValueSelect(item.value, item.data)}\n data-qa=\"suggest-item-chips\"\n >\n {item.value}\n </CustomChip>\n );\n })}\n </div>\n );\n};\n\nconst delimiterSectionRenderer: SectionRenderer<'delimiter'> = (section, _, __, breakpoint) => {\n return [\n <Card\n style=\"blank\"\n stretched\n padding={[Breakpoint.XS, Breakpoint.S].includes(breakpoint) ? 0 : 16}\n paddingTop={8}\n paddingBottom={8}\n key={`delimiter_${section.text}`}\n data-qa=\"suggest-item-delimiter\"\n >\n <Text style=\"secondary\" typography=\"label-3-regular\">\n {section.text}\n </Text>\n </Card>,\n ];\n};\n\nconst SECTION_TYPE_TO_RENDERER_MAP: { [K in DataSection['type']]: SectionRenderer<K> } = {\n cells: cellSectionRenderer,\n chips: chipsSectionRenderer,\n delimiter: delimiterSectionRenderer,\n};\n\nexport const renderSection = <T, K extends DataSection['type']>(\n section: DataSectionOfType<K>,\n sectionId: number,\n onValueSelect: ValueSelectHandler<T>,\n breakpoint: Breakpoint\n): JSX.Element[] => {\n const renderer = SECTION_TYPE_TO_RENDERER_MAP[section.type];\n const result = renderer(section, sectionId, onValueSelect as ValueSelectHandler<unknown>, breakpoint);\n return Array.isArray(result) ? result : [result];\n};\n"],"names":["_jsx","_createElement"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { Breakpoint } from '@hh.ru/magritte-ui-breakpoint';
2
+ import { DataSectionOfType } from './types';
3
+ interface ValueSelectHandler<K> {
4
+ (value: string, data: K | undefined): void;
5
+ }
6
+ export declare const renderSection: <T, K extends "cells" | "chips" | "delimiter">(section: DataSectionOfType<K, unknown>, sectionId: number, onValueSelect: ValueSelectHandler<T>, breakpoint: Breakpoint) => JSX.Element[];
7
+ export {};
@@ -0,0 +1,10 @@
1
+ import './index.css';
2
+ import 'react';
3
+ import 'react/jsx-runtime';
4
+ import '@hh.ru/magritte-ui-breakpoint';
5
+ import '@hh.ru/magritte-ui-card';
6
+ import '@hh.ru/magritte-ui-cell';
7
+ import '@hh.ru/magritte-ui-chips';
8
+ import '@hh.ru/magritte-ui-typography';
9
+ export { r as renderSection } from './sectionRenderers-859b79db.js';
10
+ //# sourceMappingURL=sectionRenderers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sectionRenderers.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
package/types.d.ts ADDED
@@ -0,0 +1,88 @@
1
+ import { FocusEventHandler, ForwardRefExoticComponent, FunctionComponent, KeyboardEventHandler, Ref, RefObject } from 'react';
2
+ import { type TokenBaseCoreSpace } from '@hh.ru/magritte-design-tokens/types';
3
+ import { type CellProps } from '@hh.ru/magritte-ui-cell';
4
+ import { type CustomChipProps } from '@hh.ru/magritte-ui-chips';
5
+ import { type NavigationBarProps } from '@hh.ru/magritte-ui-navigation-bar';
6
+ export interface NativeFocusEventHandler {
7
+ (event: FocusEvent): void;
8
+ }
9
+ type DataSectionTypes<T = never> = {
10
+ cells: {
11
+ items: Array<{
12
+ componentProps: CellProps;
13
+ value: string;
14
+ data?: T;
15
+ }>;
16
+ };
17
+ chips: {
18
+ items: Array<{
19
+ componentProps?: Omit<CustomChipProps, 'Element' | 'onDelete'>;
20
+ value: string;
21
+ data?: T;
22
+ }>;
23
+ };
24
+ delimiter: {
25
+ text: string;
26
+ };
27
+ };
28
+ export type DataSectionOfType<T extends keyof DataSectionTypes, K = unknown> = DataSectionTypes<K>[T] & {
29
+ type: T;
30
+ };
31
+ export type DataSection<K = unknown> = {
32
+ [T in keyof DataSectionTypes]: DataSectionOfType<T, K>;
33
+ }[keyof DataSectionTypes];
34
+ export type DataProviderResult<T = unknown> = Array<DataSection<T>>;
35
+ export type DataProviderRequest<T = unknown> = Promise<DataProviderResult<T>> & {
36
+ cancel: VoidFunction;
37
+ };
38
+ export interface DataProvider<T = never> {
39
+ (query: string): DataProviderRequest<T>;
40
+ minCharsCount: number;
41
+ }
42
+ export interface RequiredInputProps {
43
+ value: string;
44
+ onChange: (value: string) => void;
45
+ ref: Ref<HTMLElement>;
46
+ onFocus: FocusEventHandler<HTMLElement>;
47
+ onKeyDown: KeyboardEventHandler<HTMLElement>;
48
+ }
49
+ export interface ControlledInputProps {
50
+ value: string;
51
+ onChange: (value: string) => void;
52
+ }
53
+ export interface SuggestInputProps extends ControlledInputProps {
54
+ ref?: Ref<HTMLElement>;
55
+ onFocus?: FocusEventHandler<HTMLElement>;
56
+ onKeyDown?: KeyboardEventHandler<HTMLElement>;
57
+ }
58
+ export type SuggestInputComponent = FunctionComponent<ControlledInputProps> | ForwardRefExoticComponent<ControlledInputProps>;
59
+ export type InputProp<P extends SuggestInputComponent, T = Parameters<P>[0], K = Required<T>> = K extends RequiredInputProps ? {
60
+ component: P;
61
+ props: Omit<T, keyof RequiredInputProps> & Partial<RequiredInputProps> & {
62
+ [key: `data-${string}`]: string;
63
+ };
64
+ } : never;
65
+ export interface SuggestProps<K, P extends SuggestInputComponent = SuggestInputComponent> {
66
+ /** Функция провайдер данных */
67
+ dataProvider: DataProvider<K>;
68
+ /** Значение в инпуте в саджесте, используется при работе в controlled режиме */
69
+ inputValue?: string;
70
+ /** Обработчик выбора значения в саджесте, с помощью возвращаемого значения
71
+ * происходит управление применением выбора */
72
+ onSelectValidator?: (value: string, data: K | undefined) => boolean;
73
+ /** Отступ в пикселях между элементами саджеста */
74
+ itemsGap?: TokenBaseCoreSpace;
75
+ /** Компонент используемый в качестве инпута в саджесте и его пропсы, в качестве компонента могуст использоваться
76
+ * только controlled инпуты
77
+ */
78
+ input: InputProp<P>;
79
+ /** Пропсы NavigationBar который отображается в BottomSheet саджеста на мобильных разрешениях */
80
+ navigationBarProps?: NavigationBarProps;
81
+ /** Максимальная высота дропа с вариантами */
82
+ maxHeight?: number;
83
+ /** Хост элемент для отрисовки дропа, если не передан используется document.body */
84
+ dropHost?: RefObject<HTMLElement>;
85
+ /** Обработчик события потери фокуса садджестом */
86
+ onBlur?: NativeFocusEventHandler;
87
+ }
88
+ export {};
package/types.js ADDED
@@ -0,0 +1,3 @@
1
+ import './index.css';
2
+
3
+ //# sourceMappingURL=types.js.map
package/types.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ import { RefObject, KeyboardEventHandler, FocusEventHandler, Dispatch, SetStateAction } from 'react';
2
+ import { NativeFocusEventHandler } from './types';
3
+ export declare const useSuggestKeyboardNav: (suggestContainerRef: RefObject<HTMLElement>, inputContainerRef: RefObject<HTMLDivElement>, setValueSelectedFromSuggest: Dispatch<SetStateAction<string | false>>, actualInputValue: string, inputRef: RefObject<HTMLElement>, onBlur: RefObject<NativeFocusEventHandler | undefined>) => {
4
+ focused: boolean;
5
+ onFocus: FocusEventHandler<HTMLElement>;
6
+ onKeyDown: KeyboardEventHandler<HTMLInputElement>;
7
+ onBottomSheetClose: VoidFunction;
8
+ };
@@ -0,0 +1,70 @@
1
+ import './index.css';
2
+ import { useState, useRef, useCallback } from 'react';
3
+ import { keyboardMatches, keyboardKeys, keyboardMatch } from '@hh.ru/magritte-common-keyboard';
4
+ import { useBreakpoint } from '@hh.ru/magritte-ui-breakpoint';
5
+
6
+ const getFirstFocusableElement = (container) => {
7
+ const focusableElement = container.querySelector('button, [href], [tabindex]:not([tabindex="-1"])');
8
+ return focusableElement || null;
9
+ };
10
+ const useSuggestKeyboardNav = (suggestContainerRef, inputContainerRef, setValueSelectedFromSuggest, actualInputValue, inputRef, onBlur) => {
11
+ const [focused, setFocused] = useState(false);
12
+ const actualInputValueRef = useRef(actualInputValue);
13
+ actualInputValueRef.current = actualInputValue;
14
+ const { isMobile } = useBreakpoint();
15
+ const onKeyDown = useCallback((event) => {
16
+ if (!suggestContainerRef.current) {
17
+ return;
18
+ }
19
+ if (keyboardMatches(event.nativeEvent, [keyboardKeys.Tab, keyboardKeys.ArrowDown])) {
20
+ event.preventDefault();
21
+ // Не даем событию всплыть, чтобы его не обработал код клавиатурной навигации в дропе
22
+ event.stopPropagation();
23
+ const focusableElement = getFirstFocusableElement(suggestContainerRef.current);
24
+ if (focusableElement) {
25
+ focusableElement.focus();
26
+ }
27
+ }
28
+ }, [suggestContainerRef]);
29
+ const onBottomSheetClose = useCallback(() => setFocused(false), [setFocused]);
30
+ const onFocus = useCallback(() => {
31
+ const escapeHandler = (event) => {
32
+ if (keyboardMatch(event, keyboardKeys.Escape)) {
33
+ if (!isMobile) {
34
+ inputRef.current?.focus();
35
+ }
36
+ setValueSelectedFromSuggest(actualInputValueRef.current);
37
+ }
38
+ };
39
+ const blurHandler = (event) => {
40
+ // Даем фокусу перейти на следующий элемент
41
+ requestAnimationFrame(() => {
42
+ if (inputContainerRef.current?.contains(document.activeElement) ||
43
+ suggestContainerRef.current?.contains(document.activeElement) ||
44
+ document.activeElement === suggestContainerRef.current) {
45
+ return;
46
+ }
47
+ setFocused(false);
48
+ document.removeEventListener('blur', blurHandler, true);
49
+ document.removeEventListener('keydown', escapeHandler, true);
50
+ onBlur.current?.(event);
51
+ });
52
+ };
53
+ // может быть focused если например перешли на другую вкладку
54
+ // в этом случае диспатчится blur, но активным элементом остается инпут
55
+ if (!focused) {
56
+ document.addEventListener('blur', blurHandler, true);
57
+ document.addEventListener('keydown', escapeHandler, true);
58
+ }
59
+ setFocused(true);
60
+ }, [focused, setValueSelectedFromSuggest, inputContainerRef, suggestContainerRef, isMobile, inputRef, onBlur]);
61
+ return {
62
+ focused,
63
+ onBottomSheetClose,
64
+ onFocus,
65
+ onKeyDown,
66
+ };
67
+ };
68
+
69
+ export { useSuggestKeyboardNav };
70
+ //# sourceMappingURL=useSuggestKeyboardNav.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSuggestKeyboardNav.js","sources":["../src/useSuggestKeyboardNav.ts"],"sourcesContent":["import {\n RefObject,\n KeyboardEventHandler,\n useCallback,\n useState,\n FocusEventHandler,\n Dispatch,\n SetStateAction,\n useRef,\n} from 'react';\n\nimport { keyboardKeys, keyboardMatches, keyboardMatch } from '@hh.ru/magritte-common-keyboard';\nimport { useBreakpoint } from '@hh.ru/magritte-ui-breakpoint';\nimport { NativeFocusEventHandler } from '@hh.ru/magritte-ui-suggest/types';\n\nconst getFirstFocusableElement = (container: HTMLElement) => {\n const focusableElement = container.querySelector<HTMLElement>('button, [href], [tabindex]:not([tabindex=\"-1\"])');\n return focusableElement || null;\n};\n\nexport const useSuggestKeyboardNav = (\n suggestContainerRef: RefObject<HTMLElement>,\n inputContainerRef: RefObject<HTMLDivElement>,\n setValueSelectedFromSuggest: Dispatch<SetStateAction<string | false>>,\n actualInputValue: string,\n inputRef: RefObject<HTMLElement>,\n onBlur: RefObject<NativeFocusEventHandler | undefined>\n): {\n focused: boolean;\n onFocus: FocusEventHandler<HTMLElement>;\n onKeyDown: KeyboardEventHandler<HTMLInputElement>;\n onBottomSheetClose: VoidFunction;\n} => {\n const [focused, setFocused] = useState(false);\n const actualInputValueRef = useRef(actualInputValue);\n actualInputValueRef.current = actualInputValue;\n const { isMobile } = useBreakpoint();\n\n const onKeyDown: KeyboardEventHandler<HTMLInputElement> = useCallback(\n (event) => {\n if (!suggestContainerRef.current) {\n return;\n }\n\n if (keyboardMatches(event.nativeEvent, [keyboardKeys.Tab, keyboardKeys.ArrowDown])) {\n event.preventDefault();\n // Не даем событию всплыть, чтобы его не обработал код клавиатурной навигации в дропе\n event.stopPropagation();\n const focusableElement = getFirstFocusableElement(suggestContainerRef.current);\n if (focusableElement) {\n focusableElement.focus();\n }\n }\n },\n [suggestContainerRef]\n );\n\n const onBottomSheetClose = useCallback(() => setFocused(false), [setFocused]);\n\n const onFocus = useCallback(() => {\n const escapeHandler = (event: KeyboardEvent) => {\n if (keyboardMatch(event, keyboardKeys.Escape)) {\n if (!isMobile) {\n inputRef.current?.focus();\n }\n setValueSelectedFromSuggest(actualInputValueRef.current);\n }\n };\n const blurHandler = (event: FocusEvent) => {\n // Даем фокусу перейти на следующий элемент\n requestAnimationFrame(() => {\n if (\n inputContainerRef.current?.contains(document.activeElement) ||\n suggestContainerRef.current?.contains(document.activeElement) ||\n document.activeElement === suggestContainerRef.current\n ) {\n return;\n }\n setFocused(false);\n document.removeEventListener('blur', blurHandler, true);\n document.removeEventListener('keydown', escapeHandler, true);\n onBlur.current?.(event);\n });\n };\n\n // может быть focused если например перешли на другую вкладку\n // в этом случае диспатчится blur, но активным элементом остается инпут\n if (!focused) {\n document.addEventListener('blur', blurHandler, true);\n document.addEventListener('keydown', escapeHandler, true);\n }\n\n setFocused(true);\n }, [focused, setValueSelectedFromSuggest, inputContainerRef, suggestContainerRef, isMobile, inputRef, onBlur]);\n\n return {\n focused,\n onBottomSheetClose,\n onFocus,\n onKeyDown,\n };\n};\n"],"names":[],"mappings":"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"}