├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.js ├── index.css └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .netlify 26 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## react-autocomplete 2 | 3 | Derste birlikte bir autocomplete örneği hazırladık, kaynak kodları inceleyebilirsiniz. Hatalar varsa katkılarınıı beklerim. 4 | 5 | demo - [https://react-custom-autocomplete.prototurk.com/](https://react-custom-autocomplete.prototurk.com/) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-autocomplete", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "react": "^17.0.2", 10 | "react-content-loader": "^6.0.3", 11 | "react-dom": "^17.0.2", 12 | "react-scripts": "4.0.3", 13 | "react-virtualized": "^9.22.3", 14 | "web-vitals": "^1.0.1" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tayfunerbilen/react-autocomplete/661aeeb69c947caa2b1257ee2071d60236a229b0/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React Autocomplete Example 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tayfunerbilen/react-autocomplete/661aeeb69c947caa2b1257ee2071d60236a229b0/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tayfunerbilen/react-autocomplete/661aeeb69c947caa2b1257ee2071d60236a229b0/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import {useEffect, useRef, useState} from 'react'; 2 | import ContentLoader from "react-content-loader" 3 | 4 | /* 5 | const data = [ 6 | { 7 | id: 1, 8 | title: "test 1" 9 | }, 10 | { 11 | id: 2, 12 | title: "Test 2" 13 | }, 14 | { 15 | id: 3, 16 | title: "deneme 1" 17 | }, 18 | { 19 | id: 4, 20 | title: "Deneme 2" 21 | } 22 | ] 23 | */ 24 | 25 | const AutocompleteLoader = () => ( 26 | 34 | 35 | 36 | 37 | 38 | 39 | ) 40 | 41 | function App() { 42 | const [search, setSearch] = useState('') 43 | const [result, setResult] = useState(false) 44 | const [loading, setLoading] = useState(false) 45 | const searchRef = useRef() 46 | 47 | const isTyping = search.replace(/\s+/, '').length > 0; 48 | 49 | useEffect(() => { 50 | document.addEventListener('mousedown', handleClickOutside) 51 | return () => { 52 | document.removeEventListener('mousedown', handleClickOutside) 53 | } 54 | }, []) 55 | 56 | const handleClickOutside = (e) => { 57 | if (searchRef.current && !searchRef.current.contains(e.target)){ 58 | setSearch('') 59 | } 60 | } 61 | 62 | const getResultItem = (item) => { 63 | // console.log(item) 64 | window.location.href = item.url; 65 | } 66 | 67 | useEffect(() => { 68 | console.log(loading) 69 | }, [loading]) 70 | 71 | useEffect(() => { 72 | 73 | if (isTyping){ 74 | /* 75 | const filteredResult = data.filter(item => item.title.toLowerCase().includes(search.toLowerCase())) 76 | setResult(filteredResult.length > 0 ? filteredResult : false) 77 | */ 78 | 79 | setLoading(true) 80 | const getData = setTimeout(() => { 81 | fetch(`https://frontendaily.com/search.php?query=${search}`) 82 | .then(res => res.json()) 83 | .then(data => { 84 | setResult(data.length > 0 ? data : false) 85 | setLoading(false) 86 | }) 87 | }, 500) 88 | 89 | return () => { 90 | clearTimeout(getData) 91 | setLoading(false) 92 | } 93 | 94 | } else { 95 | setResult(false) 96 | } 97 | 98 | }, [search]) 99 | 100 | return ( 101 | <> 102 |
103 | setSearch(e.target.value)}/> 104 | {isTyping && ( 105 |
106 | {result && loading === false && result.map(item => ( 107 |
getResultItem(item)} key={item.id} className="search-result-item"> 108 | 109 |
110 |
{item.title}
111 |
{item.date}
112 |
113 |
114 | ))} 115 | {loading && new Array(3).fill().map(() => )} 116 | {!result && loading === false && ( 117 |
118 | "{search}" ile ilgili bir şey bulamadık! 119 |
120 | )} 121 |
122 | )} 123 |
124 | 125 | ); 126 | } 127 | 128 | export default App; 129 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | background: #ddd; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | 16 | * { 17 | padding: 0; 18 | margin: 0; 19 | list-style: none; 20 | border: 0; 21 | box-sizing: border-box; 22 | text-decoration: none; 23 | outline: 0; 24 | } 25 | 26 | .search { 27 | width: 500px; 28 | margin: 20px auto; 29 | position: relative; 30 | } 31 | 32 | .search input { 33 | width: 100%; 34 | height: 50px; 35 | border-radius: 10px; 36 | font-size: 20px; 37 | padding: 0 15px; 38 | } 39 | 40 | .search input.typing { 41 | border-radius: 10px 10px 0 0; 42 | } 43 | 44 | .search .search-result { 45 | position: absolute; 46 | left: 0; 47 | width: 100%; 48 | border-radius: 0 0 10px 10px; 49 | background: #fff; 50 | max-height: 300px; 51 | overflow: auto; 52 | } 53 | 54 | .search .search-result .search-result-item { 55 | padding: 0 15px; 56 | height: 60px; 57 | display: flex; 58 | align-items: center; 59 | cursor: pointer; 60 | } 61 | 62 | .search .search-result .search-result-item:hover { 63 | background: antiquewhite; 64 | } 65 | 66 | .search .search-result .search-result-item img { 67 | height: 40px; 68 | margin-right: 15px; 69 | } 70 | 71 | .search .search-result .search-result-item .date { 72 | font-size: 12px; 73 | color: #999; 74 | padding-top: 5px; 75 | } 76 | 77 | .search .result-not-found { 78 | padding: 25px; 79 | text-align: center; 80 | font-size: 14px; 81 | color: rgba(0, 0, 0, .5); 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); --------------------------------------------------------------------------------