Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 51 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,65 @@ yarn add react-api-search
## Usage

```tsx
import React, { useState } from 'react';
import SearchBar from 'react-api-search';

const MyComponent = () => {
const [selectedItem, setSelectedItem] = useState(null);

const fetchSearchResults = async (query: string) => {
const response = await fetch('https://api.example.com/search?q=${query}');
const data = await response.json();
return data.results; // return an array of results
};

const renderSearchResult = (item: any) => <div>{item.name}</div>;
type Post = {
id: number;
title: string;
body: string;
};

const handleItemSelect = (item: any) => {
setSelectedItem(item);
};
const fetchPosts = async (query: string): Promise<Post[]> => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?q=${encodeURIComponent(query)}`
);
if (!response.ok) throw new Error('Failed to fetch posts');
const data = await response.json();
return data;
};

function App() {
return (
<div>
<SearchBar
fetchData={fetchSearchResults}
renderItem={renderSearchResult}
onSelect={handleItemSelect}
placeholder='Search for items...'
debounceDelay={300}
/>
{selectedItem && <div>You selected: {selectedItem.name}</div>}
<div style={{ padding: '20px' }}>
<h1>Posts Search</h1>
<div style={{ width: '40rem' }}>
<SearchBar<Post>
placeholder='Search for posts...'
fetchData={fetchPosts}
loadingElement={
<div className='custom-loading'>Please wait, fetching data...</div>
}
emptyElement={
<div>No posts match your search. Try something else!</div>
}
errorElement={
<div>Oops, something went wrong. Please try again later.</div>
}
renderItem={(post) => (
<div
style={{ padding: '10px', borderBottom: '1px solid #ccc' }}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = '#f0f0f0'; // hover background
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'transparent'; // reset background
}}
>
<h3 style={{ margin: 0 }}>{post.title}</h3>
<p style={{ margin: '5px 0', fontSize: '0.9em', color: '#555' }}>
{post.body}
</p>
</div>
)}
onSelect={(post) => alert(`Selected post: ${post.title}`)}
debounceDelay={500}
/>
</div>
</div>
);
};
}

export default MyComponent;
export default App;
```

## Props
Expand Down Expand Up @@ -94,24 +120,6 @@ export default MyComponent;
| dropDownBorderRadius | string | Border radius of the dropdown. | '8px' |
| scrollBarColor | string | Color of the scrollbar inside the dropdown. | #ccc |

## Example

### Basic Example

```tsx
<SearchBar
fetchData={async (query: string) => {
const response = await fetch('https://api.example.com/search?q=${query}');
return response.json();
}}
renderItem={(item) => <div>{item.name}</div>}
onSelect={(item) => console.log('Selected:', item)}
placeholder='Search for items...'
loadingElement={<div>Loading...</div>}
emptyElement={<div>No results found</div>}
/>
```

## License

This project is licensed under the MIT License. See the [LICENSE](https://github.com/ghosnkarl/react-api-search/blob/main/LICENSE) file for more information.
Expand Down
62 changes: 62 additions & 0 deletions example/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import SearchBar from '../src/SearchBar';

const container = document.getElementById('root');
const root = createRoot(container!);

type Post = {
id: number;
title: string;
body: string;
};

const fetchPosts = async (query: string): Promise<Post[]> => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?q=${encodeURIComponent(query)}`
);
if (!response.ok) throw new Error('Failed to fetch posts');
const data = await response.json();
return data;
};

root.render(
<React.StrictMode>
<div style={{ padding: '20px' }}>
<h1>Posts Search</h1>
<div style={{ width: '40rem' }}>
<SearchBar<Post>
placeholder='Search for posts...'
fetchData={fetchPosts}
loadingElement={
<div className='custom-loading'>Please wait, fetching data...</div>
}
emptyElement={
<div>No posts match your search. Try something else!</div>
}
errorElement={
<div>Oops, something went wrong. Please try again later.</div>
}
renderItem={(post) => (
<div
style={{ padding: '10px', borderBottom: '1px solid #ccc' }}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = '#f0f0f0'; // hover background
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'transparent'; // reset background
}}
>
<h3 style={{ margin: 0 }}>{post.title}</h3>
<p style={{ margin: '5px 0', fontSize: '0.9em', color: '#555' }}>
{post.body}
</p>
</div>
)}
onSelect={(post) => alert(`Selected post: ${post.title}`)}
debounceDelay={500}
/>
</div>
</div>
</React.StrictMode>
);
25 changes: 25 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@rollup/plugin-typescript": "^11.1.6",
"@types/react": "^18.3.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rollup": "^4.18.1",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-peer-deps-external": "^2.2.4",
Expand Down
2 changes: 0 additions & 2 deletions src/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { useState, useEffect, ChangeEvent, useRef } from 'react';
import styles from './SearchBar.module.css';
import { SearchBarProps } from './types';
import { MdSearch } from 'react-icons/md';
import { MdClose } from 'react-icons/md';
import Dropdown from './Dropdown';
import InputField from './InputField';

Expand Down