Skip to content

Commit

Permalink
Merge pull request #44 from szhsin/feat/search
Browse files Browse the repository at this point in the history
feat: search
  • Loading branch information
szhsin authored Jul 9, 2024
2 parents 064009b + daa5ee0 commit db809e5
Show file tree
Hide file tree
Showing 18 changed files with 137 additions and 38 deletions.
9 changes: 5 additions & 4 deletions dist/cjs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,11 @@ const autocompleteLite = ({
!noAction && (onAction == null ? void 0 : onAction(item));
return true;
}
onSelectChange(item);
const itemValue = getItemValue(item);
if (!select) onChange(itemValue);
const endIndex = itemValue.length;
inputRef.current.setSelectionRange(endIndex, endIndex);
if (!select) onChange(itemValue);
onSelectChange(item);
};
const resetState = shouldClose => {
setFocusItem();
Expand All @@ -206,7 +206,7 @@ const autocompleteLite = ({
}
};
return {
clearable: !!inputValue,
isInputEmpty: !inputValue,
getClearProps: () => ({
tabIndex: -1,
onMouseDown: startCapture,
Expand Down Expand Up @@ -268,6 +268,7 @@ const autocompleteLite = ({
case 'Enter':
if (open) {
if (focusItem) {
e.preventDefault();
resetState(selectItemOrAction(focusItem));
} else if (!select) {
resetState(true);
Expand Down Expand Up @@ -389,7 +390,7 @@ const dropdownToggle = ({
return (_toggleRef$current = toggleRef.current) == null ? void 0 : _toggleRef$current.focus();
}, 0);
return {
clearable: !!inputValue,
isInputEmpty: !inputValue,
getToggleProps: () => ({
ref: toggleRef,
onMouseDown: startToggle,
Expand Down
7 changes: 4 additions & 3 deletions dist/esm/features/atom/autocompleteLite.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ const autocompleteLite = ({
!noAction && (onAction == null ? void 0 : onAction(item));
return true;
}
onSelectChange(item);
const itemValue = getItemValue(item);
if (!select) onChange(itemValue);
const endIndex = itemValue.length;
inputRef.current.setSelectionRange(endIndex, endIndex);
if (!select) onChange(itemValue);
onSelectChange(item);
};
const resetState = shouldClose => {
setFocusItem();
Expand All @@ -52,7 +52,7 @@ const autocompleteLite = ({
}
};
return {
clearable: !!inputValue,
isInputEmpty: !inputValue,
getClearProps: () => ({
tabIndex: -1,
onMouseDown: startCapture,
Expand Down Expand Up @@ -114,6 +114,7 @@ const autocompleteLite = ({
case 'Enter':
if (open) {
if (focusItem) {
e.preventDefault();
resetState(selectItemOrAction(focusItem));
} else if (!select) {
resetState(true);
Expand Down
2 changes: 1 addition & 1 deletion dist/esm/features/atom/dropdownToggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const dropdownToggle = ({
return (_toggleRef$current = toggleRef.current) == null ? void 0 : _toggleRef$current.focus();
}, 0);
return {
clearable: !!inputValue,
isInputEmpty: !inputValue,
getToggleProps: () => ({
ref: toggleRef,
onMouseDown: startToggle,
Expand Down
2 changes: 1 addition & 1 deletion examples/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions examples/pages/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function Dropdown() {
getItemProps,
getToggleProps,
getClearProps,
clearable,
isInputEmpty,
open,
focusItem,
inputRef
Expand Down Expand Up @@ -128,7 +128,7 @@ export default function Dropdown() {
{...getInputProps()}
placeholder="Search a state..."
/>
{clearable && (
{!isInputEmpty && (
<button
className={styles.clearButton}
style={{ position: 'absolute', transform: 'translate(-120%, 10%)' }}
Expand Down
4 changes: 2 additions & 2 deletions examples/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default function Home() {
getClearProps,
open,
focusItem,
clearable
isInputEmpty
} = useCombobox({
// traversal: linearTraversal({
// items,
Expand Down Expand Up @@ -204,7 +204,7 @@ export default function Home() {
</button>
</div>
<input className={styles.input} {...getInputProps()} />
{clearable && (
{!isInputEmpty && (
<button
className={styles.clearButton}
style={{ position: 'absolute', transform: 'translate(-120%, 10%)' }}
Expand Down
4 changes: 2 additions & 2 deletions examples/pages/multiDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function Dropdown() {
getItemProps,
getToggleProps,
getClearProps,
clearable,
isInputEmpty,
open,
focusItem,
removeSelect
Expand Down Expand Up @@ -131,7 +131,7 @@ export default function Dropdown() {

<div className={styles.multiInputContainer}>
<input className={`${styles.input} ${styles.multiInput}`} {...getInputProps()} />
{clearable && (
{!isInputEmpty && (
<button className={styles.clearButton} {...getClearProps()}>
</button>
Expand Down
4 changes: 2 additions & 2 deletions examples/pages/multiSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function Home() {
getInputWrapperProps,
open,
focusItem,
clearable,
isInputEmpty,
removeSelect,
focused
} = useMultiSelect({
Expand Down Expand Up @@ -134,7 +134,7 @@ export default function Home() {

<div className={styles.multiInputContainer}>
<input className={`${styles.input} ${styles.multiInput}`} {...getInputProps()} />
{clearable && (
{!isInputEmpty && (
<button className={styles.clearButton} {...getClearProps()}>
</button>
Expand Down
4 changes: 2 additions & 2 deletions examples/pages/multiSelectAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default function Home() {
getInputWrapperProps,
open,
focusItem,
clearable,
isInputEmpty,
removeSelect,
focused
} = useMultiSelect({
Expand Down Expand Up @@ -155,7 +155,7 @@ export default function Home() {

<div className={styles.multiInputContainer}>
<input className={`${styles.input} ${styles.multiInput}`} {...getInputProps()} />
{clearable && (
{!isInputEmpty && (
<button className={styles.clearButton} {...getClearProps()}>
</button>
Expand Down
93 changes: 93 additions & 0 deletions examples/pages/search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { useState } from 'react';
import { useCombobox, supercomplete, linearTraversal } from '@szhsin/react-autocomplete';
import styles from '@/styles/Home.module.css';
import { US_STATES } from '../data';

type Item = { name: string; abbr: string };
const getItemValue = (item: Item) => item.name;

const search = (value: string | undefined) => value && console.log(`Searching for "${value}"`);

export default function Home() {
const [value, setValue] = useState<string | undefined>();
const items = value
? US_STATES.filter((item) => item.name.toLowerCase().startsWith(value.toLowerCase()))
: US_STATES;

const {
getInputProps,
getListProps,
getItemProps,
getToggleProps,
getClearProps,
open,
focusItem,
isInputEmpty
} = useCombobox({
onSelectChange: (selected) => search(selected?.name),
getItemValue,
value,
onChange: setValue,
feature: supercomplete({
selectOnBlur: false,
getFocusItem: (newValue) =>
US_STATES.filter((item) =>
item.name.toLowerCase().startsWith(newValue.toLowerCase())
)[0]
}),

traversal: linearTraversal({ items, traverseInput: true })
});

return (
<div className={styles.wrapper}>
<div>value: {value}</div>
<div>Focus item: {focusItem?.name}</div>

<form
onSubmit={(e) => {
e.preventDefault();
search(value);
}}
>
<input className={styles.input} {...getInputProps()} />

{!isInputEmpty && (
<button
type="button"
className={styles.clearButton}
style={{ position: 'absolute', transform: 'translate(-120%, 10%)' }}
{...getClearProps()}
>
</button>
)}
<button type="button" {...getToggleProps()}>
{open ? '⬆️' : '⬇️'}
</button>
</form>
<ul
{...getListProps()}
className={styles.list}
style={{
position: 'absolute',
border: '1px solid',
display: open && items.length ? 'block' : 'none'
}}
>
{items.map((item) => (
<li
className={styles.option}
key={item.abbr}
style={{
background: focusItem === item ? '#0a0' : 'none'
}}
{...getItemProps({ item })}
>
{item.name}
</li>
))}
</ul>
</div>
);
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@szhsin/react-autocomplete",
"version": "0.9.2",
"version": "0.9.3",
"description": "",
"author": "Zheng Song",
"license": "MIT",
Expand Down
4 changes: 2 additions & 2 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export interface Contextual<T>
setTmpValue: (value?: string | undefined) => void;
}

export interface Clearable {
clearable: boolean;
export interface FeatureState {
isInputEmpty: boolean;
}

export interface TraversalProps {
Expand Down
14 changes: 9 additions & 5 deletions src/features/atom/autocompleteLite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import type {
GetPropsFunctions,
GetPropsWithRefFunctions,
AutocompleteFeatureProps,
Clearable
FeatureState
} from '../../common';
import { useFocusCapture } from '../../hooks/useFocusCapture';

type AutocompleteLiteFeature<T> = Feature<
T,
Pick<GetPropsFunctions<T>, 'getListProps' | 'getItemProps' | 'getClearProps'> &
Pick<GetPropsWithRefFunctions<T>, 'getInputProps'> &
Clearable
FeatureState
>;

const scrollIntoView = (element: HTMLElement | null) =>
Expand Down Expand Up @@ -55,11 +55,13 @@ const autocompleteLite =
return true; // Always close list on action
}

onSelectChange(item);
const itemValue = getItemValue(item);
if (!select) onChange(itemValue);
const endIndex = itemValue.length;
inputRef.current!.setSelectionRange(endIndex, endIndex);
if (!select) onChange(itemValue);
// We place onSelectChange after onChange to give user an opportunity
// to manipulate the `value` state
onSelectChange(item);
};

const resetState = (shouldClose?: boolean) => {
Expand All @@ -72,7 +74,7 @@ const autocompleteLite =
};

return {
clearable: !!inputValue,
isInputEmpty: !inputValue,

getClearProps: () => ({
tabIndex: -1,
Expand Down Expand Up @@ -145,6 +147,8 @@ const autocompleteLite =
case 'Enter':
if (open) {
if (focusItem) {
// Call preventDefault as we've already triggered on* events in this branch
e.preventDefault();
resetState(selectItemOrAction(focusItem));
} else if (!select) {
resetState(true);
Expand Down
6 changes: 3 additions & 3 deletions src/features/atom/dropdownToggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import type {
FeatureProps,
GetPropsFunctions,
GetPropsWithRefFunctions,
Clearable
FeatureState
} from '../../common';
import { useToggle } from '../../hooks/useToggle';

type DropdownToggleFeature<T> = Feature<
T,
Pick<GetPropsWithRefFunctions<T>, 'getToggleProps'> &
Pick<GetPropsFunctions<T>, 'getInputProps'> &
Clearable
FeatureState
>;

const dropdownToggle =
Expand All @@ -33,7 +33,7 @@ const dropdownToggle =
const focusToggle = () => setTimeout(() => toggleRef.current?.focus(), 0);

return {
clearable: !!inputValue,
isInputEmpty: !inputValue,

getToggleProps: () => ({
ref: toggleRef,
Expand Down
4 changes: 2 additions & 2 deletions types/common.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export interface Contextual<T> extends PassthroughProps<T>, AdapterProps<T>, Equ
tmpValue?: string;
setTmpValue: (value?: string | undefined) => void;
}
export interface Clearable {
clearable: boolean;
export interface FeatureState {
isInputEmpty: boolean;
}
export interface TraversalProps {
traverseInput?: boolean;
Expand Down
Loading

0 comments on commit db809e5

Please sign in to comment.