@szhsin/react-autocomplete
Advanced tools
Comparing version 0.8.6 to 0.8.7
@@ -7,19 +7,2 @@ 'use strict'; | ||
const mergeEvents = (events1, events2) => { | ||
const result = { | ||
...events1 | ||
}; | ||
Object.keys(events2).forEach(key => { | ||
const e2 = events2[key]; | ||
if (e2) { | ||
const e1 = events1[key]; | ||
result[key] = e1 ? e => { | ||
e1(e); | ||
e2(e); | ||
} : e2; | ||
} | ||
}); | ||
return result; | ||
}; | ||
const useAutocomplete = ({ | ||
@@ -37,4 +20,3 @@ onChange = () => {}, | ||
const mutable = useMutableState({ | ||
b: '', | ||
c: [] | ||
b: '' | ||
}); | ||
@@ -63,36 +45,9 @@ const getItemValue = react.useCallback(item => item == null ? '' : _getItemValue ? _getItemValue(item) : item.toString(), [_getItemValue]); | ||
}; | ||
const { | ||
getInputProps: _getInputProps, | ||
getListProps: _getListProps, | ||
...restFeature | ||
} = useFeature({ | ||
const featureYield = useFeature({ | ||
...contextual, | ||
...useTraversal(contextual) | ||
}); | ||
const getInputProps = () => { | ||
const { | ||
onBlur, | ||
...rest | ||
} = _getInputProps(); | ||
return { | ||
...rest, | ||
onBlur: e => !mutable.a && (onBlur == null ? void 0 : onBlur(e)), | ||
ref: inputRef | ||
}; | ||
}; | ||
const getListProps = () => mergeEvents(_getListProps(), { | ||
onMouseDown: () => { | ||
mutable.a = 1; | ||
}, | ||
onClick: () => { | ||
var _inputRef$current; | ||
(_inputRef$current = inputRef.current) == null || _inputRef$current.focus(); | ||
mutable.a = 0; | ||
} | ||
}); | ||
return { | ||
getInputProps, | ||
getListProps, | ||
...state, | ||
...restFeature | ||
...featureYield | ||
}; | ||
@@ -155,2 +110,3 @@ }; | ||
}) => { | ||
const mutable = useMutableState({}); | ||
const updateValue = (value, moveCaretToEnd = true) => { | ||
@@ -174,11 +130,14 @@ setInputValue(value); | ||
}; | ||
const traverseItems = isForward => { | ||
const nextItem = traverse(isForward); | ||
if (rovingText) { | ||
setInputValue(getItemValue(nextItem) || cxMutable.b); | ||
const input = inputRef.current; | ||
cxMutable.c = [input.selectionStart, input.selectionEnd]; | ||
const getListProps = () => ({ | ||
onMouseDown: () => { | ||
mutable.a = 1; | ||
}, | ||
onClick: () => { | ||
var _inputRef$current; | ||
(_inputRef$current = inputRef.current) == null || _inputRef$current.focus(); | ||
mutable.a = 0; | ||
} | ||
}; | ||
}); | ||
const getInputProps = () => ({ | ||
ref: inputRef, | ||
onChange: e => { | ||
@@ -189,17 +148,5 @@ setFocusItem(); | ||
}, | ||
onSelect: e => { | ||
const { | ||
value, | ||
selectionStart, | ||
selectionEnd | ||
} = e.target; | ||
const [start, end] = cxMutable.c; | ||
if (cxMutable.b != value && (selectionStart != start || selectionEnd != end)) { | ||
setFocusItem(); | ||
updateValue(value, false); | ||
} | ||
}, | ||
onClick: () => setOpen(true), | ||
onBlur: () => { | ||
if (!open) return; | ||
if (mutable.a || !open) return; | ||
if (focusItem) { | ||
@@ -220,3 +167,4 @@ updateAll(focusItem); | ||
if (open) { | ||
traverseItems(e.key != 'ArrowUp'); | ||
const nextItem = traverse(e.key != 'ArrowUp'); | ||
if (rovingText) setInputValue(getItemValue(nextItem) || cxMutable.b); | ||
} else { | ||
@@ -260,50 +208,122 @@ setOpen(true); | ||
getItemProps, | ||
getListProps: () => ({}) | ||
getListProps | ||
}; | ||
}; | ||
const supercomplete = props => { | ||
const useAutocomplete = autocomplete({ | ||
...props, | ||
rovingText: true | ||
const mergeObjects = (obj1, obj2) => { | ||
const merged = { | ||
...obj1 | ||
}; | ||
Object.entries(obj2).forEach(([key, prop2]) => { | ||
if (typeof prop2 === 'function') { | ||
const prop1 = obj1[key]; | ||
merged[key] = prop1 ? (...args) => { | ||
const result1 = prop1(...args); | ||
const result2 = prop2(...args); | ||
if (typeof result1 === 'object') { | ||
return mergeObjects(result1, result2); | ||
} | ||
} : prop2; | ||
} else { | ||
merged[key] = prop2; | ||
} | ||
}); | ||
return cx => { | ||
const { | ||
getInputProps: _getInputProps, | ||
...rest | ||
} = useAutocomplete(cx); | ||
const mutable = useMutableState({}); | ||
const { | ||
inputRef, | ||
getItemValue, | ||
setInputValue, | ||
setFocusItem, | ||
$: cxMutable | ||
} = cx; | ||
return { | ||
...rest, | ||
getInputProps: () => mergeEvents({ | ||
onChange: e => { | ||
mutable.c = e.nativeEvent.inputType === 'insertText'; | ||
return merged; | ||
}; | ||
const mergeFeatures = (...features) => cx => features.reduce((accu, curr) => mergeObjects(accu, curr(cx)), {}); | ||
const inline = () => ({ | ||
inputRef, | ||
getItemValue, | ||
setInputValue, | ||
setFocusItem, | ||
$: cxMutable | ||
}) => { | ||
const mutable = useMutableState({}); | ||
return { | ||
getInputProps: () => ({ | ||
onChange: e => { | ||
mutable.c = e.nativeEvent.inputType === 'insertText'; | ||
} | ||
}), | ||
inlineComplete: react.useCallback(({ | ||
item | ||
}) => { | ||
if (mutable.c) { | ||
var _inputRef$current; | ||
mutable.c = 0; | ||
setFocusItem(item); | ||
const value = getItemValue(item); | ||
const start = cxMutable.b.length; | ||
const end = value.length; | ||
setInputValue(cxMutable.b + value.slice(start)); | ||
(_inputRef$current = inputRef.current) == null || _inputRef$current.setSelectionRange(start, end); | ||
} | ||
}, [cxMutable, mutable, inputRef, getItemValue, setFocusItem, setInputValue]) | ||
}; | ||
}; | ||
const supercomplete = props => mergeFeatures(inline(), autocomplete({ | ||
...props, | ||
rovingText: true | ||
})); | ||
const toggle = () => ({ | ||
inputRef, | ||
open, | ||
setOpen, | ||
focusItem | ||
}) => { | ||
const mutable = useMutableState({}); | ||
const toggleRef = react.useRef(null); | ||
react.useEffect(() => { | ||
var _inputRef$current; | ||
if (open) (_inputRef$current = inputRef.current) == null || _inputRef$current.focus(); | ||
}, [open, inputRef]); | ||
return { | ||
getToggleProps: () => ({ | ||
ref: toggleRef, | ||
onMouseDown: () => { | ||
mutable.a = open; | ||
}, | ||
onClick: () => { | ||
if (mutable.a) { | ||
mutable.a = 0; | ||
} else { | ||
setOpen(true); | ||
} | ||
}, _getInputProps()), | ||
inlineComplete: react.useCallback(({ | ||
item | ||
}) => { | ||
if (mutable.c) { | ||
var _inputRef$current; | ||
mutable.c = 0; | ||
setFocusItem(item); | ||
const value = getItemValue(item); | ||
const start = cxMutable.b.length; | ||
const end = value.length; | ||
setInputValue(cxMutable.b + value.slice(start)); | ||
cxMutable.c = [start, end]; | ||
(_inputRef$current = inputRef.current) == null || _inputRef$current.setSelectionRange(start, end); | ||
}, | ||
onKeyDown: e => { | ||
const { | ||
key | ||
} = e; | ||
if (key === 'ArrowUp' || key === 'ArrowDown') { | ||
e.preventDefault(); | ||
setOpen(true); | ||
} | ||
}, [cxMutable, mutable, inputRef, getItemValue, setFocusItem, setInputValue]) | ||
}; | ||
} | ||
}), | ||
getInputProps: () => ({ | ||
onKeyDown: e => { | ||
var _toggleRef$current; | ||
const { | ||
key | ||
} = e; | ||
if (key === 'Escape') (_toggleRef$current = toggleRef.current) == null || _toggleRef$current.focus(); | ||
if (key === 'Enter' && focusItem) { | ||
var _toggleRef$current2; | ||
e.preventDefault(); | ||
(_toggleRef$current2 = toggleRef.current) == null || _toggleRef$current2.focus(); | ||
} | ||
} | ||
}) | ||
}; | ||
}; | ||
const dropdown = props => mergeFeatures(autocomplete({ | ||
...props, | ||
constricted: true | ||
}), toggle()); | ||
const linearTraversal = ({ | ||
@@ -364,2 +384,3 @@ traverseInput, | ||
exports.autocomplete = autocomplete; | ||
exports.dropdown = dropdown; | ||
exports.groupedTraversal = groupedTraversal; | ||
@@ -366,0 +387,0 @@ exports.linearTraversal = linearTraversal; |
@@ -0,1 +1,3 @@ | ||
import { useMutableState } from '../hooks/useMutableState.js'; | ||
const scrollIntoView = element => element == null ? void 0 : element.scrollIntoView({ | ||
@@ -22,2 +24,3 @@ block: 'nearest' | ||
}) => { | ||
const mutable = useMutableState({}); | ||
const updateValue = (value, moveCaretToEnd = true) => { | ||
@@ -41,11 +44,14 @@ setInputValue(value); | ||
}; | ||
const traverseItems = isForward => { | ||
const nextItem = traverse(isForward); | ||
if (rovingText) { | ||
setInputValue(getItemValue(nextItem) || cxMutable.b); | ||
const input = inputRef.current; | ||
cxMutable.c = [input.selectionStart, input.selectionEnd]; | ||
const getListProps = () => ({ | ||
onMouseDown: () => { | ||
mutable.a = 1; | ||
}, | ||
onClick: () => { | ||
var _inputRef$current; | ||
(_inputRef$current = inputRef.current) == null || _inputRef$current.focus(); | ||
mutable.a = 0; | ||
} | ||
}; | ||
}); | ||
const getInputProps = () => ({ | ||
ref: inputRef, | ||
onChange: e => { | ||
@@ -56,17 +62,5 @@ setFocusItem(); | ||
}, | ||
onSelect: e => { | ||
const { | ||
value, | ||
selectionStart, | ||
selectionEnd | ||
} = e.target; | ||
const [start, end] = cxMutable.c; | ||
if (cxMutable.b != value && (selectionStart != start || selectionEnd != end)) { | ||
setFocusItem(); | ||
updateValue(value, false); | ||
} | ||
}, | ||
onClick: () => setOpen(true), | ||
onBlur: () => { | ||
if (!open) return; | ||
if (mutable.a || !open) return; | ||
if (focusItem) { | ||
@@ -87,3 +81,4 @@ updateAll(focusItem); | ||
if (open) { | ||
traverseItems(e.key != 'ArrowUp'); | ||
const nextItem = traverse(e.key != 'ArrowUp'); | ||
if (rovingText) setInputValue(getItemValue(nextItem) || cxMutable.b); | ||
} else { | ||
@@ -127,3 +122,3 @@ setOpen(true); | ||
getItemProps, | ||
getListProps: () => ({}) | ||
getListProps | ||
}; | ||
@@ -130,0 +125,0 @@ }; |
@@ -1,50 +0,10 @@ | ||
import { useCallback } from 'react'; | ||
import { mergeEvents } from '../utils/mergeEvents.js'; | ||
import { useMutableState } from '../hooks/useMutableState.js'; | ||
import { mergeFeatures } from '../utils/mergeFeatures.js'; | ||
import { autocomplete } from './autocomplete.js'; | ||
import { inline } from './inline.js'; | ||
const supercomplete = props => { | ||
const useAutocomplete = autocomplete({ | ||
...props, | ||
rovingText: true | ||
}); | ||
return cx => { | ||
const { | ||
getInputProps: _getInputProps, | ||
...rest | ||
} = useAutocomplete(cx); | ||
const mutable = useMutableState({}); | ||
const { | ||
inputRef, | ||
getItemValue, | ||
setInputValue, | ||
setFocusItem, | ||
$: cxMutable | ||
} = cx; | ||
return { | ||
...rest, | ||
getInputProps: () => mergeEvents({ | ||
onChange: e => { | ||
mutable.c = e.nativeEvent.inputType === 'insertText'; | ||
} | ||
}, _getInputProps()), | ||
inlineComplete: useCallback(({ | ||
item | ||
}) => { | ||
if (mutable.c) { | ||
var _inputRef$current; | ||
mutable.c = 0; | ||
setFocusItem(item); | ||
const value = getItemValue(item); | ||
const start = cxMutable.b.length; | ||
const end = value.length; | ||
setInputValue(cxMutable.b + value.slice(start)); | ||
cxMutable.c = [start, end]; | ||
(_inputRef$current = inputRef.current) == null || _inputRef$current.setSelectionRange(start, end); | ||
} | ||
}, [cxMutable, mutable, inputRef, getItemValue, setFocusItem, setInputValue]) | ||
}; | ||
}; | ||
}; | ||
const supercomplete = props => mergeFeatures(inline(), autocomplete({ | ||
...props, | ||
rovingText: true | ||
})); | ||
export { supercomplete }; |
import { useRef, useState, useCallback } from 'react'; | ||
import { useMutableState } from './useMutableState.js'; | ||
import { mergeEvents } from '../utils/mergeEvents.js'; | ||
@@ -17,4 +16,3 @@ const useAutocomplete = ({ | ||
const mutable = useMutableState({ | ||
b: '', | ||
c: [] | ||
b: '' | ||
}); | ||
@@ -43,36 +41,9 @@ const getItemValue = useCallback(item => item == null ? '' : _getItemValue ? _getItemValue(item) : item.toString(), [_getItemValue]); | ||
}; | ||
const { | ||
getInputProps: _getInputProps, | ||
getListProps: _getListProps, | ||
...restFeature | ||
} = useFeature({ | ||
const featureYield = useFeature({ | ||
...contextual, | ||
...useTraversal(contextual) | ||
}); | ||
const getInputProps = () => { | ||
const { | ||
onBlur, | ||
...rest | ||
} = _getInputProps(); | ||
return { | ||
...rest, | ||
onBlur: e => !mutable.a && (onBlur == null ? void 0 : onBlur(e)), | ||
ref: inputRef | ||
}; | ||
}; | ||
const getListProps = () => mergeEvents(_getListProps(), { | ||
onMouseDown: () => { | ||
mutable.a = 1; | ||
}, | ||
onClick: () => { | ||
var _inputRef$current; | ||
(_inputRef$current = inputRef.current) == null || _inputRef$current.focus(); | ||
mutable.a = 0; | ||
} | ||
}); | ||
return { | ||
getInputProps, | ||
getListProps, | ||
...state, | ||
...restFeature | ||
...featureYield | ||
}; | ||
@@ -79,0 +50,0 @@ }; |
@@ -5,3 +5,4 @@ export { useAutocomplete } from './hooks/useAutocomplete.js'; | ||
export { supercomplete } from './features/supercomplete.js'; | ||
export { dropdown } from './features/dropdown.js'; | ||
export { linearTraversal } from './traversals/linearTraversal.js'; | ||
export { groupedTraversal } from './traversals/groupedTraversal.js'; |
{ | ||
"name": "@szhsin/react-autocomplete", | ||
"version": "0.8.6", | ||
"version": "0.8.7", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "author": "Zheng Song", |
@@ -1,2 +0,2 @@ | ||
import type { HTMLAttributes, InputHTMLAttributes } from 'react'; | ||
import type { HTMLAttributes, InputHTMLAttributes, ButtonHTMLAttributes } from 'react'; | ||
export type PropsWithObjectRef<T> = T extends HTMLAttributes<infer E> ? T & { | ||
@@ -6,3 +6,4 @@ ref: React.RefObject<E>; | ||
export interface GetProps<T> { | ||
getInputProps: () => InputHTMLAttributes<HTMLInputElement>; | ||
getInputProps: () => PropsWithObjectRef<InputHTMLAttributes<HTMLInputElement>>; | ||
getToggleProps: () => PropsWithObjectRef<ButtonHTMLAttributes<HTMLButtonElement>>; | ||
getListProps: () => HTMLAttributes<HTMLElement>; | ||
@@ -29,15 +30,5 @@ getItemProps: (option: { | ||
* ### INTERNAL API ### | ||
* Whether to bypass onblur event on input | ||
*/ | ||
a?: number; | ||
/** | ||
* ### INTERNAL API ### | ||
* The most recent value | ||
*/ | ||
b: string; | ||
/** | ||
* ### INTERNAL API ### | ||
* The last recorded selection position | ||
*/ | ||
c: [number | null, number | null] | []; | ||
} | ||
@@ -58,10 +49,12 @@ export interface Contextual<T> extends ContextualProps<T>, AutocompleteState<T> { | ||
}; | ||
export type Feature<T, Actions = object> = (cx: Contextual<T> & ReturnType<Traversal<T>>) => GetProps<T> & Actions; | ||
export type Feature<T, Yield extends object> = (cx: Contextual<T> & ReturnType<Traversal<T>>) => Yield; | ||
export type MergedFeatureYield<T, Features> = Features extends readonly [Feature<T, infer S>] ? S : Features extends readonly [Feature<T, infer F>, ...infer R] ? F & MergedFeatureYield<T, R> : never; | ||
export type MergedFeature<T, Features> = Feature<T, MergedFeatureYield<T, Features>>; | ||
interface GetItemValue<T> { | ||
getItemValue: (item: T) => string; | ||
} | ||
export type AutocompleteProps<T, FeatureActions = object> = Partial<ContextualProps<T>> & { | ||
feature: Feature<T, FeatureActions>; | ||
export type AutocompleteProps<T, FeatureYield extends object = object> = Partial<ContextualProps<T>> & { | ||
feature: Feature<T, FeatureYield>; | ||
traversal: Traversal<T>; | ||
} & (T extends string ? Partial<GetItemValue<T>> : GetItemValue<T>); | ||
export {}; |
@@ -1,6 +0,7 @@ | ||
import type { Feature } from '../common'; | ||
import type { Feature, GetProps } from '../common'; | ||
type AutocompleteFeature<T> = Feature<T, Pick<GetProps<T>, 'getInputProps' | 'getListProps' | 'getItemProps'>>; | ||
declare const autocomplete: <T>({ rovingText, constricted }?: { | ||
rovingText?: boolean | undefined; | ||
constricted?: boolean | undefined; | ||
}) => Feature<T>; | ||
export { autocomplete }; | ||
}) => AutocompleteFeature<T>; | ||
export { type AutocompleteFeature, autocomplete }; |
@@ -1,9 +0,8 @@ | ||
import type { Feature } from '../common'; | ||
import type { MergedFeature } from '../common'; | ||
import { type AutocompleteFeature } from './autocomplete'; | ||
import { type InlineFeature } from './inline'; | ||
type SupercompleteFeature<T> = MergedFeature<T, [InlineFeature<T>, AutocompleteFeature<T>]>; | ||
declare const supercomplete: <T>(props?: { | ||
constricted?: boolean; | ||
}) => Feature<T, { | ||
inlineComplete: (props: { | ||
item: T; | ||
}) => void; | ||
}>; | ||
export { supercomplete }; | ||
}) => SupercompleteFeature<T>; | ||
export { type SupercompleteFeature, supercomplete }; |
@@ -1,4 +0,3 @@ | ||
import type { InputHTMLAttributes } from 'react'; | ||
import type { GetProps, AutocompleteProps, PropsWithObjectRef } from '../common'; | ||
declare const useAutocomplete: <T, FeatureActions>({ onChange, isItemDisabled, feature: useFeature, traversal: useTraversal, getItemValue: _getItemValue }: AutocompleteProps<T, FeatureActions>) => { | ||
import type { AutocompleteProps } from '../common'; | ||
declare const useAutocomplete: <T, FeatureYield extends object>({ onChange, isItemDisabled, feature: useFeature, traversal: useTraversal, getItemValue: _getItemValue }: AutocompleteProps<T, FeatureYield>) => { | ||
setInputValue: (value: string) => void; | ||
@@ -11,5 +10,3 @@ focusItem: T | undefined; | ||
setOpen: (value: boolean) => void; | ||
getInputProps: () => PropsWithObjectRef<InputHTMLAttributes<HTMLInputElement>>; | ||
getListProps: () => import("react").HTMLAttributes<HTMLElement>; | ||
} & Omit<GetProps<T> & FeatureActions, "getListProps" | "getInputProps">; | ||
} & FeatureYield; | ||
export { useAutocomplete }; |
@@ -5,4 +5,5 @@ export { useAutocomplete } from './hooks/useAutocomplete'; | ||
export { supercomplete } from './features/supercomplete'; | ||
export { dropdown } from './features/dropdown'; | ||
export { linearTraversal } from './traversals/linearTraversal'; | ||
export { groupedTraversal } from './traversals/groupedTraversal'; | ||
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
31734
35
932