@n3/react-autocomplete
Advanced tools
| // src/Autocomplete.tsx | ||
| import { | ||
| useCallback as useCallback2, | ||
| useState as useState2, | ||
| useMemo, | ||
| useRef as useRef2, | ||
| useEffect as useEffect2 | ||
| } from "react"; | ||
| // src/components/Wrapper.tsx | ||
| import styled from "styled-components"; | ||
| var Wrapper = styled.div({ | ||
| position: "relative" | ||
| }); | ||
| // src/components/Input.tsx | ||
| import styled2 from "styled-components"; | ||
| var Input = styled2.input(({ | ||
| $hasError, | ||
| $hasWarning | ||
| }) => { | ||
| const res = { | ||
| width: "100%", | ||
| boxSizing: "border-box" | ||
| }; | ||
| if ($hasError) { | ||
| res.borderColor = "#d64c4c"; | ||
| res.backgroundColor = "#fdf6f6"; | ||
| } else if ($hasWarning) { | ||
| res.borderColor = "#eea505"; | ||
| res.backgroundColor = "#fdf6e6"; | ||
| } | ||
| return res; | ||
| }); | ||
| // src/components/Menu.tsx | ||
| import styled3 from "styled-components"; | ||
| var Menu = styled3.div({ | ||
| boxSizing: "border-box", | ||
| position: "absolute", | ||
| top: "100%", | ||
| width: "100%", | ||
| zIndex: 1, | ||
| marginTop: 4, | ||
| maxHeight: 200, | ||
| overflowY: "auto", | ||
| borderRadius: 4, | ||
| backgroundColor: "#fff", | ||
| border: "1px solid #d9e1e8", | ||
| boxShadow: "0 1px 0 rgba(0, 0, 0, 0.06)" | ||
| }); | ||
| // src/components/MenuItem.tsx | ||
| import styled4 from "styled-components"; | ||
| var MenuItem = styled4.div(({ | ||
| $isHighlighted | ||
| }) => ({ | ||
| display: "block", | ||
| boxSizing: "border-box", | ||
| padding: 10, | ||
| color: "#666", | ||
| backgroundColor: $isHighlighted ? "#f9f9f9" : "#fff", | ||
| cursor: "pointer" | ||
| })); | ||
| // src/components/index.ts | ||
| var components = { | ||
| Wrapper, | ||
| Input, | ||
| Menu, | ||
| MenuItem | ||
| }; | ||
| // src/Dropdown.tsx | ||
| import { | ||
| useRef, | ||
| useState, | ||
| useEffect | ||
| } from "react"; | ||
| import useOnClickOutside from "use-onclickoutside"; | ||
| // src/DropdownItem.tsx | ||
| import { | ||
| useCallback | ||
| } from "react"; | ||
| import { jsx } from "react/jsx-runtime"; | ||
| function DropdownItem({ | ||
| components: components2, | ||
| getOptionLabel, | ||
| formatOptionLabel, | ||
| isHighlighted, | ||
| option, | ||
| onSelect, | ||
| setHighlightedIndex, | ||
| index | ||
| }) { | ||
| const onClick = useCallback(() => { | ||
| onSelect(getOptionLabel(option), option); | ||
| }, [onSelect, option, getOptionLabel]); | ||
| const onMouseEnter = useCallback(() => { | ||
| setHighlightedIndex(index); | ||
| }, [setHighlightedIndex, index]); | ||
| const { | ||
| MenuItem: MenuItem2 | ||
| } = components2; | ||
| return /* @__PURE__ */ jsx( | ||
| MenuItem2, | ||
| { | ||
| $isHighlighted: isHighlighted, | ||
| onMouseEnter, | ||
| onClick, | ||
| children: formatOptionLabel(option) | ||
| } | ||
| ); | ||
| } | ||
| // src/Dropdown.tsx | ||
| import { jsx as jsx2 } from "react/jsx-runtime"; | ||
| function Dropdown({ | ||
| components: components2, | ||
| getOptionLabel, | ||
| formatOptionLabel, | ||
| options, | ||
| onSelect, | ||
| closeMenu | ||
| }) { | ||
| const rootRef = useRef(null); | ||
| useOnClickOutside(rootRef, closeMenu); | ||
| const isInitRef = useRef(true); | ||
| const [highlightedIndex, setHighlightedIndex] = useState(0); | ||
| const highlightedIndexRef = useRef(highlightedIndex); | ||
| highlightedIndexRef.current = highlightedIndex; | ||
| useEffect(() => { | ||
| if (isInitRef.current) { | ||
| isInitRef.current = false; | ||
| } else { | ||
| setHighlightedIndex(0); | ||
| } | ||
| const onKeyDown = (event) => { | ||
| switch (event.key) { | ||
| case "ArrowDown": | ||
| setHighlightedIndex((prevIndex) => { | ||
| if (prevIndex === options.length - 1) { | ||
| return 0; | ||
| } | ||
| return prevIndex + 1; | ||
| }); | ||
| break; | ||
| case "ArrowUp": | ||
| setHighlightedIndex((prevIndex) => { | ||
| if (prevIndex === 0) { | ||
| return options.length - 1; | ||
| } | ||
| return prevIndex - 1; | ||
| }); | ||
| break; | ||
| case "Enter": { | ||
| const option = options[highlightedIndexRef.current]; | ||
| onSelect(getOptionLabel(option), option); | ||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| } | ||
| }; | ||
| document.addEventListener("keydown", onKeyDown); | ||
| return () => { | ||
| document.removeEventListener("keydown", onKeyDown); | ||
| }; | ||
| }, [ | ||
| options, | ||
| getOptionLabel, | ||
| onSelect | ||
| ]); | ||
| const { | ||
| Menu: Menu2 | ||
| } = components2; | ||
| return /* @__PURE__ */ jsx2( | ||
| Menu2, | ||
| { | ||
| ref: rootRef, | ||
| children: options.map((option, index) => /* @__PURE__ */ jsx2( | ||
| DropdownItem, | ||
| { | ||
| components: components2, | ||
| getOptionLabel, | ||
| formatOptionLabel, | ||
| option, | ||
| isHighlighted: highlightedIndex === index, | ||
| onSelect, | ||
| setHighlightedIndex, | ||
| index | ||
| }, | ||
| index | ||
| )) | ||
| } | ||
| ); | ||
| } | ||
| // src/Autocomplete.tsx | ||
| import { jsx as jsx3, jsxs } from "react/jsx-runtime"; | ||
| var emptyObj = {}; | ||
| function Autocomplete({ | ||
| loadOptions, | ||
| value, | ||
| onChange: onChangeProp = void 0, | ||
| onSelect: onSelectProp = void 0, | ||
| disabled = false, | ||
| hasError = false, | ||
| hasWarning = false, | ||
| inputProps = emptyObj, | ||
| labelKey = "label", | ||
| getOptionLabel: getOptionLabelProp = void 0, | ||
| formatOptionLabel: formatOptionLabelProp = void 0, | ||
| components: componentsProp = emptyObj | ||
| }) { | ||
| const components2 = useMemo(() => ({ | ||
| ...components, | ||
| ...componentsProp | ||
| }), [componentsProp]); | ||
| const valueRef = useRef2(value); | ||
| valueRef.current = value; | ||
| const [options, setOptions] = useState2([]); | ||
| const [isOpen, setIsOpen] = useState2(false); | ||
| const getOptionLabel = useCallback2((option) => { | ||
| if (getOptionLabelProp) { | ||
| return getOptionLabelProp(option); | ||
| } | ||
| if (typeof option === "string") { | ||
| return option; | ||
| } | ||
| if (typeof option === "number") { | ||
| return String(option); | ||
| } | ||
| if (!option || typeof option !== "object") { | ||
| return ""; | ||
| } | ||
| const label = option[labelKey]; | ||
| if (typeof label === "string") { | ||
| return label; | ||
| } | ||
| if (typeof label === "number") { | ||
| return String(label); | ||
| } | ||
| return ""; | ||
| }, [getOptionLabelProp, labelKey]); | ||
| const formatOptionLabel = useCallback2((option) => { | ||
| if (formatOptionLabelProp) { | ||
| return formatOptionLabelProp(option); | ||
| } | ||
| return getOptionLabel(option); | ||
| }, [formatOptionLabelProp, getOptionLabel]); | ||
| const onInputFocus = useCallback2(() => { | ||
| setIsOpen(true); | ||
| }, []); | ||
| const closeMenu = useCallback2(() => { | ||
| setIsOpen(false); | ||
| }, []); | ||
| const onSelect = useCallback2((label, option) => { | ||
| setIsOpen(false); | ||
| if (onSelectProp) { | ||
| onSelectProp(label, option); | ||
| } | ||
| }, [onSelectProp]); | ||
| const onChange = useCallback2((event) => { | ||
| if (onChangeProp) { | ||
| onChangeProp(event); | ||
| } | ||
| if (!isOpen) { | ||
| setIsOpen(true); | ||
| } | ||
| }, [onChangeProp, isOpen]); | ||
| useEffect2(() => { | ||
| (async () => { | ||
| const response = await loadOptions(value); | ||
| if (valueRef.current === value) { | ||
| setOptions(response.options); | ||
| } | ||
| })().catch((e) => { | ||
| throw e; | ||
| }); | ||
| }, [ | ||
| loadOptions, | ||
| value | ||
| ]); | ||
| const { | ||
| Wrapper: Wrapper2, | ||
| Input: Input2 | ||
| } = components2; | ||
| return /* @__PURE__ */ jsxs(Wrapper2, { children: [ | ||
| /* @__PURE__ */ jsx3( | ||
| Input2, | ||
| { | ||
| ...inputProps, | ||
| disabled, | ||
| $hasError: hasError, | ||
| $hasWarning: hasWarning, | ||
| value, | ||
| onChange, | ||
| onFocus: onInputFocus | ||
| } | ||
| ), | ||
| isOpen && options.length > 0 && /* @__PURE__ */ jsx3( | ||
| Dropdown, | ||
| { | ||
| components: components2, | ||
| getOptionLabel, | ||
| formatOptionLabel, | ||
| options, | ||
| onSelect, | ||
| closeMenu | ||
| } | ||
| ) | ||
| ] }); | ||
| } | ||
| export { | ||
| Autocomplete | ||
| }; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"sources":["../../src/Autocomplete.tsx","../../src/components/Wrapper.tsx","../../src/components/Input.tsx","../../src/components/Menu.tsx","../../src/components/MenuItem.tsx","../../src/components/index.ts","../../src/Dropdown.tsx","../../src/DropdownItem.tsx"],"sourcesContent":["import {\n useCallback,\n useState,\n useMemo,\n useRef,\n useEffect,\n} from 'react';\nimport type {\n ChangeEventHandler,\n HTMLProps,\n ReactElement,\n} from 'react';\n\nimport { components as defaultComponents } from './components';\nimport { Dropdown } from './Dropdown';\n\nimport type {\n Components,\n FormatOptionLabel,\n GetOptionLabel,\n LoadOptions,\n OnSelect,\n} from './types';\n\nexport type AutocompleteProps<Option> = {\n /**\n * Функция загрузки опций\n * @param {String} search - текущее значение поля ввода\n * @returns {Object[]} response.options - опции\n */\n loadOptions: LoadOptions<Option>;\n /**\n * Значение элемента input\n */\n value: string;\n /**\n * Обработчик изменения значения поля при ручном вводе\n */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /**\n * Обработчик изменения значения поля при выборе из меню\n * @param {String} value - текст выбранной опции\n * @param {Object} option - выбранная опция\n */\n onSelect?: OnSelect<Option>;\n /**\n * Выключено ли поле\n */\n disabled?: boolean;\n /**\n * Есть ли у поля ошибка\n */\n hasError?: boolean;\n /**\n * Есть ли у поля предупреждение\n */\n hasWarning?: boolean;\n\n /**\n * Дополнительные props элемента input\n */\n inputProps?: Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange' | 'disabled'>;\n\n /**\n * Ключ, по которому хранится текст опции\n */\n labelKey?: string;\n /**\n * Функция получения текста опции, который будет подставлен при выборе\n */\n getOptionLabel?: GetOptionLabel<Option>;\n /**\n * Функция отображения опции\n */\n formatOptionLabel?: FormatOptionLabel<Option>;\n /**\n * Переиспользуемые компоненты\n */\n components?: Partial<Components>;\n};\n\nconst emptyObj = {};\n\nexport function Autocomplete<Option>({\n loadOptions,\n value,\n onChange: onChangeProp = undefined,\n onSelect: onSelectProp = undefined,\n disabled = false,\n hasError = false,\n hasWarning = false,\n inputProps = emptyObj,\n labelKey = 'label',\n getOptionLabel: getOptionLabelProp = undefined,\n formatOptionLabel: formatOptionLabelProp = undefined,\n components: componentsProp = emptyObj,\n}: AutocompleteProps<Option>): ReactElement {\n const components = useMemo<Components>(() => ({\n ...defaultComponents,\n ...componentsProp,\n }), [componentsProp]);\n\n const valueRef = useRef<string>(value);\n valueRef.current = value;\n\n const [options, setOptions] = useState<Option[]>([]);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n\n const getOptionLabel = useCallback<GetOptionLabel<Option>>((option) => {\n if (getOptionLabelProp) {\n return getOptionLabelProp(option);\n }\n\n if (typeof option === 'string') {\n return option;\n }\n\n if (typeof option === 'number') {\n return String(option);\n }\n\n if (!option || typeof option !== 'object') {\n return '';\n }\n\n const label = (option as Record<string, unknown>)[labelKey];\n\n if (typeof label === 'string') {\n return label;\n }\n\n if (typeof label === 'number') {\n return String(label);\n }\n\n return '';\n }, [getOptionLabelProp, labelKey]);\n\n const formatOptionLabel = useCallback<FormatOptionLabel<Option>>((option) => {\n if (formatOptionLabelProp) {\n return formatOptionLabelProp(option);\n }\n\n return getOptionLabel(option);\n }, [formatOptionLabelProp, getOptionLabel]);\n\n const onInputFocus = useCallback(() => {\n setIsOpen(true);\n }, []);\n\n const closeMenu = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n const onSelect = useCallback<OnSelect<Option>>((label, option) => {\n setIsOpen(false);\n\n if (onSelectProp) {\n onSelectProp(label, option);\n }\n }, [onSelectProp]);\n\n const onChange = useCallback<ChangeEventHandler<HTMLInputElement>>((event) => {\n if (onChangeProp) {\n onChangeProp(event);\n }\n\n if (!isOpen) {\n setIsOpen(true);\n }\n }, [onChangeProp, isOpen]);\n\n useEffect(() => {\n (async (): Promise<void> => {\n const response = await loadOptions(value);\n\n if (valueRef.current === value) {\n setOptions(response.options);\n }\n })().catch((e) => {\n throw e;\n });\n }, [\n loadOptions,\n value,\n ]);\n\n const {\n Wrapper,\n Input,\n } = components;\n\n return (\n <Wrapper>\n <Input\n {...inputProps}\n disabled={disabled}\n $hasError={hasError}\n $hasWarning={hasWarning}\n value={value}\n onChange={onChange}\n onFocus={onInputFocus}\n />\n\n {\n isOpen && options.length > 0 && (\n <Dropdown\n components={components}\n getOptionLabel={getOptionLabel}\n formatOptionLabel={formatOptionLabel}\n options={options}\n onSelect={onSelect}\n closeMenu={closeMenu}\n />\n )\n }\n </Wrapper>\n );\n}\n","import styled from 'styled-components';\n\nimport type {\n WrapperComponent,\n} from '../types';\n\nexport const Wrapper: WrapperComponent = styled.div({\n position: 'relative',\n});\n","import styled from 'styled-components';\nimport type {\n CSSObject,\n} from 'styled-components';\n\nimport type {\n InputComponentProps,\n InputComponent,\n} from '../types';\n\nexport const Input: InputComponent = styled.input<InputComponentProps>(({\n $hasError,\n $hasWarning,\n}) => {\n const res: CSSObject = {\n width: '100%',\n boxSizing: 'border-box',\n };\n\n if ($hasError) {\n res.borderColor = '#d64c4c';\n res.backgroundColor = '#fdf6f6';\n } else if ($hasWarning) {\n res.borderColor = '#eea505';\n res.backgroundColor = '#fdf6e6';\n }\n\n return res;\n});\n","import styled from 'styled-components';\n\nimport type {\n MenuComponent,\n} from '../types';\n\nexport const Menu: MenuComponent = styled.div({\n boxSizing: 'border-box',\n position: 'absolute',\n top: '100%',\n width: '100%',\n zIndex: 1,\n marginTop: 4,\n maxHeight: 200,\n overflowY: 'auto',\n borderRadius: 4,\n backgroundColor: '#fff',\n border: '1px solid #d9e1e8',\n boxShadow: '0 1px 0 rgba(0, 0, 0, 0.06)',\n});\n","import styled from 'styled-components';\n\nimport type {\n MenuItemProps,\n MenuItemComponent,\n} from '../types';\n\nexport const MenuItem: MenuItemComponent = styled.div<MenuItemProps>(({\n $isHighlighted,\n}) => ({\n display: 'block',\n boxSizing: 'border-box',\n padding: 10,\n color: '#666',\n backgroundColor: $isHighlighted ? '#f9f9f9' : '#fff',\n cursor: 'pointer',\n}));\n","import { Wrapper } from './Wrapper';\nimport { Input } from './Input';\nimport { Menu } from './Menu';\nimport { MenuItem } from './MenuItem';\n\nimport type {\n Components,\n} from '../types';\n\nexport const components: Components = {\n Wrapper,\n Input,\n Menu,\n MenuItem,\n};\n","import {\n useRef,\n useState,\n useEffect,\n} from 'react';\nimport type {\n ReactElement,\n} from 'react';\n\nimport useOnClickOutside from 'use-onclickoutside';\n\nimport { DropdownItem } from './DropdownItem';\nimport type {\n Components,\n FormatOptionLabel,\n GetOptionLabel,\n OnSelect,\n} from './types';\n\nexport type DropdownProps<Option> = {\n components: Components;\n getOptionLabel: GetOptionLabel<Option>;\n formatOptionLabel: FormatOptionLabel<Option>;\n options: Option[];\n onSelect: OnSelect<Option>;\n closeMenu: () => void;\n};\n\nexport function Dropdown<Option>({\n components,\n getOptionLabel,\n formatOptionLabel,\n options,\n onSelect,\n closeMenu,\n}: DropdownProps<Option>): ReactElement {\n const rootRef = useRef<HTMLElement>(null);\n useOnClickOutside(rootRef, closeMenu);\n\n const isInitRef = useRef<boolean>(true);\n\n const [highlightedIndex, setHighlightedIndex] = useState<number>(0);\n\n const highlightedIndexRef = useRef<number>(highlightedIndex);\n highlightedIndexRef.current = highlightedIndex;\n\n useEffect(() => {\n if (isInitRef.current) {\n isInitRef.current = false;\n } else {\n setHighlightedIndex(0);\n }\n\n const onKeyDown = (event: KeyboardEvent): void => {\n switch (event.key) {\n case 'ArrowDown':\n setHighlightedIndex((prevIndex) => {\n if (prevIndex === options.length - 1) {\n return 0;\n }\n\n return prevIndex + 1;\n });\n break;\n\n case 'ArrowUp':\n setHighlightedIndex((prevIndex) => {\n if (prevIndex === 0) {\n return options.length - 1;\n }\n\n return prevIndex - 1;\n });\n break;\n\n case 'Enter':\n {\n const option = options[highlightedIndexRef.current];\n onSelect(getOptionLabel(option), option);\n\n break;\n }\n\n default:\n break;\n }\n };\n\n document.addEventListener('keydown', onKeyDown);\n\n return (): void => {\n document.removeEventListener('keydown', onKeyDown);\n };\n }, [\n options,\n getOptionLabel,\n onSelect,\n ]);\n\n const {\n Menu,\n } = components;\n\n return (\n <Menu\n ref={rootRef}\n >\n {\n options.map((option, index) => (\n <DropdownItem\n components={components}\n getOptionLabel={getOptionLabel}\n formatOptionLabel={formatOptionLabel}\n option={option}\n isHighlighted={highlightedIndex === index}\n onSelect={onSelect}\n setHighlightedIndex={setHighlightedIndex}\n index={index}\n key={index}\n />\n ))\n }\n </Menu>\n );\n}\n","import {\n useCallback,\n} from 'react';\nimport type {\n ReactElement,\n} from 'react';\n\nimport type {\n Components,\n FormatOptionLabel,\n GetOptionLabel,\n OnSelect,\n} from './types';\n\nexport type DropdownItemProps<Option> = {\n components: Components;\n getOptionLabel: GetOptionLabel<Option>;\n formatOptionLabel: FormatOptionLabel<Option>;\n option: Option;\n isHighlighted: boolean;\n onSelect: OnSelect<Option>;\n setHighlightedIndex: (index: number) => void;\n index: number;\n};\n\nexport function DropdownItem<Option>({\n components,\n getOptionLabel,\n formatOptionLabel,\n isHighlighted,\n option,\n onSelect,\n setHighlightedIndex,\n index,\n}: DropdownItemProps<Option>): ReactElement {\n const onClick = useCallback(() => {\n onSelect(getOptionLabel(option), option);\n }, [onSelect, option, getOptionLabel]);\n\n const onMouseEnter = useCallback(() => {\n setHighlightedIndex(index);\n }, [setHighlightedIndex, index]);\n\n const {\n MenuItem,\n } = components;\n\n return (\n <MenuItem\n $isHighlighted={isHighlighted}\n onMouseEnter={onMouseEnter}\n onClick={onClick}\n >\n {formatOptionLabel(option)}\n </MenuItem>\n );\n}\n"],"mappings":"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","names":["useCallback","useState","useRef","useEffect","styled","styled","styled","components","MenuItem","jsx","components","Menu","jsx","components","useRef","useState","useCallback","useEffect","Wrapper","Input"]} |
| import { ComponentType, PropsWithChildren, HTMLProps, Ref, ReactNode, ChangeEventHandler, ReactElement } from 'react'; | ||
| type WrapperComponent = ComponentType<PropsWithChildren>; | ||
| type InputComponentProps = HTMLProps<HTMLInputElement> & { | ||
| $hasError: boolean; | ||
| $hasWarning: boolean; | ||
| }; | ||
| type InputComponent = ComponentType<InputComponentProps>; | ||
| type MenuComponent = ComponentType<PropsWithChildren<{ | ||
| ref: Ref<HTMLElement>; | ||
| }>>; | ||
| type MenuItemProps = HTMLProps<HTMLDivElement> & { | ||
| $isHighlighted: boolean; | ||
| }; | ||
| type MenuItemComponent = ComponentType<MenuItemProps>; | ||
| type Components = { | ||
| Wrapper: WrapperComponent; | ||
| Input: InputComponent; | ||
| Menu: MenuComponent; | ||
| MenuItem: MenuItemComponent; | ||
| }; | ||
| type LoadOptionsResponse<Option> = { | ||
| options: Option[]; | ||
| }; | ||
| type LoadOptions<Option> = (inputValue: string) => LoadOptionsResponse<Option> | Promise<LoadOptionsResponse<Option>>; | ||
| type GetOptionLabel<Option> = (option: Option) => string; | ||
| type FormatOptionLabel<Option> = (Option: Option) => ReactNode; | ||
| type OnSelect<Option> = (value: string, option: Option) => void; | ||
| type AutocompleteProps<Option> = { | ||
| /** | ||
| * Функция загрузки опций | ||
| * @param {String} search - текущее значение поля ввода | ||
| * @returns {Object[]} response.options - опции | ||
| */ | ||
| loadOptions: LoadOptions<Option>; | ||
| /** | ||
| * Значение элемента input | ||
| */ | ||
| value: string; | ||
| /** | ||
| * Обработчик изменения значения поля при ручном вводе | ||
| */ | ||
| onChange?: ChangeEventHandler<HTMLInputElement>; | ||
| /** | ||
| * Обработчик изменения значения поля при выборе из меню | ||
| * @param {String} value - текст выбранной опции | ||
| * @param {Object} option - выбранная опция | ||
| */ | ||
| onSelect?: OnSelect<Option>; | ||
| /** | ||
| * Выключено ли поле | ||
| */ | ||
| disabled?: boolean; | ||
| /** | ||
| * Есть ли у поля ошибка | ||
| */ | ||
| hasError?: boolean; | ||
| /** | ||
| * Есть ли у поля предупреждение | ||
| */ | ||
| hasWarning?: boolean; | ||
| /** | ||
| * Дополнительные props элемента input | ||
| */ | ||
| inputProps?: Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange' | 'disabled'>; | ||
| /** | ||
| * Ключ, по которому хранится текст опции | ||
| */ | ||
| labelKey?: string; | ||
| /** | ||
| * Функция получения текста опции, который будет подставлен при выборе | ||
| */ | ||
| getOptionLabel?: GetOptionLabel<Option>; | ||
| /** | ||
| * Функция отображения опции | ||
| */ | ||
| formatOptionLabel?: FormatOptionLabel<Option>; | ||
| /** | ||
| * Переиспользуемые компоненты | ||
| */ | ||
| components?: Partial<Components>; | ||
| }; | ||
| declare function Autocomplete<Option>({ loadOptions, value, onChange: onChangeProp, onSelect: onSelectProp, disabled, hasError, hasWarning, inputProps, labelKey, getOptionLabel: getOptionLabelProp, formatOptionLabel: formatOptionLabelProp, components: componentsProp, }: AutocompleteProps<Option>): ReactElement; | ||
| export { Autocomplete, AutocompleteProps, Components, FormatOptionLabel, GetOptionLabel, InputComponent, InputComponentProps, LoadOptions, LoadOptionsResponse, MenuComponent, MenuItemComponent, MenuItemProps, OnSelect, WrapperComponent }; |
+344
| "use strict"; | ||
| var __create = Object.create; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __getProtoOf = Object.getPrototypeOf; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
| // If the importer is in node compatibility mode or this is not an ESM | ||
| // file that has been converted to a CommonJS file using a Babel- | ||
| // compatible transform (i.e. "__esModule" has not been set), then set | ||
| // "default" to the CommonJS "module.exports" for node compatibility. | ||
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
| mod | ||
| )); | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/index.ts | ||
| var src_exports = {}; | ||
| __export(src_exports, { | ||
| Autocomplete: () => Autocomplete | ||
| }); | ||
| module.exports = __toCommonJS(src_exports); | ||
| // src/Autocomplete.tsx | ||
| var import_react3 = require("react"); | ||
| // src/components/Wrapper.tsx | ||
| var import_styled_components = __toESM(require("styled-components")); | ||
| var Wrapper = import_styled_components.default.div({ | ||
| position: "relative" | ||
| }); | ||
| // src/components/Input.tsx | ||
| var import_styled_components2 = __toESM(require("styled-components")); | ||
| var Input = import_styled_components2.default.input(({ | ||
| $hasError, | ||
| $hasWarning | ||
| }) => { | ||
| const res = { | ||
| width: "100%", | ||
| boxSizing: "border-box" | ||
| }; | ||
| if ($hasError) { | ||
| res.borderColor = "#d64c4c"; | ||
| res.backgroundColor = "#fdf6f6"; | ||
| } else if ($hasWarning) { | ||
| res.borderColor = "#eea505"; | ||
| res.backgroundColor = "#fdf6e6"; | ||
| } | ||
| return res; | ||
| }); | ||
| // src/components/Menu.tsx | ||
| var import_styled_components3 = __toESM(require("styled-components")); | ||
| var Menu = import_styled_components3.default.div({ | ||
| boxSizing: "border-box", | ||
| position: "absolute", | ||
| top: "100%", | ||
| width: "100%", | ||
| zIndex: 1, | ||
| marginTop: 4, | ||
| maxHeight: 200, | ||
| overflowY: "auto", | ||
| borderRadius: 4, | ||
| backgroundColor: "#fff", | ||
| border: "1px solid #d9e1e8", | ||
| boxShadow: "0 1px 0 rgba(0, 0, 0, 0.06)" | ||
| }); | ||
| // src/components/MenuItem.tsx | ||
| var import_styled_components4 = __toESM(require("styled-components")); | ||
| var MenuItem = import_styled_components4.default.div(({ | ||
| $isHighlighted | ||
| }) => ({ | ||
| display: "block", | ||
| boxSizing: "border-box", | ||
| padding: 10, | ||
| color: "#666", | ||
| backgroundColor: $isHighlighted ? "#f9f9f9" : "#fff", | ||
| cursor: "pointer" | ||
| })); | ||
| // src/components/index.ts | ||
| var components = { | ||
| Wrapper, | ||
| Input, | ||
| Menu, | ||
| MenuItem | ||
| }; | ||
| // src/Dropdown.tsx | ||
| var import_react2 = require("react"); | ||
| var import_use_onclickoutside = __toESM(require("use-onclickoutside")); | ||
| // src/DropdownItem.tsx | ||
| var import_react = require("react"); | ||
| var import_jsx_runtime = require("react/jsx-runtime"); | ||
| function DropdownItem({ | ||
| components: components2, | ||
| getOptionLabel, | ||
| formatOptionLabel, | ||
| isHighlighted, | ||
| option, | ||
| onSelect, | ||
| setHighlightedIndex, | ||
| index | ||
| }) { | ||
| const onClick = (0, import_react.useCallback)(() => { | ||
| onSelect(getOptionLabel(option), option); | ||
| }, [onSelect, option, getOptionLabel]); | ||
| const onMouseEnter = (0, import_react.useCallback)(() => { | ||
| setHighlightedIndex(index); | ||
| }, [setHighlightedIndex, index]); | ||
| const { | ||
| MenuItem: MenuItem2 | ||
| } = components2; | ||
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | ||
| MenuItem2, | ||
| { | ||
| $isHighlighted: isHighlighted, | ||
| onMouseEnter, | ||
| onClick, | ||
| children: formatOptionLabel(option) | ||
| } | ||
| ); | ||
| } | ||
| // src/Dropdown.tsx | ||
| var import_jsx_runtime2 = require("react/jsx-runtime"); | ||
| function Dropdown({ | ||
| components: components2, | ||
| getOptionLabel, | ||
| formatOptionLabel, | ||
| options, | ||
| onSelect, | ||
| closeMenu | ||
| }) { | ||
| const rootRef = (0, import_react2.useRef)(null); | ||
| (0, import_use_onclickoutside.default)(rootRef, closeMenu); | ||
| const isInitRef = (0, import_react2.useRef)(true); | ||
| const [highlightedIndex, setHighlightedIndex] = (0, import_react2.useState)(0); | ||
| const highlightedIndexRef = (0, import_react2.useRef)(highlightedIndex); | ||
| highlightedIndexRef.current = highlightedIndex; | ||
| (0, import_react2.useEffect)(() => { | ||
| if (isInitRef.current) { | ||
| isInitRef.current = false; | ||
| } else { | ||
| setHighlightedIndex(0); | ||
| } | ||
| const onKeyDown = (event) => { | ||
| switch (event.key) { | ||
| case "ArrowDown": | ||
| setHighlightedIndex((prevIndex) => { | ||
| if (prevIndex === options.length - 1) { | ||
| return 0; | ||
| } | ||
| return prevIndex + 1; | ||
| }); | ||
| break; | ||
| case "ArrowUp": | ||
| setHighlightedIndex((prevIndex) => { | ||
| if (prevIndex === 0) { | ||
| return options.length - 1; | ||
| } | ||
| return prevIndex - 1; | ||
| }); | ||
| break; | ||
| case "Enter": { | ||
| const option = options[highlightedIndexRef.current]; | ||
| onSelect(getOptionLabel(option), option); | ||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| } | ||
| }; | ||
| document.addEventListener("keydown", onKeyDown); | ||
| return () => { | ||
| document.removeEventListener("keydown", onKeyDown); | ||
| }; | ||
| }, [ | ||
| options, | ||
| getOptionLabel, | ||
| onSelect | ||
| ]); | ||
| const { | ||
| Menu: Menu2 | ||
| } = components2; | ||
| return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( | ||
| Menu2, | ||
| { | ||
| ref: rootRef, | ||
| children: options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( | ||
| DropdownItem, | ||
| { | ||
| components: components2, | ||
| getOptionLabel, | ||
| formatOptionLabel, | ||
| option, | ||
| isHighlighted: highlightedIndex === index, | ||
| onSelect, | ||
| setHighlightedIndex, | ||
| index | ||
| }, | ||
| index | ||
| )) | ||
| } | ||
| ); | ||
| } | ||
| // src/Autocomplete.tsx | ||
| var import_jsx_runtime3 = require("react/jsx-runtime"); | ||
| var emptyObj = {}; | ||
| function Autocomplete({ | ||
| loadOptions, | ||
| value, | ||
| onChange: onChangeProp = void 0, | ||
| onSelect: onSelectProp = void 0, | ||
| disabled = false, | ||
| hasError = false, | ||
| hasWarning = false, | ||
| inputProps = emptyObj, | ||
| labelKey = "label", | ||
| getOptionLabel: getOptionLabelProp = void 0, | ||
| formatOptionLabel: formatOptionLabelProp = void 0, | ||
| components: componentsProp = emptyObj | ||
| }) { | ||
| const components2 = (0, import_react3.useMemo)(() => ({ | ||
| ...components, | ||
| ...componentsProp | ||
| }), [componentsProp]); | ||
| const valueRef = (0, import_react3.useRef)(value); | ||
| valueRef.current = value; | ||
| const [options, setOptions] = (0, import_react3.useState)([]); | ||
| const [isOpen, setIsOpen] = (0, import_react3.useState)(false); | ||
| const getOptionLabel = (0, import_react3.useCallback)((option) => { | ||
| if (getOptionLabelProp) { | ||
| return getOptionLabelProp(option); | ||
| } | ||
| if (typeof option === "string") { | ||
| return option; | ||
| } | ||
| if (typeof option === "number") { | ||
| return String(option); | ||
| } | ||
| if (!option || typeof option !== "object") { | ||
| return ""; | ||
| } | ||
| const label = option[labelKey]; | ||
| if (typeof label === "string") { | ||
| return label; | ||
| } | ||
| if (typeof label === "number") { | ||
| return String(label); | ||
| } | ||
| return ""; | ||
| }, [getOptionLabelProp, labelKey]); | ||
| const formatOptionLabel = (0, import_react3.useCallback)((option) => { | ||
| if (formatOptionLabelProp) { | ||
| return formatOptionLabelProp(option); | ||
| } | ||
| return getOptionLabel(option); | ||
| }, [formatOptionLabelProp, getOptionLabel]); | ||
| const onInputFocus = (0, import_react3.useCallback)(() => { | ||
| setIsOpen(true); | ||
| }, []); | ||
| const closeMenu = (0, import_react3.useCallback)(() => { | ||
| setIsOpen(false); | ||
| }, []); | ||
| const onSelect = (0, import_react3.useCallback)((label, option) => { | ||
| setIsOpen(false); | ||
| if (onSelectProp) { | ||
| onSelectProp(label, option); | ||
| } | ||
| }, [onSelectProp]); | ||
| const onChange = (0, import_react3.useCallback)((event) => { | ||
| if (onChangeProp) { | ||
| onChangeProp(event); | ||
| } | ||
| if (!isOpen) { | ||
| setIsOpen(true); | ||
| } | ||
| }, [onChangeProp, isOpen]); | ||
| (0, import_react3.useEffect)(() => { | ||
| (async () => { | ||
| const response = await loadOptions(value); | ||
| if (valueRef.current === value) { | ||
| setOptions(response.options); | ||
| } | ||
| })().catch((e) => { | ||
| throw e; | ||
| }); | ||
| }, [ | ||
| loadOptions, | ||
| value | ||
| ]); | ||
| const { | ||
| Wrapper: Wrapper2, | ||
| Input: Input2 | ||
| } = components2; | ||
| return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Wrapper2, { children: [ | ||
| /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( | ||
| Input2, | ||
| { | ||
| ...inputProps, | ||
| disabled, | ||
| $hasError: hasError, | ||
| $hasWarning: hasWarning, | ||
| value, | ||
| onChange, | ||
| onFocus: onInputFocus | ||
| } | ||
| ), | ||
| isOpen && options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( | ||
| Dropdown, | ||
| { | ||
| components: components2, | ||
| getOptionLabel, | ||
| formatOptionLabel, | ||
| options, | ||
| onSelect, | ||
| closeMenu | ||
| } | ||
| ) | ||
| ] }); | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| Autocomplete | ||
| }); | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"sources":["../src/index.ts","../src/Autocomplete.tsx","../src/components/Wrapper.tsx","../src/components/Input.tsx","../src/components/Menu.tsx","../src/components/MenuItem.tsx","../src/components/index.ts","../src/Dropdown.tsx","../src/DropdownItem.tsx"],"sourcesContent":["export { Autocomplete } from './Autocomplete';\nexport type {\n AutocompleteProps,\n} from './Autocomplete';\n\nexport * from './types';\n","import {\n useCallback,\n useState,\n useMemo,\n useRef,\n useEffect,\n} from 'react';\nimport type {\n ChangeEventHandler,\n HTMLProps,\n ReactElement,\n} from 'react';\n\nimport { components as defaultComponents } from './components';\nimport { Dropdown } from './Dropdown';\n\nimport type {\n Components,\n FormatOptionLabel,\n GetOptionLabel,\n LoadOptions,\n OnSelect,\n} from './types';\n\nexport type AutocompleteProps<Option> = {\n /**\n * Функция загрузки опций\n * @param {String} search - текущее значение поля ввода\n * @returns {Object[]} response.options - опции\n */\n loadOptions: LoadOptions<Option>;\n /**\n * Значение элемента input\n */\n value: string;\n /**\n * Обработчик изменения значения поля при ручном вводе\n */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /**\n * Обработчик изменения значения поля при выборе из меню\n * @param {String} value - текст выбранной опции\n * @param {Object} option - выбранная опция\n */\n onSelect?: OnSelect<Option>;\n /**\n * Выключено ли поле\n */\n disabled?: boolean;\n /**\n * Есть ли у поля ошибка\n */\n hasError?: boolean;\n /**\n * Есть ли у поля предупреждение\n */\n hasWarning?: boolean;\n\n /**\n * Дополнительные props элемента input\n */\n inputProps?: Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange' | 'disabled'>;\n\n /**\n * Ключ, по которому хранится текст опции\n */\n labelKey?: string;\n /**\n * Функция получения текста опции, который будет подставлен при выборе\n */\n getOptionLabel?: GetOptionLabel<Option>;\n /**\n * Функция отображения опции\n */\n formatOptionLabel?: FormatOptionLabel<Option>;\n /**\n * Переиспользуемые компоненты\n */\n components?: Partial<Components>;\n};\n\nconst emptyObj = {};\n\nexport function Autocomplete<Option>({\n loadOptions,\n value,\n onChange: onChangeProp = undefined,\n onSelect: onSelectProp = undefined,\n disabled = false,\n hasError = false,\n hasWarning = false,\n inputProps = emptyObj,\n labelKey = 'label',\n getOptionLabel: getOptionLabelProp = undefined,\n formatOptionLabel: formatOptionLabelProp = undefined,\n components: componentsProp = emptyObj,\n}: AutocompleteProps<Option>): ReactElement {\n const components = useMemo<Components>(() => ({\n ...defaultComponents,\n ...componentsProp,\n }), [componentsProp]);\n\n const valueRef = useRef<string>(value);\n valueRef.current = value;\n\n const [options, setOptions] = useState<Option[]>([]);\n const [isOpen, setIsOpen] = useState<boolean>(false);\n\n const getOptionLabel = useCallback<GetOptionLabel<Option>>((option) => {\n if (getOptionLabelProp) {\n return getOptionLabelProp(option);\n }\n\n if (typeof option === 'string') {\n return option;\n }\n\n if (typeof option === 'number') {\n return String(option);\n }\n\n if (!option || typeof option !== 'object') {\n return '';\n }\n\n const label = (option as Record<string, unknown>)[labelKey];\n\n if (typeof label === 'string') {\n return label;\n }\n\n if (typeof label === 'number') {\n return String(label);\n }\n\n return '';\n }, [getOptionLabelProp, labelKey]);\n\n const formatOptionLabel = useCallback<FormatOptionLabel<Option>>((option) => {\n if (formatOptionLabelProp) {\n return formatOptionLabelProp(option);\n }\n\n return getOptionLabel(option);\n }, [formatOptionLabelProp, getOptionLabel]);\n\n const onInputFocus = useCallback(() => {\n setIsOpen(true);\n }, []);\n\n const closeMenu = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n const onSelect = useCallback<OnSelect<Option>>((label, option) => {\n setIsOpen(false);\n\n if (onSelectProp) {\n onSelectProp(label, option);\n }\n }, [onSelectProp]);\n\n const onChange = useCallback<ChangeEventHandler<HTMLInputElement>>((event) => {\n if (onChangeProp) {\n onChangeProp(event);\n }\n\n if (!isOpen) {\n setIsOpen(true);\n }\n }, [onChangeProp, isOpen]);\n\n useEffect(() => {\n (async (): Promise<void> => {\n const response = await loadOptions(value);\n\n if (valueRef.current === value) {\n setOptions(response.options);\n }\n })().catch((e) => {\n throw e;\n });\n }, [\n loadOptions,\n value,\n ]);\n\n const {\n Wrapper,\n Input,\n } = components;\n\n return (\n <Wrapper>\n <Input\n {...inputProps}\n disabled={disabled}\n $hasError={hasError}\n $hasWarning={hasWarning}\n value={value}\n onChange={onChange}\n onFocus={onInputFocus}\n />\n\n {\n isOpen && options.length > 0 && (\n <Dropdown\n components={components}\n getOptionLabel={getOptionLabel}\n formatOptionLabel={formatOptionLabel}\n options={options}\n onSelect={onSelect}\n closeMenu={closeMenu}\n />\n )\n }\n </Wrapper>\n );\n}\n","import styled from 'styled-components';\n\nimport type {\n WrapperComponent,\n} from '../types';\n\nexport const Wrapper: WrapperComponent = styled.div({\n position: 'relative',\n});\n","import styled from 'styled-components';\nimport type {\n CSSObject,\n} from 'styled-components';\n\nimport type {\n InputComponentProps,\n InputComponent,\n} from '../types';\n\nexport const Input: InputComponent = styled.input<InputComponentProps>(({\n $hasError,\n $hasWarning,\n}) => {\n const res: CSSObject = {\n width: '100%',\n boxSizing: 'border-box',\n };\n\n if ($hasError) {\n res.borderColor = '#d64c4c';\n res.backgroundColor = '#fdf6f6';\n } else if ($hasWarning) {\n res.borderColor = '#eea505';\n res.backgroundColor = '#fdf6e6';\n }\n\n return res;\n});\n","import styled from 'styled-components';\n\nimport type {\n MenuComponent,\n} from '../types';\n\nexport const Menu: MenuComponent = styled.div({\n boxSizing: 'border-box',\n position: 'absolute',\n top: '100%',\n width: '100%',\n zIndex: 1,\n marginTop: 4,\n maxHeight: 200,\n overflowY: 'auto',\n borderRadius: 4,\n backgroundColor: '#fff',\n border: '1px solid #d9e1e8',\n boxShadow: '0 1px 0 rgba(0, 0, 0, 0.06)',\n});\n","import styled from 'styled-components';\n\nimport type {\n MenuItemProps,\n MenuItemComponent,\n} from '../types';\n\nexport const MenuItem: MenuItemComponent = styled.div<MenuItemProps>(({\n $isHighlighted,\n}) => ({\n display: 'block',\n boxSizing: 'border-box',\n padding: 10,\n color: '#666',\n backgroundColor: $isHighlighted ? '#f9f9f9' : '#fff',\n cursor: 'pointer',\n}));\n","import { Wrapper } from './Wrapper';\nimport { Input } from './Input';\nimport { Menu } from './Menu';\nimport { MenuItem } from './MenuItem';\n\nimport type {\n Components,\n} from '../types';\n\nexport const components: Components = {\n Wrapper,\n Input,\n Menu,\n MenuItem,\n};\n","import {\n useRef,\n useState,\n useEffect,\n} from 'react';\nimport type {\n ReactElement,\n} from 'react';\n\nimport useOnClickOutside from 'use-onclickoutside';\n\nimport { DropdownItem } from './DropdownItem';\nimport type {\n Components,\n FormatOptionLabel,\n GetOptionLabel,\n OnSelect,\n} from './types';\n\nexport type DropdownProps<Option> = {\n components: Components;\n getOptionLabel: GetOptionLabel<Option>;\n formatOptionLabel: FormatOptionLabel<Option>;\n options: Option[];\n onSelect: OnSelect<Option>;\n closeMenu: () => void;\n};\n\nexport function Dropdown<Option>({\n components,\n getOptionLabel,\n formatOptionLabel,\n options,\n onSelect,\n closeMenu,\n}: DropdownProps<Option>): ReactElement {\n const rootRef = useRef<HTMLElement>(null);\n useOnClickOutside(rootRef, closeMenu);\n\n const isInitRef = useRef<boolean>(true);\n\n const [highlightedIndex, setHighlightedIndex] = useState<number>(0);\n\n const highlightedIndexRef = useRef<number>(highlightedIndex);\n highlightedIndexRef.current = highlightedIndex;\n\n useEffect(() => {\n if (isInitRef.current) {\n isInitRef.current = false;\n } else {\n setHighlightedIndex(0);\n }\n\n const onKeyDown = (event: KeyboardEvent): void => {\n switch (event.key) {\n case 'ArrowDown':\n setHighlightedIndex((prevIndex) => {\n if (prevIndex === options.length - 1) {\n return 0;\n }\n\n return prevIndex + 1;\n });\n break;\n\n case 'ArrowUp':\n setHighlightedIndex((prevIndex) => {\n if (prevIndex === 0) {\n return options.length - 1;\n }\n\n return prevIndex - 1;\n });\n break;\n\n case 'Enter':\n {\n const option = options[highlightedIndexRef.current];\n onSelect(getOptionLabel(option), option);\n\n break;\n }\n\n default:\n break;\n }\n };\n\n document.addEventListener('keydown', onKeyDown);\n\n return (): void => {\n document.removeEventListener('keydown', onKeyDown);\n };\n }, [\n options,\n getOptionLabel,\n onSelect,\n ]);\n\n const {\n Menu,\n } = components;\n\n return (\n <Menu\n ref={rootRef}\n >\n {\n options.map((option, index) => (\n <DropdownItem\n components={components}\n getOptionLabel={getOptionLabel}\n formatOptionLabel={formatOptionLabel}\n option={option}\n isHighlighted={highlightedIndex === index}\n onSelect={onSelect}\n setHighlightedIndex={setHighlightedIndex}\n index={index}\n key={index}\n />\n ))\n }\n </Menu>\n );\n}\n","import {\n useCallback,\n} from 'react';\nimport type {\n ReactElement,\n} from 'react';\n\nimport type {\n Components,\n FormatOptionLabel,\n GetOptionLabel,\n OnSelect,\n} from './types';\n\nexport type DropdownItemProps<Option> = {\n components: Components;\n getOptionLabel: GetOptionLabel<Option>;\n formatOptionLabel: FormatOptionLabel<Option>;\n option: Option;\n isHighlighted: boolean;\n onSelect: OnSelect<Option>;\n setHighlightedIndex: (index: number) => void;\n index: number;\n};\n\nexport function DropdownItem<Option>({\n components,\n getOptionLabel,\n formatOptionLabel,\n isHighlighted,\n option,\n onSelect,\n setHighlightedIndex,\n index,\n}: DropdownItemProps<Option>): ReactElement {\n const onClick = useCallback(() => {\n onSelect(getOptionLabel(option), option);\n }, [onSelect, option, getOptionLabel]);\n\n const onMouseEnter = useCallback(() => {\n setHighlightedIndex(index);\n }, [setHighlightedIndex, index]);\n\n const {\n MenuItem,\n } = components;\n\n return (\n <MenuItem\n $isHighlighted={isHighlighted}\n onMouseEnter={onMouseEnter}\n onClick={onClick}\n >\n {formatOptionLabel(option)}\n </MenuItem>\n );\n}\n"],"mappings":"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","names":["import_react","styled","import_styled_components","styled","import_styled_components","styled","import_styled_components","styled","import_react","components","MenuItem","import_jsx_runtime","components","useOnClickOutside","Menu","import_jsx_runtime","components","Wrapper","Input"]} |
+56
-47
| { | ||
| "name": "@n3/react-autocomplete", | ||
| "version": "0.2.0", | ||
| "version": "1.0.0", | ||
| "description": "Autocomplete component for react applications", | ||
| "main": "lib/index.js", | ||
| "module": "es/index.js", | ||
| "typings": "ts/index.d.ts", | ||
| "main": "./dist/index.js", | ||
| "module": "./dist/esm/index.js", | ||
| "typings": "dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "import": "./dist/esm/index.js", | ||
| "require": "./dist/index.js", | ||
| "types": "./dist/index.d.ts" | ||
| } | ||
| }, | ||
| "files": [ | ||
| "/es", | ||
| "/lib", | ||
| "/ts" | ||
| "/dist" | ||
| ], | ||
@@ -23,53 +28,57 @@ "repository": "git@gitlab.develop.netrika.ru:web/frontend/n3-react-autocomplete.git", | ||
| "scripts": { | ||
| "clean": "rimraf es lib ts", | ||
| "start": "BABEL_ENV=es start-storybook", | ||
| "build": "yarn build:cjs && yarn build:es && yarn build:ts", | ||
| "build:cjs": "cross-env BABEL_ENV=cjs babel --extensions '.ts,.tsx' src --out-dir lib --ignore \"src/**/__tests__\",\"src/**/__stories__\"", | ||
| "build:es": "cross-env BABEL_ENV=es babel --extensions '.ts,.tsx' src --out-dir es --ignore \"src/**/__tests__\",\"src/**/__stories__\"", | ||
| "build:ts": "tsc --declaration --emitDeclarationOnly", | ||
| "clean": "rimraf dist", | ||
| "start": "start-storybook", | ||
| "build": "tsup src/index.ts --sourcemap --format esm,cjs --dts --legacy-output", | ||
| "test:ts": "tsc -p ./tsconfig.validate.json --noEmit", | ||
| "lint": "eslint src --ext .ts,.tsx", | ||
| "test": "yarn lint && yarn test:ts", | ||
| "test:unit": "jest", | ||
| "prepare": "yarn clean && yarn build" | ||
| }, | ||
| "peerDependencies": { | ||
| "react": "^16.0.0" | ||
| "react": "^17.0.0 || ^18.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@babel/cli": "^7.10.1", | ||
| "@babel/core": "^7.10.2", | ||
| "@babel/plugin-transform-runtime": "^7.10.1", | ||
| "@babel/preset-env": "^7.10.2", | ||
| "@babel/preset-react": "^7.10.1", | ||
| "@babel/preset-typescript": "^7.10.1", | ||
| "@n3/eslint-config": "^0.6.0", | ||
| "@storybook/addon-knobs": "^5.3.19", | ||
| "@storybook/react": "^5.3.19", | ||
| "@types/react": "^16.9.35", | ||
| "babel-jest": "^26.0.1", | ||
| "babel-loader": "^8.1.0", | ||
| "cross-env": "^7.0.2", | ||
| "enzyme": "^3.11.0", | ||
| "enzyme-adapter-react-16": "^1.15.2", | ||
| "jest-cli": "^26.0.1", | ||
| "react": "^16.13.1", | ||
| "react-dom": "^16.13.1", | ||
| "rimraf": "^3.0.2", | ||
| "typescript": "^3.9.3" | ||
| "@babel/types": "^7.21.2", | ||
| "@mdx-js/react": "^2.3.0", | ||
| "@n3/eslint-config": "^0.13.3", | ||
| "@storybook/addon-controls": "^6.5.16", | ||
| "@storybook/addon-docs": "^6.5.16", | ||
| "@storybook/addons": "^6.5.16", | ||
| "@storybook/builder-vite": "^0.4.2", | ||
| "@storybook/channel-postmessage": "^6.5.16", | ||
| "@storybook/channel-websocket": "^6.5.16", | ||
| "@storybook/client-api": "^6.5.16", | ||
| "@storybook/mdx2-csf": "^0.0.4", | ||
| "@storybook/node-logger": "^6.5.16", | ||
| "@storybook/preset-create-react-app": "^4.1.2", | ||
| "@storybook/preview-web": "^6.5.16", | ||
| "@storybook/react": "^6.5.16", | ||
| "@types/react": "^18.0.28", | ||
| "@types/styled-components": "^5.1.26", | ||
| "@typescript-eslint/eslint-plugin": "^5.54.0", | ||
| "@typescript-eslint/parser": "^5.54.0", | ||
| "@vitejs/plugin-react-swc": "^3.2.0", | ||
| "eslint": "^8.35.0", | ||
| "eslint-config-airbnb": "^19.0.4", | ||
| "eslint-config-airbnb-base": "^15.0.0", | ||
| "eslint-config-airbnb-typescript": "^17.0.0", | ||
| "eslint-import-resolver-typescript": "^3.5.3", | ||
| "eslint-plugin-import": "^2.27.5", | ||
| "eslint-plugin-jest": "^27.2.1", | ||
| "eslint-plugin-jsx-a11y": "^6.7.1", | ||
| "eslint-plugin-react": "^7.32.2", | ||
| "eslint-plugin-react-hooks": "^4.6.0", | ||
| "react": "^18.2.0", | ||
| "react-dom": "^18.2.0", | ||
| "react-is": "^18.2.0", | ||
| "react-refresh": "^0.14.0", | ||
| "rimraf": "^4.1.2", | ||
| "tsup": "^6.6.3", | ||
| "typescript": "^4.9.5", | ||
| "vite": "^4.1.4" | ||
| }, | ||
| "dependencies": { | ||
| "@babel/runtime": "^7.10.2", | ||
| "styled-components": "^5.1.1", | ||
| "use-onclickoutside": "^0.3.1" | ||
| }, | ||
| "jest": { | ||
| "modulePaths": [ | ||
| "node_modules", | ||
| "src" | ||
| ], | ||
| "setupFiles": [ | ||
| "./setup-jest.js" | ||
| ] | ||
| "styled-components": "^5.3.6", | ||
| "use-onclickoutside": "^0.4.1" | ||
| } | ||
| } |
+16
-0
@@ -58,1 +58,17 @@ # @n3/react-autocomplete | ||
| - `MenuItem` | ||
| ## Локальная разработка | ||
| Репозиторий использует стабильную версию [yarn](https://yarnpkg.com/getting-started). | ||
| [Инструкция по установке](https://yarnpkg.com/getting-started/install). | ||
| ### Команды | ||
| - `yarn build` - сборка; | ||
| - `yarn clean` - удалить все собранне файлы; | ||
| - `yarn test` - валидация кода; | ||
| - `yarn start` - запуск [storybook](https://storybook.js.org/) с примерами. |
-15
| ## 0.2.0 | ||
| ### Улучшения | ||
| * Миграция на `typescript` | ||
| * Отказ от `react-autocomplete` | ||
| * Миграция на переопределяемые компоненты | ||
| ### Несовместимые изменения и миграция | ||
| * Замена `getItemValue` на `getOptionLabel` | ||
| * Замена `valueRenderer` на `formatOptionLabel` | ||
| * Отказ от `onChangeValue`, вместо него должно использоваться обычное `onChange` input-элемента | ||
| * Отказ от свойств `className`, `wrapperClassName`, `inputClassName`, `disabledInputClassName`, `errorInputClassName`, `warningInputClassName`, `menuClassName`, `menuItemClassName`, `menuItemHighlightedClassName` | ||
| * Отказ от свойств `valueKey` и `uniqKey` |
| import _extends from "@babel/runtime/helpers/extends"; | ||
| import _regeneratorRuntime from "@babel/runtime/regenerator"; | ||
| import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; | ||
| import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; | ||
| import _objectSpread from "@babel/runtime/helpers/objectSpread2"; | ||
| import React, { useCallback, useState, useMemo, useRef, useEffect } from 'react'; | ||
| import defaultComponents from './components'; | ||
| import Dropdown from './Dropdown'; | ||
| var Autocomplete = function Autocomplete(_ref) { | ||
| var loadOptions = _ref.loadOptions, | ||
| value = _ref.value, | ||
| onChangeProp = _ref.onChange, | ||
| onSelectProp = _ref.onSelect, | ||
| disabled = _ref.disabled, | ||
| hasError = _ref.hasError, | ||
| hasWarning = _ref.hasWarning, | ||
| inputProps = _ref.inputProps, | ||
| labelKey = _ref.labelKey, | ||
| getOptionLabelProp = _ref.getOptionLabel, | ||
| formatOptionLabelProp = _ref.formatOptionLabel, | ||
| componentsProp = _ref.components; | ||
| var components = useMemo(function () { | ||
| return _objectSpread(_objectSpread({}, defaultComponents), componentsProp); | ||
| }, [componentsProp]); | ||
| var valueRef = useRef(value); | ||
| valueRef.current = value; | ||
| var _useState = useState([]), | ||
| _useState2 = _slicedToArray(_useState, 2), | ||
| options = _useState2[0], | ||
| setOptions = _useState2[1]; | ||
| var _useState3 = useState(false), | ||
| _useState4 = _slicedToArray(_useState3, 2), | ||
| isOpen = _useState4[0], | ||
| setIsOpen = _useState4[1]; | ||
| var getOptionLabel = useCallback(function (option) { | ||
| if (getOptionLabelProp) { | ||
| return getOptionLabelProp(option); | ||
| } | ||
| return option[labelKey]; | ||
| }, [getOptionLabelProp, labelKey]); | ||
| var formatOptionLabel = useCallback(function (option) { | ||
| if (formatOptionLabelProp) { | ||
| return formatOptionLabelProp(option); | ||
| } | ||
| return getOptionLabel(option); | ||
| }, [formatOptionLabelProp, getOptionLabel]); | ||
| var onInputFocus = useCallback(function () { | ||
| setIsOpen(true); | ||
| }, []); | ||
| var closeMenu = useCallback(function () { | ||
| setIsOpen(false); | ||
| }, []); | ||
| var onSelect = useCallback(function (label, option) { | ||
| setIsOpen(false); | ||
| onSelectProp(label, option); | ||
| }, [onSelectProp]); | ||
| var onChange = useCallback(function (event) { | ||
| onChangeProp(event); | ||
| if (!isOpen) { | ||
| setIsOpen(true); | ||
| } | ||
| }, [onChangeProp, isOpen]); | ||
| useEffect(function () { | ||
| _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() { | ||
| var response; | ||
| return _regeneratorRuntime.wrap(function _callee$(_context) { | ||
| while (1) { | ||
| switch (_context.prev = _context.next) { | ||
| case 0: | ||
| _context.next = 2; | ||
| return loadOptions(value); | ||
| case 2: | ||
| response = _context.sent; | ||
| if (valueRef.current === value) { | ||
| setOptions(response.options); | ||
| } | ||
| case 4: | ||
| case "end": | ||
| return _context.stop(); | ||
| } | ||
| } | ||
| }, _callee); | ||
| }))(); // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [value]); | ||
| var Wrapper = components.Wrapper, | ||
| Input = components.Input; | ||
| return /*#__PURE__*/React.createElement(Wrapper, null, /*#__PURE__*/React.createElement(Input, _extends({}, inputProps, { | ||
| disabled: disabled, | ||
| $hasError: hasError, | ||
| $hasWarning: hasWarning, | ||
| value: value, | ||
| onChange: onChange, | ||
| onFocus: onInputFocus | ||
| })), isOpen && options.length > 0 && /*#__PURE__*/React.createElement(Dropdown, { | ||
| components: components, | ||
| getOptionLabel: getOptionLabel, | ||
| formatOptionLabel: formatOptionLabel, | ||
| options: options, | ||
| onSelect: onSelect, | ||
| closeMenu: closeMenu | ||
| })); | ||
| }; | ||
| Autocomplete.defaultProps = { | ||
| onChange: function onChange() {}, | ||
| onSelect: function onSelect() {}, | ||
| disabled: false, | ||
| hasError: false, | ||
| hasWarning: false, | ||
| inputProps: {}, | ||
| labelKey: 'label', | ||
| getOptionLabel: null, | ||
| formatOptionLabel: null, | ||
| components: {} | ||
| }; | ||
| export default Autocomplete; |
| import Wrapper from './Wrapper'; | ||
| import Input from './Input'; | ||
| import Menu from './Menu'; | ||
| import MenuItem from './MenuItem'; | ||
| var components = { | ||
| Wrapper: Wrapper, | ||
| Input: Input, | ||
| Menu: Menu, | ||
| MenuItem: MenuItem | ||
| }; | ||
| export default components; |
| import styled from 'styled-components'; | ||
| var Input = styled.input(function (_ref) { | ||
| var $hasError = _ref.$hasError, | ||
| $hasWarning = _ref.$hasWarning; | ||
| var res = { | ||
| width: '100%', | ||
| boxSizing: 'border-box' | ||
| }; | ||
| if ($hasError) { | ||
| res.borderColor = '#eea505'; | ||
| res.backgroundColor = '#fdf6e6'; | ||
| } else if ($hasWarning) { | ||
| res.borderColor = '#d64c4c'; | ||
| res.backgroundColor = '#fdf6f6'; | ||
| } | ||
| return res; | ||
| }); | ||
| export default Input; |
| import styled from 'styled-components'; | ||
| var Menu = styled.div({ | ||
| boxSizing: 'border-box', | ||
| position: 'absolute', | ||
| top: '100%', | ||
| width: '100%', | ||
| zIndex: 1, | ||
| marginTop: 4, | ||
| maxHeight: 200, | ||
| overflowY: 'auto', | ||
| borderRadius: 4, | ||
| backgroundColor: '#fff', | ||
| border: '1px solid #d9e1e8', | ||
| boxShadow: '0 1px 0 rgba(0, 0, 0, 0.06)' | ||
| }); | ||
| export default Menu; |
| import styled from 'styled-components'; | ||
| var MenuItem = styled.div(function (_ref) { | ||
| var $isHighlighted = _ref.$isHighlighted; | ||
| return { | ||
| display: 'block', | ||
| boxSizing: 'border-box', | ||
| padding: 10, | ||
| color: '#666', | ||
| backgroundColor: $isHighlighted ? '#f9f9f9' : '#fff', | ||
| cursor: 'pointer' | ||
| }; | ||
| }); | ||
| export default MenuItem; |
| import styled from 'styled-components'; | ||
| var Wrapper = styled.div({ | ||
| position: 'relative' | ||
| }); | ||
| export default Wrapper; |
| import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; | ||
| import React, { useRef, useState, useEffect } from 'react'; | ||
| import useOnClickOutside from 'use-onclickoutside'; | ||
| import DropdownItem from './DropdownItem'; | ||
| var Dropdown = function Dropdown(_ref) { | ||
| var components = _ref.components, | ||
| getOptionLabel = _ref.getOptionLabel, | ||
| formatOptionLabel = _ref.formatOptionLabel, | ||
| options = _ref.options, | ||
| onSelect = _ref.onSelect, | ||
| closeMenu = _ref.closeMenu; | ||
| var rootRef = useRef(null); | ||
| useOnClickOutside(rootRef, closeMenu); | ||
| var isInitRef = useRef(true); | ||
| var _useState = useState(0), | ||
| _useState2 = _slicedToArray(_useState, 2), | ||
| highlightedIndex = _useState2[0], | ||
| setHighlightedIndex = _useState2[1]; | ||
| var highlightedIndexRef = useRef(highlightedIndex); | ||
| highlightedIndexRef.current = highlightedIndex; | ||
| useEffect(function () { | ||
| if (isInitRef.current) { | ||
| isInitRef.current = false; | ||
| } else { | ||
| setHighlightedIndex(0); | ||
| } | ||
| var onKeyDown = function onKeyDown(event) { | ||
| switch (event.key) { | ||
| case 'ArrowDown': | ||
| setHighlightedIndex(function (prevIndex) { | ||
| if (prevIndex === options.length - 1) { | ||
| return 0; | ||
| } | ||
| return prevIndex + 1; | ||
| }); | ||
| break; | ||
| case 'ArrowUp': | ||
| setHighlightedIndex(function (prevIndex) { | ||
| if (prevIndex === 0) { | ||
| return options.length - 1; | ||
| } | ||
| return prevIndex - 1; | ||
| }); | ||
| break; | ||
| case 'Enter': | ||
| { | ||
| var option = options[highlightedIndexRef.current]; | ||
| onSelect(getOptionLabel(option), option); | ||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| } | ||
| }; | ||
| document.addEventListener('keydown', onKeyDown); | ||
| return function () { | ||
| document.removeEventListener('keydown', onKeyDown); | ||
| }; | ||
| }, [options, getOptionLabel, onSelect]); | ||
| var Menu = components.Menu; | ||
| return /*#__PURE__*/React.createElement(Menu, { | ||
| ref: rootRef | ||
| }, options.map(function (option, index) { | ||
| return /*#__PURE__*/React.createElement(DropdownItem, { | ||
| components: components, | ||
| getOptionLabel: getOptionLabel, | ||
| formatOptionLabel: formatOptionLabel, | ||
| option: option, | ||
| isHighlighted: highlightedIndex === index, | ||
| onSelect: onSelect, | ||
| setHighlightedIndex: setHighlightedIndex, | ||
| index: index, | ||
| key: index | ||
| }); | ||
| })); | ||
| }; | ||
| export default Dropdown; |
| import React, { useCallback } from 'react'; | ||
| var DropdownItem = function DropdownItem(_ref) { | ||
| var components = _ref.components, | ||
| getOptionLabel = _ref.getOptionLabel, | ||
| formatOptionLabel = _ref.formatOptionLabel, | ||
| isHighlighted = _ref.isHighlighted, | ||
| option = _ref.option, | ||
| onSelect = _ref.onSelect, | ||
| setHighlightedIndex = _ref.setHighlightedIndex, | ||
| index = _ref.index; | ||
| var onClick = useCallback(function () { | ||
| onSelect(getOptionLabel(option), option); | ||
| }, [onSelect, option, getOptionLabel]); | ||
| var onMouseEnter = useCallback(function () { | ||
| setHighlightedIndex(index); | ||
| }, [setHighlightedIndex, index]); | ||
| var MenuItem = components.MenuItem; | ||
| return /*#__PURE__*/React.createElement(MenuItem, { | ||
| $isHighlighted: isHighlighted, | ||
| onMouseEnter: onMouseEnter, | ||
| onClick: onClick | ||
| }, formatOptionLabel(option)); | ||
| }; | ||
| export default DropdownItem; |
| export { default } from './Autocomplete'; |
| "use strict"; | ||
| var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); | ||
| var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports["default"] = void 0; | ||
| var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); | ||
| var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); | ||
| var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); | ||
| var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); | ||
| var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2")); | ||
| var _react = _interopRequireWildcard(require("react")); | ||
| var _components = _interopRequireDefault(require("./components")); | ||
| var _Dropdown = _interopRequireDefault(require("./Dropdown")); | ||
| var Autocomplete = function Autocomplete(_ref) { | ||
| var loadOptions = _ref.loadOptions, | ||
| value = _ref.value, | ||
| onChangeProp = _ref.onChange, | ||
| onSelectProp = _ref.onSelect, | ||
| disabled = _ref.disabled, | ||
| hasError = _ref.hasError, | ||
| hasWarning = _ref.hasWarning, | ||
| inputProps = _ref.inputProps, | ||
| labelKey = _ref.labelKey, | ||
| getOptionLabelProp = _ref.getOptionLabel, | ||
| formatOptionLabelProp = _ref.formatOptionLabel, | ||
| componentsProp = _ref.components; | ||
| var components = (0, _react.useMemo)(function () { | ||
| return (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, _components["default"]), componentsProp); | ||
| }, [componentsProp]); | ||
| var valueRef = (0, _react.useRef)(value); | ||
| valueRef.current = value; | ||
| var _useState = (0, _react.useState)([]), | ||
| _useState2 = (0, _slicedToArray2["default"])(_useState, 2), | ||
| options = _useState2[0], | ||
| setOptions = _useState2[1]; | ||
| var _useState3 = (0, _react.useState)(false), | ||
| _useState4 = (0, _slicedToArray2["default"])(_useState3, 2), | ||
| isOpen = _useState4[0], | ||
| setIsOpen = _useState4[1]; | ||
| var getOptionLabel = (0, _react.useCallback)(function (option) { | ||
| if (getOptionLabelProp) { | ||
| return getOptionLabelProp(option); | ||
| } | ||
| return option[labelKey]; | ||
| }, [getOptionLabelProp, labelKey]); | ||
| var formatOptionLabel = (0, _react.useCallback)(function (option) { | ||
| if (formatOptionLabelProp) { | ||
| return formatOptionLabelProp(option); | ||
| } | ||
| return getOptionLabel(option); | ||
| }, [formatOptionLabelProp, getOptionLabel]); | ||
| var onInputFocus = (0, _react.useCallback)(function () { | ||
| setIsOpen(true); | ||
| }, []); | ||
| var closeMenu = (0, _react.useCallback)(function () { | ||
| setIsOpen(false); | ||
| }, []); | ||
| var onSelect = (0, _react.useCallback)(function (label, option) { | ||
| setIsOpen(false); | ||
| onSelectProp(label, option); | ||
| }, [onSelectProp]); | ||
| var onChange = (0, _react.useCallback)(function (event) { | ||
| onChangeProp(event); | ||
| if (!isOpen) { | ||
| setIsOpen(true); | ||
| } | ||
| }, [onChangeProp, isOpen]); | ||
| (0, _react.useEffect)(function () { | ||
| (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee() { | ||
| var response; | ||
| return _regenerator["default"].wrap(function _callee$(_context) { | ||
| while (1) { | ||
| switch (_context.prev = _context.next) { | ||
| case 0: | ||
| _context.next = 2; | ||
| return loadOptions(value); | ||
| case 2: | ||
| response = _context.sent; | ||
| if (valueRef.current === value) { | ||
| setOptions(response.options); | ||
| } | ||
| case 4: | ||
| case "end": | ||
| return _context.stop(); | ||
| } | ||
| } | ||
| }, _callee); | ||
| }))(); // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [value]); | ||
| var Wrapper = components.Wrapper, | ||
| Input = components.Input; | ||
| return /*#__PURE__*/_react["default"].createElement(Wrapper, null, /*#__PURE__*/_react["default"].createElement(Input, (0, _extends2["default"])({}, inputProps, { | ||
| disabled: disabled, | ||
| $hasError: hasError, | ||
| $hasWarning: hasWarning, | ||
| value: value, | ||
| onChange: onChange, | ||
| onFocus: onInputFocus | ||
| })), isOpen && options.length > 0 && /*#__PURE__*/_react["default"].createElement(_Dropdown["default"], { | ||
| components: components, | ||
| getOptionLabel: getOptionLabel, | ||
| formatOptionLabel: formatOptionLabel, | ||
| options: options, | ||
| onSelect: onSelect, | ||
| closeMenu: closeMenu | ||
| })); | ||
| }; | ||
| Autocomplete.defaultProps = { | ||
| onChange: function onChange() {}, | ||
| onSelect: function onSelect() {}, | ||
| disabled: false, | ||
| hasError: false, | ||
| hasWarning: false, | ||
| inputProps: {}, | ||
| labelKey: 'label', | ||
| getOptionLabel: null, | ||
| formatOptionLabel: null, | ||
| components: {} | ||
| }; | ||
| var _default = Autocomplete; | ||
| exports["default"] = _default; |
| "use strict"; | ||
| var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports["default"] = void 0; | ||
| var _Wrapper = _interopRequireDefault(require("./Wrapper")); | ||
| var _Input = _interopRequireDefault(require("./Input")); | ||
| var _Menu = _interopRequireDefault(require("./Menu")); | ||
| var _MenuItem = _interopRequireDefault(require("./MenuItem")); | ||
| var components = { | ||
| Wrapper: _Wrapper["default"], | ||
| Input: _Input["default"], | ||
| Menu: _Menu["default"], | ||
| MenuItem: _MenuItem["default"] | ||
| }; | ||
| var _default = components; | ||
| exports["default"] = _default; |
| "use strict"; | ||
| var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports["default"] = void 0; | ||
| var _styledComponents = _interopRequireDefault(require("styled-components")); | ||
| var Input = _styledComponents["default"].input(function (_ref) { | ||
| var $hasError = _ref.$hasError, | ||
| $hasWarning = _ref.$hasWarning; | ||
| var res = { | ||
| width: '100%', | ||
| boxSizing: 'border-box' | ||
| }; | ||
| if ($hasError) { | ||
| res.borderColor = '#eea505'; | ||
| res.backgroundColor = '#fdf6e6'; | ||
| } else if ($hasWarning) { | ||
| res.borderColor = '#d64c4c'; | ||
| res.backgroundColor = '#fdf6f6'; | ||
| } | ||
| return res; | ||
| }); | ||
| var _default = Input; | ||
| exports["default"] = _default; |
| "use strict"; | ||
| var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports["default"] = void 0; | ||
| var _styledComponents = _interopRequireDefault(require("styled-components")); | ||
| var Menu = _styledComponents["default"].div({ | ||
| boxSizing: 'border-box', | ||
| position: 'absolute', | ||
| top: '100%', | ||
| width: '100%', | ||
| zIndex: 1, | ||
| marginTop: 4, | ||
| maxHeight: 200, | ||
| overflowY: 'auto', | ||
| borderRadius: 4, | ||
| backgroundColor: '#fff', | ||
| border: '1px solid #d9e1e8', | ||
| boxShadow: '0 1px 0 rgba(0, 0, 0, 0.06)' | ||
| }); | ||
| var _default = Menu; | ||
| exports["default"] = _default; |
| "use strict"; | ||
| var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports["default"] = void 0; | ||
| var _styledComponents = _interopRequireDefault(require("styled-components")); | ||
| var MenuItem = _styledComponents["default"].div(function (_ref) { | ||
| var $isHighlighted = _ref.$isHighlighted; | ||
| return { | ||
| display: 'block', | ||
| boxSizing: 'border-box', | ||
| padding: 10, | ||
| color: '#666', | ||
| backgroundColor: $isHighlighted ? '#f9f9f9' : '#fff', | ||
| cursor: 'pointer' | ||
| }; | ||
| }); | ||
| var _default = MenuItem; | ||
| exports["default"] = _default; |
| "use strict"; | ||
| var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports["default"] = void 0; | ||
| var _styledComponents = _interopRequireDefault(require("styled-components")); | ||
| var Wrapper = _styledComponents["default"].div({ | ||
| position: 'relative' | ||
| }); | ||
| var _default = Wrapper; | ||
| exports["default"] = _default; |
-103
| "use strict"; | ||
| var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); | ||
| var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports["default"] = void 0; | ||
| var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); | ||
| var _react = _interopRequireWildcard(require("react")); | ||
| var _useOnclickoutside = _interopRequireDefault(require("use-onclickoutside")); | ||
| var _DropdownItem = _interopRequireDefault(require("./DropdownItem")); | ||
| var Dropdown = function Dropdown(_ref) { | ||
| var components = _ref.components, | ||
| getOptionLabel = _ref.getOptionLabel, | ||
| formatOptionLabel = _ref.formatOptionLabel, | ||
| options = _ref.options, | ||
| onSelect = _ref.onSelect, | ||
| closeMenu = _ref.closeMenu; | ||
| var rootRef = (0, _react.useRef)(null); | ||
| (0, _useOnclickoutside["default"])(rootRef, closeMenu); | ||
| var isInitRef = (0, _react.useRef)(true); | ||
| var _useState = (0, _react.useState)(0), | ||
| _useState2 = (0, _slicedToArray2["default"])(_useState, 2), | ||
| highlightedIndex = _useState2[0], | ||
| setHighlightedIndex = _useState2[1]; | ||
| var highlightedIndexRef = (0, _react.useRef)(highlightedIndex); | ||
| highlightedIndexRef.current = highlightedIndex; | ||
| (0, _react.useEffect)(function () { | ||
| if (isInitRef.current) { | ||
| isInitRef.current = false; | ||
| } else { | ||
| setHighlightedIndex(0); | ||
| } | ||
| var onKeyDown = function onKeyDown(event) { | ||
| switch (event.key) { | ||
| case 'ArrowDown': | ||
| setHighlightedIndex(function (prevIndex) { | ||
| if (prevIndex === options.length - 1) { | ||
| return 0; | ||
| } | ||
| return prevIndex + 1; | ||
| }); | ||
| break; | ||
| case 'ArrowUp': | ||
| setHighlightedIndex(function (prevIndex) { | ||
| if (prevIndex === 0) { | ||
| return options.length - 1; | ||
| } | ||
| return prevIndex - 1; | ||
| }); | ||
| break; | ||
| case 'Enter': | ||
| { | ||
| var option = options[highlightedIndexRef.current]; | ||
| onSelect(getOptionLabel(option), option); | ||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| } | ||
| }; | ||
| document.addEventListener('keydown', onKeyDown); | ||
| return function () { | ||
| document.removeEventListener('keydown', onKeyDown); | ||
| }; | ||
| }, [options, getOptionLabel, onSelect]); | ||
| var Menu = components.Menu; | ||
| return /*#__PURE__*/_react["default"].createElement(Menu, { | ||
| ref: rootRef | ||
| }, options.map(function (option, index) { | ||
| return /*#__PURE__*/_react["default"].createElement(_DropdownItem["default"], { | ||
| components: components, | ||
| getOptionLabel: getOptionLabel, | ||
| formatOptionLabel: formatOptionLabel, | ||
| option: option, | ||
| isHighlighted: highlightedIndex === index, | ||
| onSelect: onSelect, | ||
| setHighlightedIndex: setHighlightedIndex, | ||
| index: index, | ||
| key: index | ||
| }); | ||
| })); | ||
| }; | ||
| var _default = Dropdown; | ||
| exports["default"] = _default; |
| "use strict"; | ||
| var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports["default"] = void 0; | ||
| var _react = _interopRequireWildcard(require("react")); | ||
| var DropdownItem = function DropdownItem(_ref) { | ||
| var components = _ref.components, | ||
| getOptionLabel = _ref.getOptionLabel, | ||
| formatOptionLabel = _ref.formatOptionLabel, | ||
| isHighlighted = _ref.isHighlighted, | ||
| option = _ref.option, | ||
| onSelect = _ref.onSelect, | ||
| setHighlightedIndex = _ref.setHighlightedIndex, | ||
| index = _ref.index; | ||
| var onClick = (0, _react.useCallback)(function () { | ||
| onSelect(getOptionLabel(option), option); | ||
| }, [onSelect, option, getOptionLabel]); | ||
| var onMouseEnter = (0, _react.useCallback)(function () { | ||
| setHighlightedIndex(index); | ||
| }, [setHighlightedIndex, index]); | ||
| var MenuItem = components.MenuItem; | ||
| return /*#__PURE__*/_react["default"].createElement(MenuItem, { | ||
| $isHighlighted: isHighlighted, | ||
| onMouseEnter: onMouseEnter, | ||
| onClick: onClick | ||
| }, formatOptionLabel(option)); | ||
| }; | ||
| var _default = DropdownItem; | ||
| exports["default"] = _default; |
-15
| "use strict"; | ||
| var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function get() { | ||
| return _Autocomplete["default"]; | ||
| } | ||
| }); | ||
| var _Autocomplete = _interopRequireDefault(require("./Autocomplete")); |
| "use strict"; |
| import type { FC, HTMLProps } from 'react'; | ||
| import type { Components, FormatOptionLabel, GetOptionLabel, LoadOptions, OnChange, OnSelect } from './types'; | ||
| export declare type AutocompleteProps = { | ||
| /** | ||
| * Функция загрузки опций | ||
| * @param {String} search - текущее значение поля ввода | ||
| * @returns {Object[]} response.options - опции | ||
| */ | ||
| loadOptions: LoadOptions; | ||
| /** | ||
| * Значение элемента input | ||
| */ | ||
| value: string; | ||
| /** | ||
| * Обработчик изменения значения поля при ручном вводе | ||
| */ | ||
| onChange?: OnChange; | ||
| /** | ||
| * Обработчик изменения значения поля при выборе из меню | ||
| * @param {String} value - текст выбранной опции | ||
| * @param {Object} option - выбранная опция | ||
| */ | ||
| onSelect?: OnSelect; | ||
| /** | ||
| * Выключено ли поле | ||
| */ | ||
| disabled?: boolean; | ||
| /** | ||
| * Есть ли у поля ошибка | ||
| */ | ||
| hasError?: boolean; | ||
| /** | ||
| * Есть ли у поля предупреждение | ||
| */ | ||
| hasWarning?: boolean; | ||
| /** | ||
| * Дополнительные props элемента input | ||
| */ | ||
| inputProps?: Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange' | 'disabled'>; | ||
| /** | ||
| * Ключ, по которому хранится текст опции | ||
| */ | ||
| labelKey?: string; | ||
| /** | ||
| * Функция получения текста опции, который будет подставлен при выборе | ||
| */ | ||
| getOptionLabel?: GetOptionLabel; | ||
| /** | ||
| * Функция отображения опции | ||
| */ | ||
| formatOptionLabel?: FormatOptionLabel; | ||
| /** | ||
| * Переиспользуемые компоненты | ||
| */ | ||
| components?: Partial<Components>; | ||
| }; | ||
| declare const Autocomplete: FC<AutocompleteProps>; | ||
| export default Autocomplete; |
| import type { Components } from '../types'; | ||
| declare const components: Components; | ||
| export default components; |
| import type { InputComponent } from '../types'; | ||
| declare const Input: InputComponent; | ||
| export default Input; |
| import type { MenuComponent } from '../types'; | ||
| declare const Menu: MenuComponent; | ||
| export default Menu; |
| import type { MenuItemComponent } from '../types'; | ||
| declare const MenuItem: MenuItemComponent; | ||
| export default MenuItem; |
| import type { WrapperComponent } from '../types'; | ||
| declare const Wrapper: WrapperComponent; | ||
| export default Wrapper; |
| import type { FC } from 'react'; | ||
| import type { Components, FormatOptionLabel, GetOptionLabel, Option, OnSelect } from './types'; | ||
| export declare type DropdownProps = { | ||
| components: Components; | ||
| getOptionLabel: GetOptionLabel; | ||
| formatOptionLabel: FormatOptionLabel; | ||
| options: Option[]; | ||
| onSelect: OnSelect; | ||
| closeMenu: () => void; | ||
| }; | ||
| declare const Dropdown: FC<DropdownProps>; | ||
| export default Dropdown; |
| import type { FC } from 'react'; | ||
| import type { Components, FormatOptionLabel, GetOptionLabel, Option, OnSelect } from './types'; | ||
| export declare type DropdownItemProps = { | ||
| components: Components; | ||
| getOptionLabel: GetOptionLabel; | ||
| formatOptionLabel: FormatOptionLabel; | ||
| option: Option; | ||
| isHighlighted: boolean; | ||
| onSelect: OnSelect; | ||
| setHighlightedIndex: (index: number) => void; | ||
| index: number; | ||
| }; | ||
| declare const DropdownItem: FC<DropdownItemProps>; | ||
| export default DropdownItem; |
| export { default } from './Autocomplete'; | ||
| export type { AutocompleteProps, } from './Autocomplete'; | ||
| export type { WrapperComponent, InputComponentProps, InputComponent, MenuComponent, MenuItemProps, MenuItemComponent, Components, Option, LoadOptionsResponse, LoadOptions, GetOptionLabel, FormatOptionLabel, OnSelect, } from './types'; |
| import type { ComponentType, HTMLProps, ReactNode, Ref, SyntheticEvent } from 'react'; | ||
| export declare type WrapperComponent = ComponentType; | ||
| export declare type InputComponentProps = HTMLProps<HTMLInputElement> & { | ||
| $hasError: boolean; | ||
| $hasWarning: boolean; | ||
| }; | ||
| export declare type InputComponent = ComponentType<InputComponentProps>; | ||
| export declare type MenuComponent = ComponentType<{ | ||
| ref: Ref<HTMLElement>; | ||
| }>; | ||
| export declare type MenuItemProps = HTMLProps<HTMLDivElement> & { | ||
| $isHighlighted: boolean; | ||
| }; | ||
| export declare type MenuItemComponent = ComponentType<MenuItemProps>; | ||
| export declare type Components = { | ||
| Wrapper: WrapperComponent; | ||
| Input: InputComponent; | ||
| Menu: MenuComponent; | ||
| MenuItem: MenuItemComponent; | ||
| }; | ||
| export declare type Option = { | ||
| [key: string]: any; | ||
| }; | ||
| export declare type LoadOptionsResponse = { | ||
| options: Option[]; | ||
| }; | ||
| export declare type LoadOptions = (inputValue: string) => LoadOptionsResponse | Promise<LoadOptionsResponse>; | ||
| export declare type GetOptionLabel = (option: Option) => string; | ||
| export declare type FormatOptionLabel = (Option: Option) => ReactNode; | ||
| export declare type OnChange = (event: SyntheticEvent<HTMLInputElement>) => void; | ||
| export declare type OnSelect = (value: string, option: Option) => void; |
AI-detected possible typosquat
Supply chain riskAI has identified this package as a potential typosquat of a more popular package. This suggests that the package may be intentionally mimicking another package's name, description, or other metadata.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
59748
85.73%3
-25%1
-50%74
27.59%38
90%7
-78.79%725
-2.55%2
100%+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated