@szhsin/react-autocomplete
Advanced tools
Comparing version 0.7.0 to 0.8.0
@@ -6,5 +6,5 @@ 'use strict'; | ||
const useAutocomplete = ({ | ||
items = [], | ||
onChange = () => {}, | ||
feature: useFeature, | ||
traversal: useTraversal, | ||
getItemValue: _getItemValue | ||
@@ -14,3 +14,3 @@ }) => { | ||
const [open, setOpen] = react.useState(false); | ||
const [focusIndex, setFocusIndex] = react.useState(-1); | ||
const [focusItem, setFocusItem] = react.useState(); | ||
const [instance] = react.useState({ | ||
@@ -20,3 +20,3 @@ b: '', | ||
}); | ||
const getItemValue = item => item == null ? null : _getItemValue ? _getItemValue(item) : typeof item === 'string' ? item : null; | ||
const getItemValue = react.useCallback(item => item == null ? null : _getItemValue ? _getItemValue(item) : item.toString(), [_getItemValue]); | ||
const setInputValue = react.useCallback(value => { | ||
@@ -28,7 +28,14 @@ const input = inputRef.current; | ||
setInputValue, | ||
focusIndex, | ||
setFocusIndex, | ||
focusItem, | ||
setFocusItem, | ||
open, | ||
setOpen | ||
}; | ||
const contextual = { | ||
_: instance, | ||
getItemValue, | ||
onChange, | ||
inputRef, | ||
...state | ||
}; | ||
const { | ||
@@ -39,12 +46,7 @@ getInputProps: _getInputProps, | ||
} = useFeature({ | ||
_: instance, | ||
items, | ||
getItemValue, | ||
onChange, | ||
inputRef, | ||
...state | ||
...contextual, | ||
...useTraversal(contextual) | ||
}); | ||
const { | ||
onBlur, | ||
onKeyDown, | ||
...featureInputProps | ||
@@ -55,9 +57,2 @@ } = _getInputProps(); | ||
onBlur: e => !instance.a && (onBlur == null ? void 0 : onBlur(e)), | ||
onKeyDown: e => { | ||
const { | ||
key | ||
} = e; | ||
if (items.length && (key === 'ArrowUp' || key === 'ArrowDown')) e.preventDefault(); | ||
onKeyDown == null || onKeyDown(e); | ||
}, | ||
ref: inputRef | ||
@@ -94,12 +89,11 @@ }); | ||
const autocomplete = ({ | ||
rovingText, | ||
traverseInput | ||
rovingText | ||
} = {}) => ({ | ||
_: cxInstance, | ||
items, | ||
getItemValue, | ||
traverse, | ||
onChange, | ||
setInputValue, | ||
focusIndex, | ||
setFocusIndex, | ||
focusItem, | ||
setFocusItem, | ||
open, | ||
@@ -122,18 +116,10 @@ setOpen, | ||
setOpen(false); | ||
setFocusIndex(-1); | ||
setFocusItem(); | ||
} | ||
}; | ||
const traverseItems = isUp => { | ||
const baseIndex = (traverseInput != null ? traverseInput : rovingText) ? -1 : 0; | ||
let nextIndex = focusIndex; | ||
const itemLength = items.length; | ||
if (isUp) { | ||
if (--nextIndex < baseIndex) nextIndex = itemLength - 1; | ||
} else { | ||
if (++nextIndex >= itemLength) nextIndex = baseIndex; | ||
} | ||
setFocusIndex(nextIndex); | ||
const traverseItems = isForward => { | ||
const nextItem = traverse(isForward); | ||
if (rovingText) { | ||
var _getItemValue; | ||
setInputValue((_getItemValue = getItemValue(items[nextIndex])) != null ? _getItemValue : cxInstance.b); | ||
setInputValue((_getItemValue = getItemValue(nextItem)) != null ? _getItemValue : cxInstance.b); | ||
const input = inputRef.current; | ||
@@ -145,3 +131,3 @@ cxInstance.c = [input.selectionStart, input.selectionEnd]; | ||
onChange: e => { | ||
setFocusIndex(-1); | ||
setFocusItem(); | ||
setOpen(true); | ||
@@ -158,3 +144,3 @@ updateValue(e.target.value, 'input'); | ||
if (cxInstance.b !== value && (selectionStart !== start || selectionEnd !== end)) { | ||
setFocusIndex(-1); | ||
setFocusItem(); | ||
updateValue(value, 'input'); | ||
@@ -164,17 +150,10 @@ } | ||
onClick: () => setOpen(true), | ||
onBlur: () => updateAndCloseList(getItemValue(items[focusIndex]), 'blur'), | ||
onKeyDown: ({ | ||
key | ||
}) => { | ||
switch (key) { | ||
onBlur: () => updateAndCloseList(getItemValue(focusItem), 'blur'), | ||
onKeyDown: e => { | ||
switch (e.key) { | ||
case 'ArrowUp': | ||
if (open) { | ||
traverseItems(true); | ||
} else { | ||
setOpen(true); | ||
} | ||
break; | ||
case 'ArrowDown': | ||
e.preventDefault(); | ||
if (open) { | ||
traverseItems(false); | ||
traverseItems(e.key === 'ArrowDown'); | ||
} else { | ||
@@ -185,3 +164,3 @@ setOpen(true); | ||
case 'Enter': | ||
updateAndCloseList(getItemValue(items[focusIndex]), 'submit'); | ||
updateAndCloseList(getItemValue(focusItem), 'submit'); | ||
break; | ||
@@ -194,4 +173,6 @@ case 'Escape': | ||
}); | ||
const getItemProps = option => ({ | ||
onClick: () => updateAndCloseList(getItemValue(items[option == null ? void 0 : option.index]), 'submit') | ||
const getItemProps = ({ | ||
item | ||
}) => ({ | ||
onClick: () => updateAndCloseList(getItemValue(item), 'submit') | ||
}); | ||
@@ -208,3 +189,3 @@ return { | ||
}); | ||
const useSupercomplete = cx => { | ||
return cx => { | ||
const { | ||
@@ -217,4 +198,5 @@ getInputProps: _getInputProps, | ||
inputRef, | ||
getItemValue, | ||
setInputValue, | ||
setFocusIndex, | ||
setFocusItem, | ||
_: cxInstance | ||
@@ -235,4 +217,3 @@ } = cx; | ||
inlineComplete: react.useCallback(({ | ||
index = 0, | ||
value | ||
item | ||
}) => { | ||
@@ -242,3 +223,4 @@ if (instance.c) { | ||
instance.c = 0; | ||
setFocusIndex(index); | ||
setFocusItem(item); | ||
const value = getItemValue(item); | ||
const start = cxInstance.b.length; | ||
@@ -250,10 +232,44 @@ const end = value.length; | ||
} | ||
}, [cxInstance, instance, inputRef, setFocusIndex, setInputValue]) | ||
}, [cxInstance, instance, inputRef, getItemValue, setFocusItem, setInputValue]) | ||
}; | ||
}; | ||
return useSupercomplete; | ||
}; | ||
const linearTraversal = ({ | ||
traverseInput, | ||
isItemDisabled, | ||
items = [] | ||
}) => ({ | ||
focusItem, | ||
setFocusItem | ||
}) => { | ||
const [instance] = react.useState({ | ||
a: -1 | ||
}); | ||
return { | ||
traverse: isForward => { | ||
if (!focusItem) instance.a = -1;else if (focusItem !== items[instance.a]) instance.a = items.indexOf(focusItem); | ||
const baseIndex = traverseInput ? -1 : 0; | ||
let nextIndex = instance.a; | ||
let nextItem; | ||
const itemLength = items.length; | ||
do { | ||
if (isForward) { | ||
if (++nextIndex >= itemLength) nextIndex = baseIndex; | ||
} else { | ||
if (--nextIndex < baseIndex) nextIndex = itemLength - 1; | ||
} | ||
nextItem = items[nextIndex]; | ||
if (!nextItem || !(isItemDisabled != null && isItemDisabled(nextItem))) break; | ||
} while (nextIndex !== instance.a); | ||
instance.a = nextIndex; | ||
setFocusItem(nextItem); | ||
return nextItem; | ||
} | ||
}; | ||
}; | ||
exports.autocomplete = autocomplete; | ||
exports.linearTraversal = linearTraversal; | ||
exports.supercomplete = supercomplete; | ||
exports.useAutocomplete = useAutocomplete; |
const autocomplete = ({ | ||
rovingText, | ||
traverseInput | ||
rovingText | ||
} = {}) => ({ | ||
_: cxInstance, | ||
items, | ||
getItemValue, | ||
traverse, | ||
onChange, | ||
setInputValue, | ||
focusIndex, | ||
setFocusIndex, | ||
focusItem, | ||
setFocusItem, | ||
open, | ||
@@ -29,18 +28,10 @@ setOpen, | ||
setOpen(false); | ||
setFocusIndex(-1); | ||
setFocusItem(); | ||
} | ||
}; | ||
const traverseItems = isUp => { | ||
const baseIndex = (traverseInput != null ? traverseInput : rovingText) ? -1 : 0; | ||
let nextIndex = focusIndex; | ||
const itemLength = items.length; | ||
if (isUp) { | ||
if (--nextIndex < baseIndex) nextIndex = itemLength - 1; | ||
} else { | ||
if (++nextIndex >= itemLength) nextIndex = baseIndex; | ||
} | ||
setFocusIndex(nextIndex); | ||
const traverseItems = isForward => { | ||
const nextItem = traverse(isForward); | ||
if (rovingText) { | ||
var _getItemValue; | ||
setInputValue((_getItemValue = getItemValue(items[nextIndex])) != null ? _getItemValue : cxInstance.b); | ||
setInputValue((_getItemValue = getItemValue(nextItem)) != null ? _getItemValue : cxInstance.b); | ||
const input = inputRef.current; | ||
@@ -52,3 +43,3 @@ cxInstance.c = [input.selectionStart, input.selectionEnd]; | ||
onChange: e => { | ||
setFocusIndex(-1); | ||
setFocusItem(); | ||
setOpen(true); | ||
@@ -65,3 +56,3 @@ updateValue(e.target.value, 'input'); | ||
if (cxInstance.b !== value && (selectionStart !== start || selectionEnd !== end)) { | ||
setFocusIndex(-1); | ||
setFocusItem(); | ||
updateValue(value, 'input'); | ||
@@ -71,17 +62,10 @@ } | ||
onClick: () => setOpen(true), | ||
onBlur: () => updateAndCloseList(getItemValue(items[focusIndex]), 'blur'), | ||
onKeyDown: ({ | ||
key | ||
}) => { | ||
switch (key) { | ||
onBlur: () => updateAndCloseList(getItemValue(focusItem), 'blur'), | ||
onKeyDown: e => { | ||
switch (e.key) { | ||
case 'ArrowUp': | ||
if (open) { | ||
traverseItems(true); | ||
} else { | ||
setOpen(true); | ||
} | ||
break; | ||
case 'ArrowDown': | ||
e.preventDefault(); | ||
if (open) { | ||
traverseItems(false); | ||
traverseItems(e.key === 'ArrowDown'); | ||
} else { | ||
@@ -92,3 +76,3 @@ setOpen(true); | ||
case 'Enter': | ||
updateAndCloseList(getItemValue(items[focusIndex]), 'submit'); | ||
updateAndCloseList(getItemValue(focusItem), 'submit'); | ||
break; | ||
@@ -101,4 +85,6 @@ case 'Escape': | ||
}); | ||
const getItemProps = option => ({ | ||
onClick: () => updateAndCloseList(getItemValue(items[option == null ? void 0 : option.index]), 'submit') | ||
const getItemProps = ({ | ||
item | ||
}) => ({ | ||
onClick: () => updateAndCloseList(getItemValue(item), 'submit') | ||
}); | ||
@@ -105,0 +91,0 @@ return { |
@@ -8,3 +8,3 @@ import { useState, useCallback } from 'react'; | ||
}); | ||
const useSupercomplete = cx => { | ||
return cx => { | ||
const { | ||
@@ -17,4 +17,5 @@ getInputProps: _getInputProps, | ||
inputRef, | ||
getItemValue, | ||
setInputValue, | ||
setFocusIndex, | ||
setFocusItem, | ||
_: cxInstance | ||
@@ -35,4 +36,3 @@ } = cx; | ||
inlineComplete: useCallback(({ | ||
index = 0, | ||
value | ||
item | ||
}) => { | ||
@@ -42,3 +42,4 @@ if (instance.c) { | ||
instance.c = 0; | ||
setFocusIndex(index); | ||
setFocusItem(item); | ||
const value = getItemValue(item); | ||
const start = cxInstance.b.length; | ||
@@ -50,8 +51,7 @@ const end = value.length; | ||
} | ||
}, [cxInstance, instance, inputRef, setFocusIndex, setInputValue]) | ||
}, [cxInstance, instance, inputRef, getItemValue, setFocusItem, setInputValue]) | ||
}; | ||
}; | ||
return useSupercomplete; | ||
}; | ||
export { supercomplete }; |
import { useRef, useState, useCallback } from 'react'; | ||
const useAutocomplete = ({ | ||
items = [], | ||
onChange = () => {}, | ||
feature: useFeature, | ||
traversal: useTraversal, | ||
getItemValue: _getItemValue | ||
@@ -11,3 +11,3 @@ }) => { | ||
const [open, setOpen] = useState(false); | ||
const [focusIndex, setFocusIndex] = useState(-1); | ||
const [focusItem, setFocusItem] = useState(); | ||
const [instance] = useState({ | ||
@@ -17,3 +17,3 @@ b: '', | ||
}); | ||
const getItemValue = item => item == null ? null : _getItemValue ? _getItemValue(item) : typeof item === 'string' ? item : null; | ||
const getItemValue = useCallback(item => item == null ? null : _getItemValue ? _getItemValue(item) : item.toString(), [_getItemValue]); | ||
const setInputValue = useCallback(value => { | ||
@@ -25,7 +25,14 @@ const input = inputRef.current; | ||
setInputValue, | ||
focusIndex, | ||
setFocusIndex, | ||
focusItem, | ||
setFocusItem, | ||
open, | ||
setOpen | ||
}; | ||
const contextual = { | ||
_: instance, | ||
getItemValue, | ||
onChange, | ||
inputRef, | ||
...state | ||
}; | ||
const { | ||
@@ -36,12 +43,7 @@ getInputProps: _getInputProps, | ||
} = useFeature({ | ||
_: instance, | ||
items, | ||
getItemValue, | ||
onChange, | ||
inputRef, | ||
...state | ||
...contextual, | ||
...useTraversal(contextual) | ||
}); | ||
const { | ||
onBlur, | ||
onKeyDown, | ||
...featureInputProps | ||
@@ -52,9 +54,2 @@ } = _getInputProps(); | ||
onBlur: e => !instance.a && (onBlur == null ? void 0 : onBlur(e)), | ||
onKeyDown: e => { | ||
const { | ||
key | ||
} = e; | ||
if (items.length && (key === 'ArrowUp' || key === 'ArrowDown')) e.preventDefault(); | ||
onKeyDown == null || onKeyDown(e); | ||
}, | ||
ref: inputRef | ||
@@ -61,0 +56,0 @@ }); |
export { useAutocomplete } from './hooks/useAutocomplete.js'; | ||
export { autocomplete } from './features/autocomplete.js'; | ||
export { supercomplete } from './features/supercomplete.js'; | ||
export { linearTraversal } from './traversals/linearTraversal.js'; |
{ | ||
"name": "@szhsin/react-autocomplete", | ||
"version": "0.7.0", | ||
"version": "0.8.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "author": "Zheng Song", |
import type { HTMLAttributes, InputHTMLAttributes } from 'react'; | ||
export interface GetProps { | ||
export interface GetProps<T> { | ||
getInputProps: () => InputHTMLAttributes<HTMLInputElement>; | ||
getItemProps: (option?: { | ||
index?: number; | ||
getItemProps: (option: { | ||
item: T; | ||
}) => HTMLAttributes<HTMLElement>; | ||
} | ||
export interface AutocompleteState { | ||
export interface AutocompleteState<T> { | ||
setInputValue: (value: string) => void; | ||
focusIndex: number; | ||
setFocusIndex: (value: number) => void; | ||
focusItem: T | null | undefined; | ||
setFocusItem: (item?: T | null | undefined) => void; | ||
open: boolean; | ||
@@ -16,7 +16,6 @@ setOpen: (value: boolean) => void; | ||
export type ChangeType = 'submit' | 'input' | 'blur' | 'esc'; | ||
export interface ContextualProps<T> { | ||
export interface ContextualProps { | ||
onChange: (value: string, meta: { | ||
type: ChangeType; | ||
}) => void; | ||
items: T[]; | ||
} | ||
@@ -40,3 +39,3 @@ export interface Instance { | ||
} | ||
export interface Contextual<T> extends ContextualProps<T>, AutocompleteState { | ||
export interface Contextual<T> extends ContextualProps, AutocompleteState<T> { | ||
inputRef: React.RefObject<HTMLInputElement>; | ||
@@ -49,9 +48,17 @@ getItemValue: (item: T | undefined | null) => string | undefined | null; | ||
} | ||
export type Feature<T, Actions = object> = (cx: Contextual<T>) => GetProps & Actions; | ||
export interface TraversalProps<T> { | ||
traverseInput?: boolean; | ||
isItemDisabled?: (item: T) => boolean; | ||
} | ||
export type Traversal<T> = (cx: Contextual<T>) => { | ||
traverse: (isForward: boolean) => T | null | undefined; | ||
}; | ||
export type Feature<T, Actions = object> = (cx: Contextual<T> & ReturnType<Traversal<T>>) => GetProps<T> & Actions; | ||
interface GetItemValue<T> { | ||
getItemValue: (item: T) => string; | ||
} | ||
export type AutocompleteProps<T, FeatureActions = object> = Partial<ContextualProps<T>> & { | ||
export type AutocompleteProps<T, FeatureActions = object> = Partial<ContextualProps> & { | ||
feature: Feature<T, FeatureActions>; | ||
traversal: Traversal<T>; | ||
} & (T extends string ? Partial<GetItemValue<T>> : GetItemValue<T>); | ||
export {}; |
import type { Feature } from '../common'; | ||
declare const autocomplete: <T>(props?: { | ||
rovingText?: boolean; | ||
traverseInput?: boolean; | ||
declare const autocomplete: <T>({ rovingText }?: { | ||
rovingText?: boolean | undefined; | ||
}) => Feature<T>; | ||
export { autocomplete }; |
import type { Feature } from '../common'; | ||
export interface Instance { | ||
/** | ||
* ### INTERNAL API ### | ||
* Whether the last value change is "insertText" | ||
*/ | ||
c?: boolean | 0 | 1; | ||
} | ||
declare const supercomplete: <T>() => Feature<T, { | ||
inlineComplete: (props: { | ||
index?: number; | ||
value: string; | ||
item: T; | ||
}) => void; | ||
}>; | ||
export { supercomplete }; |
/// <reference types="react" /> | ||
import type { GetProps, AutocompleteProps } from '../common'; | ||
declare const useAutocomplete: <T, FeatureActions>({ items, onChange, feature: useFeature, getItemValue: _getItemValue }: AutocompleteProps<T, FeatureActions>) => { | ||
declare const useAutocomplete: <T, FeatureActions>({ onChange, feature: useFeature, traversal: useTraversal, getItemValue: _getItemValue }: AutocompleteProps<T, FeatureActions>) => { | ||
setInputValue: (value: string) => void; | ||
focusIndex: number; | ||
setFocusIndex: (value: number) => void; | ||
focusItem: T | null | undefined; | ||
setFocusItem: (item?: T | null | undefined) => void; | ||
open: boolean; | ||
setOpen: (value: boolean) => void; | ||
getInputProps: () => import("react").InputHTMLAttributes<HTMLInputElement>; | ||
getItemProps: (option?: { | ||
index?: number | undefined; | ||
} | undefined) => import("react").HTMLAttributes<HTMLElement>; | ||
} & Omit<GetProps & FeatureActions, "getInputProps" | "getItemProps">; | ||
getItemProps: (option: { | ||
item: T; | ||
}) => import("react").HTMLAttributes<HTMLElement>; | ||
} & Omit<GetProps<T> & FeatureActions, "getInputProps" | "getItemProps">; | ||
export { useAutocomplete }; |
export { useAutocomplete } from './hooks/useAutocomplete'; | ||
export { autocomplete } from './features/autocomplete'; | ||
export { supercomplete } from './features/supercomplete'; | ||
export { linearTraversal } from './traversals/linearTraversal'; | ||
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
19926
14
606