@szhsin/react-autocomplete
Advanced tools
Comparing version 0.5.3 to 0.6.0
@@ -6,19 +6,21 @@ 'use strict'; | ||
const useAutocomplete = ({ | ||
feature, | ||
feature: useFeature = () => ({}), | ||
items = [], | ||
onChange = () => {} | ||
}) => { | ||
const inputRef = react.useRef(); | ||
const [inputValue, setInputValue] = react.useState(''); | ||
const [isOpen, setOpen] = react.useState(false); | ||
const inputRef = react.useRef(null); | ||
const [open, setOpen] = react.useState(false); | ||
const [focusIndex, setFocusIndex] = react.useState(-1); | ||
const [instance] = react.useState({ | ||
b: inputValue | ||
b: '' | ||
}); | ||
const setInputValue = react.useCallback(value => { | ||
const input = inputRef.current; | ||
if (input) input.value = value; | ||
}, []); | ||
const state = { | ||
inputValue, | ||
setInputValue, | ||
focusIndex, | ||
setFocusIndex, | ||
isOpen, | ||
open, | ||
setOpen | ||
@@ -31,17 +33,16 @@ }; | ||
onKeyDown, | ||
onItemClick | ||
} = (feature == null ? void 0 : feature({ | ||
onItemClick, | ||
...actions | ||
} = useFeature({ | ||
_: instance, | ||
items, | ||
onChange, | ||
inputRef, | ||
...state | ||
})) || {}; | ||
}); | ||
const getInputProps = () => ({ | ||
value: inputValue, | ||
ref: inputRef, | ||
onChange: e => onInputChange == null ? void 0 : onInputChange({ | ||
value: e.target.value | ||
}), | ||
onClick: () => onInputClick == null ? void 0 : onInputClick(), | ||
onBlur: () => !instance.a && (onBlur == null ? void 0 : onBlur()), | ||
onChange: onInputChange, | ||
onClick: onInputClick, | ||
onBlur: e => !instance.a && (onBlur == null ? void 0 : onBlur(e)), | ||
onKeyDown: e => { | ||
@@ -52,5 +53,3 @@ const { | ||
if (items.length && (key === 'ArrowUp' || key === 'ArrowDown')) e.preventDefault(); | ||
onKeyDown == null || onKeyDown({ | ||
key | ||
}); | ||
onKeyDown == null || onKeyDown(e); | ||
} | ||
@@ -62,5 +61,5 @@ }); | ||
onMouseDown: () => instance.a = 1, | ||
onClick: () => { | ||
onClick: e => { | ||
var _inputRef$current; | ||
onItemClick == null || onItemClick({ | ||
onItemClick == null || onItemClick(e, { | ||
index | ||
@@ -82,26 +81,30 @@ }); | ||
getProps, | ||
...state | ||
...state, | ||
...actions | ||
}; | ||
}; | ||
const autocomplete = () => ({ | ||
_, | ||
const autocomplete = ({ | ||
rovingInput | ||
} = {}) => ({ | ||
_: cxInstance, | ||
items, | ||
onChange, | ||
inputValue, | ||
setInputValue, | ||
focusIndex, | ||
setFocusIndex, | ||
isOpen, | ||
open, | ||
setOpen | ||
}) => { | ||
const updateValue = value => { | ||
_.b = value; | ||
const updateValue = (value, type) => { | ||
cxInstance.b = value; | ||
setInputValue(value); | ||
onChange(value); | ||
onChange(value, { | ||
type | ||
}); | ||
}; | ||
const updateAndCloseList = value => { | ||
if (isOpen) { | ||
const updateAndCloseList = (value, type) => { | ||
if (open) { | ||
if (value != null) { | ||
updateValue(value); | ||
updateValue(value, type); | ||
} | ||
@@ -112,4 +115,5 @@ setOpen(false); | ||
}; | ||
const traverseItems = (isUp, baseIndex = -1) => { | ||
const traverseItems = isUp => { | ||
var _items$nextIndex; | ||
const baseIndex = rovingInput ? -1 : 0; | ||
let nextIndex = focusIndex; | ||
@@ -123,17 +127,15 @@ const itemLength = items.length; | ||
setFocusIndex(nextIndex); | ||
setInputValue((_items$nextIndex = items[nextIndex]) != null ? _items$nextIndex : _.b); | ||
rovingInput && setInputValue((_items$nextIndex = items[nextIndex]) != null ? _items$nextIndex : cxInstance.b); | ||
}; | ||
return { | ||
onItemClick: ({ | ||
onItemClick: (_, { | ||
index | ||
}) => updateAndCloseList(items[index]), | ||
onInputChange: ({ | ||
value | ||
}) => { | ||
updateValue(value); | ||
}) => updateAndCloseList(items[index], 'submit'), | ||
onInputChange: e => { | ||
setFocusIndex(-1); | ||
setOpen(true); | ||
updateValue(e.target.value, 'input'); | ||
}, | ||
onInputClick: () => setOpen(true), | ||
onBlur: () => updateAndCloseList(inputValue), | ||
onBlur: () => updateAndCloseList(items[focusIndex], 'blur'), | ||
onKeyDown: ({ | ||
@@ -144,3 +146,3 @@ key | ||
case 'ArrowUp': | ||
if (isOpen) { | ||
if (open) { | ||
traverseItems(true); | ||
@@ -152,3 +154,3 @@ } else { | ||
case 'ArrowDown': | ||
if (isOpen) { | ||
if (open) { | ||
traverseItems(false); | ||
@@ -160,6 +162,6 @@ } else { | ||
case 'Enter': | ||
updateAndCloseList(items[focusIndex]); | ||
updateAndCloseList(items[focusIndex], 'submit'); | ||
break; | ||
case 'Escape': | ||
updateAndCloseList(inputValue); | ||
updateAndCloseList(cxInstance.b, 'esc'); | ||
break; | ||
@@ -171,3 +173,72 @@ } | ||
const supercomplete = () => { | ||
const base = autocomplete({ | ||
rovingInput: true | ||
}); | ||
return ({ | ||
setFocusIndex: _setFocusIndex, | ||
onChange: _onChange, | ||
...cx | ||
}) => { | ||
const [instance] = react.useState({ | ||
a: [] | ||
}); | ||
const setFocusIndex = react.useCallback(value => { | ||
instance.b = value; | ||
_setFocusIndex(value); | ||
}, [instance, _setFocusIndex]); | ||
const onChange = (value, meta) => { | ||
if (meta.type !== 'input') { | ||
instance.c = false; | ||
instance.a = []; | ||
} | ||
_onChange(value, meta); | ||
}; | ||
const baseFeature = base({ | ||
...cx, | ||
setFocusIndex, | ||
onChange | ||
}); | ||
const { | ||
inputRef, | ||
setInputValue, | ||
_: cxInstance | ||
} = cx; | ||
return { | ||
...baseFeature, | ||
onInputChange: e => { | ||
instance.c = e.nativeEvent.inputType === 'insertText'; | ||
if (!instance.c) instance.a = []; | ||
baseFeature.onInputChange(e); | ||
}, | ||
onKeyDown: e => { | ||
baseFeature.onKeyDown(e); | ||
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { | ||
const [index, action] = instance.a; | ||
if (instance.b === index) action == null || action(); | ||
} | ||
}, | ||
inlineComplete: react.useCallback(({ | ||
index, | ||
value | ||
}) => { | ||
if (instance.c) { | ||
const action = () => { | ||
var _inputRef$current; | ||
setFocusIndex(index); | ||
const valueLength = cxInstance.b.length; | ||
const newValue = cxInstance.b + value.slice(valueLength); | ||
setInputValue(newValue); | ||
(_inputRef$current = inputRef.current) == null || _inputRef$current.setSelectionRange(valueLength, value.length); | ||
}; | ||
action(); | ||
instance.a = [index, action]; | ||
} | ||
}, [cxInstance, instance, inputRef, setFocusIndex, setInputValue]) | ||
}; | ||
}; | ||
}; | ||
exports.autocomplete = autocomplete; | ||
exports.supercomplete = supercomplete; | ||
exports.useAutocomplete = useAutocomplete; |
@@ -1,21 +0,24 @@ | ||
const autocomplete = () => ({ | ||
_, | ||
const autocomplete = ({ | ||
rovingInput | ||
} = {}) => ({ | ||
_: cxInstance, | ||
items, | ||
onChange, | ||
inputValue, | ||
setInputValue, | ||
focusIndex, | ||
setFocusIndex, | ||
isOpen, | ||
open, | ||
setOpen | ||
}) => { | ||
const updateValue = value => { | ||
_.b = value; | ||
const updateValue = (value, type) => { | ||
cxInstance.b = value; | ||
setInputValue(value); | ||
onChange(value); | ||
onChange(value, { | ||
type | ||
}); | ||
}; | ||
const updateAndCloseList = value => { | ||
if (isOpen) { | ||
const updateAndCloseList = (value, type) => { | ||
if (open) { | ||
if (value != null) { | ||
updateValue(value); | ||
updateValue(value, type); | ||
} | ||
@@ -26,4 +29,5 @@ setOpen(false); | ||
}; | ||
const traverseItems = (isUp, baseIndex = -1) => { | ||
const traverseItems = isUp => { | ||
var _items$nextIndex; | ||
const baseIndex = rovingInput ? -1 : 0; | ||
let nextIndex = focusIndex; | ||
@@ -37,17 +41,15 @@ const itemLength = items.length; | ||
setFocusIndex(nextIndex); | ||
setInputValue((_items$nextIndex = items[nextIndex]) != null ? _items$nextIndex : _.b); | ||
rovingInput && setInputValue((_items$nextIndex = items[nextIndex]) != null ? _items$nextIndex : cxInstance.b); | ||
}; | ||
return { | ||
onItemClick: ({ | ||
onItemClick: (_, { | ||
index | ||
}) => updateAndCloseList(items[index]), | ||
onInputChange: ({ | ||
value | ||
}) => { | ||
updateValue(value); | ||
}) => updateAndCloseList(items[index], 'submit'), | ||
onInputChange: e => { | ||
setFocusIndex(-1); | ||
setOpen(true); | ||
updateValue(e.target.value, 'input'); | ||
}, | ||
onInputClick: () => setOpen(true), | ||
onBlur: () => updateAndCloseList(inputValue), | ||
onBlur: () => updateAndCloseList(items[focusIndex], 'blur'), | ||
onKeyDown: ({ | ||
@@ -58,3 +60,3 @@ key | ||
case 'ArrowUp': | ||
if (isOpen) { | ||
if (open) { | ||
traverseItems(true); | ||
@@ -66,3 +68,3 @@ } else { | ||
case 'ArrowDown': | ||
if (isOpen) { | ||
if (open) { | ||
traverseItems(false); | ||
@@ -74,6 +76,6 @@ } else { | ||
case 'Enter': | ||
updateAndCloseList(items[focusIndex]); | ||
updateAndCloseList(items[focusIndex], 'submit'); | ||
break; | ||
case 'Escape': | ||
updateAndCloseList(inputValue); | ||
updateAndCloseList(cxInstance.b, 'esc'); | ||
break; | ||
@@ -80,0 +82,0 @@ } |
@@ -1,21 +0,23 @@ | ||
import { useRef, useState } from 'react'; | ||
import { useRef, useState, useCallback } from 'react'; | ||
const useAutocomplete = ({ | ||
feature, | ||
feature: useFeature = () => ({}), | ||
items = [], | ||
onChange = () => {} | ||
}) => { | ||
const inputRef = useRef(); | ||
const [inputValue, setInputValue] = useState(''); | ||
const [isOpen, setOpen] = useState(false); | ||
const inputRef = useRef(null); | ||
const [open, setOpen] = useState(false); | ||
const [focusIndex, setFocusIndex] = useState(-1); | ||
const [instance] = useState({ | ||
b: inputValue | ||
b: '' | ||
}); | ||
const setInputValue = useCallback(value => { | ||
const input = inputRef.current; | ||
if (input) input.value = value; | ||
}, []); | ||
const state = { | ||
inputValue, | ||
setInputValue, | ||
focusIndex, | ||
setFocusIndex, | ||
isOpen, | ||
open, | ||
setOpen | ||
@@ -28,17 +30,16 @@ }; | ||
onKeyDown, | ||
onItemClick | ||
} = (feature == null ? void 0 : feature({ | ||
onItemClick, | ||
...actions | ||
} = useFeature({ | ||
_: instance, | ||
items, | ||
onChange, | ||
inputRef, | ||
...state | ||
})) || {}; | ||
}); | ||
const getInputProps = () => ({ | ||
value: inputValue, | ||
ref: inputRef, | ||
onChange: e => onInputChange == null ? void 0 : onInputChange({ | ||
value: e.target.value | ||
}), | ||
onClick: () => onInputClick == null ? void 0 : onInputClick(), | ||
onBlur: () => !instance.a && (onBlur == null ? void 0 : onBlur()), | ||
onChange: onInputChange, | ||
onClick: onInputClick, | ||
onBlur: e => !instance.a && (onBlur == null ? void 0 : onBlur(e)), | ||
onKeyDown: e => { | ||
@@ -49,5 +50,3 @@ const { | ||
if (items.length && (key === 'ArrowUp' || key === 'ArrowDown')) e.preventDefault(); | ||
onKeyDown == null || onKeyDown({ | ||
key | ||
}); | ||
onKeyDown == null || onKeyDown(e); | ||
} | ||
@@ -59,5 +58,5 @@ }); | ||
onMouseDown: () => instance.a = 1, | ||
onClick: () => { | ||
onClick: e => { | ||
var _inputRef$current; | ||
onItemClick == null || onItemClick({ | ||
onItemClick == null || onItemClick(e, { | ||
index | ||
@@ -79,3 +78,4 @@ }); | ||
getProps, | ||
...state | ||
...state, | ||
...actions | ||
}; | ||
@@ -82,0 +82,0 @@ }; |
export { useAutocomplete } from './hooks/useAutocomplete.js'; | ||
export { autocomplete } from './features/autocomplete.js'; | ||
export { supercomplete } from './features/supercomplete.js'; |
{ | ||
"name": "@szhsin/react-autocomplete", | ||
"version": "0.5.3", | ||
"version": "0.6.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "author": "Zheng Song", |
@@ -0,11 +1,14 @@ | ||
/// <reference types="react" /> | ||
export interface AutocompleteState { | ||
inputValue: string; | ||
setInputValue: (value: string) => void; | ||
focusIndex: number; | ||
setFocusIndex: (value: number) => void; | ||
isOpen: boolean; | ||
open: boolean; | ||
setOpen: (value: boolean) => void; | ||
} | ||
interface ContextualProps { | ||
onChange: (value: string) => void; | ||
export type ChangeType = 'submit' | 'input' | 'blur' | 'esc'; | ||
export interface ContextualProps { | ||
onChange: (value: string, meta: { | ||
type: ChangeType; | ||
}) => void; | ||
items: string[]; | ||
@@ -26,2 +29,3 @@ } | ||
export interface Contextual extends ContextualProps, AutocompleteState { | ||
inputRef: React.RefObject<HTMLInputElement>; | ||
/** | ||
@@ -32,19 +36,13 @@ * ### INTERNAL API ### | ||
} | ||
type FeatureEventHandler<E> = (event: E) => void; | ||
export type Feature = (cx: Contextual) => { | ||
onInputChange?: FeatureEventHandler<{ | ||
value: string; | ||
}>; | ||
onInputClick?: () => void; | ||
onBlur?: () => void; | ||
onKeyDown?: FeatureEventHandler<{ | ||
key: string; | ||
}>; | ||
onItemClick?: FeatureEventHandler<{ | ||
export type Feature<Actions = object> = (cx: Contextual) => { | ||
onInputChange?: React.ChangeEventHandler<HTMLInputElement>; | ||
onInputClick?: React.MouseEventHandler<HTMLInputElement>; | ||
onBlur?: React.FocusEventHandler<HTMLInputElement>; | ||
onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>; | ||
onItemClick?: (event: React.MouseEvent<HTMLElement>, props: { | ||
index: number; | ||
}>; | ||
}; | ||
export interface AutocompleteProps extends Partial<ContextualProps> { | ||
feature?: Feature; | ||
}) => void; | ||
} & Actions; | ||
export interface AutocompleteProps<FeatureActions = object> extends Partial<ContextualProps> { | ||
feature?: Feature<FeatureActions>; | ||
} | ||
export {}; |
@@ -1,3 +0,5 @@ | ||
import { Feature } from '../common'; | ||
declare const autocomplete: () => Feature; | ||
import type { Feature } from '../common'; | ||
declare const autocomplete: (props?: { | ||
rovingInput?: boolean; | ||
}) => Feature; | ||
export { autocomplete }; |
@@ -9,11 +9,10 @@ import type { InputHTMLAttributes, HTMLAttributes } from 'react'; | ||
} | ||
declare const useAutocomplete: ({ feature, items, onChange }: AutocompleteProps) => { | ||
inputValue: string; | ||
declare const useAutocomplete: <FeatureActions = object>({ feature: useFeature, items, onChange }: AutocompleteProps<FeatureActions>) => { | ||
setInputValue: (value: string) => void; | ||
focusIndex: number; | ||
setFocusIndex: (value: number) => void; | ||
isOpen: boolean; | ||
open: boolean; | ||
setOpen: (value: boolean) => void; | ||
getProps: <T extends keyof GetProps>(elementType: T, option?: GetProps[T][0] | undefined) => GetProps[T][1]; | ||
}; | ||
} & FeatureActions; | ||
export { useAutocomplete }; |
export { useAutocomplete } from './hooks/useAutocomplete'; | ||
export { autocomplete } from './features/autocomplete'; | ||
export { supercomplete } from './features/supercomplete'; | ||
export type { AutocompleteProps, AutocompleteState, Feature } from './common'; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17988
13
561