├── src ├── index.css ├── env.js ├── app.js └── app.css ├── .gitignore ├── package.json └── index.html /src/index.css: -------------------------------------------------------------------------------- 1 | body, 2 | h1 { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, 9 | Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 10 | } 11 | -------------------------------------------------------------------------------- /src/env.js: -------------------------------------------------------------------------------- 1 | // Parcel picks the `source` field of the monorepo packages and thus doesn't 2 | // apply the Babel config. We therefore need to manually override the constants 3 | // in the app. 4 | // See https://twitter.com/devongovett/status/1134231234605830144 5 | global.__DEV__ = process.env.NODE_ENV !== 'production'; 6 | global.__TEST__ = false; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /dist 11 | /.cache 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | .parcel-cache 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-instantsearch-getting-started", 3 | "version": "1.0.14", 4 | "private": true, 5 | "scripts": { 6 | "start": "BABEL_ENV=parcel parcel index.html --port 3000", 7 | "build": "BABEL_ENV=parcel parcel build index.html", 8 | "lint": "eslint .", 9 | "lint:fix": "npm run lint -- --fix" 10 | }, 11 | "dependencies": { 12 | "algoliasearch": "4.14.3", 13 | "instantsearch.js": "4.56.11" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "7.15.5", 17 | "@parcel/core": "2.8.0", 18 | "@parcel/packager-raw-url": "2.8.0", 19 | "@parcel/transformer-webmanifest": "2.8.0", 20 | "parcel": "2.8.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | חיפוש בפורום פייתון 21 | 22 | 23 | 24 |
25 |

26 | לומדים פייתון 27 |

28 |

29 | חיפוש בפורומים 30 |

31 |
32 | 33 |
34 |
35 |
36 | 37 |
38 | 39 |
40 | 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import algoliasearch from 'algoliasearch/lite'; 2 | import instantsearch from 'instantsearch.js'; 3 | import { 4 | configure, 5 | hits, 6 | pagination, 7 | panel, 8 | refinementList, 9 | searchBox, 10 | } from 'instantsearch.js/es/widgets'; 11 | 12 | const searchClient = algoliasearch( 13 | '950FKJKDEB', 14 | 'a751cfb6edbb86ff4f5f3416b51253c0', 15 | ); 16 | 17 | const search = instantsearch({ 18 | indexName: 'prod_freepythoncourse', 19 | searchClient, 20 | }); 21 | 22 | async function displayFirstAvailableImage(imgElement, imageUrl) { 23 | const imageUrls = [ 24 | imageUrl.replace('{size}', '40').replace(".jpg", ".png"), 25 | imageUrl.replace('{size}', '40').replace(".png", ".jpg"), 26 | imageUrl.replace('{size}', '25').replace(".jpg", ".png"), 27 | imageUrl.replace('{size}', '25').replace(".png", ".jpg"), 28 | ]; 29 | 30 | for (let i = 0; i < imageUrls.length; i++) { 31 | try { 32 | const response = await fetch(imageUrls[i], { method: 'HEAD' }); 33 | 34 | if (response.ok) { 35 | imgElement.src = imageUrls[i]; 36 | return; 37 | } 38 | } catch (error) {} 39 | } 40 | 41 | imgElement.remove(); 42 | } 43 | 44 | const observer = new MutationObserver((mutationsList) => { 45 | for(const mutation of mutationsList) { 46 | if(mutation.addedNodes.length) { 47 | mutation.addedNodes.forEach((node) => { 48 | if(node.querySelectorAll) { 49 | const imgElements = node.querySelectorAll('.search-hit-avatar'); 50 | imgElements.forEach(imgElement => { 51 | const imageUrl = imgElement.getAttribute('data-img'); 52 | displayFirstAvailableImage(imgElement, imageUrl); 53 | }); 54 | } 55 | }); 56 | } 57 | } 58 | }); 59 | 60 | observer.observe(document.body, { childList: true, subtree: true }); 61 | 62 | search.addWidgets([ 63 | searchBox({ 64 | container: '#searchbox', 65 | }), 66 | hits({ 67 | container: '#hits', 68 | templates: { 69 | item: (hit, { html, components }) => html` 70 |
71 |
72 | ${hit.display_username}'s avatar 73 |
74 |

75 | 76 | ${components.Highlight({ hit, attribute: 'topic_title' })} 77 | 78 |

79 |

80 | ${ hit.display_username && html`פורסם על־ידי ${hit.display_username} ` } 81 | בתאריך ${hit.created_at} 82 |

83 |
84 |
85 |
86 |

${components.Snippet({ hit, attribute: 'cooked' })}

87 |
88 | 101 |
102 | `, 103 | }, 104 | }), 105 | configure({ 106 | hitsPerPage: 8, 107 | }), 108 | panel({ 109 | templates: { header: "תגיות" }, 110 | })(refinementList)({ 111 | container: '#tags-list', 112 | attribute: 'tags', 113 | }), 114 | panel({ 115 | templates: { header: "קטגוריות" }, 116 | })(refinementList)({ 117 | container: '#category_name-list', 118 | attribute: 'category_name', 119 | }), 120 | pagination({ 121 | container: '#pagination', 122 | }), 123 | ]); 124 | 125 | search.start(); 126 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 7 | background-color: #f8f9fa; 8 | color: #343a40; 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | .header { 14 | display: flex; 15 | justify-content: space-between; 16 | align-items: center; 17 | padding: 0.5rem 2rem; 18 | background-color: rgb(233, 236, 239); 19 | min-height: 40px; 20 | margin-bottom: 1rem; 21 | color: rgb(33, 37, 41); 22 | direction: rtl; 23 | } 24 | 25 | .header a { 26 | color: rgb(33, 37, 41); 27 | text-decoration: none; 28 | font-size: 1rem; 29 | margin: 0 1rem; 30 | transition: color 0.3s; 31 | } 32 | 33 | .header a:hover { 34 | color: rgb(52, 58, 64); 35 | } 36 | 37 | .header-title { 38 | font-weight: 700; 39 | font-size: 1.2rem; 40 | } 41 | 42 | .header-subtitle { 43 | font-size: 0.9rem; 44 | } 45 | 46 | @media (max-width: 600px) { 47 | .header { 48 | flex-direction: column; 49 | align-items: flex-start; 50 | padding: 0.5rem 1rem; 51 | } 52 | 53 | .header a, .header-title, .header-subtitle { 54 | margin-bottom: 0.5rem; 55 | } 56 | } 57 | 58 | .container { 59 | margin: auto; 60 | padding: 1rem; 61 | width: 100%; 62 | clear: both; 63 | } 64 | 65 | #hits { 66 | margin: 0 auto; 67 | padding: 0 1rem; 68 | width: 100%; 69 | box-sizing: border-box; 70 | } 71 | 72 | .search-panel { 73 | display: flex; 74 | flex-direction: column; 75 | gap: 1rem; 76 | } 77 | 78 | .search-panel__filters { 79 | flex: 1; 80 | } 81 | 82 | .search-panel__results { 83 | flex: 3; 84 | } 85 | 86 | .ais-Highlight-highlighted { 87 | color: inherit; 88 | font-size: inherit; 89 | } 90 | 91 | #searchbox { 92 | margin-bottom: 2rem; 93 | padding: 0.5rem; 94 | border-radius: 0.5rem; 95 | border: 1px solid #ccc; 96 | width: 100%; 97 | box-sizing: border-box; 98 | } 99 | 100 | #pagination { 101 | margin: 2rem auto; 102 | text-align: center; 103 | } 104 | 105 | @media(min-width: 600px) { 106 | .search-panel { 107 | flex-direction: row; 108 | } 109 | } 110 | 111 | .search-hit { 112 | background-color: #ffffff; 113 | border-radius: 0.75rem; 114 | padding: 1rem; 115 | margin-bottom: 1rem; 116 | width: 100%; 117 | box-sizing: border-box; 118 | box-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.1); 119 | transition: transform 0.2s; 120 | } 121 | 122 | .search-hit:hover { 123 | transform: translateY(-0.3125rem); 124 | } 125 | 126 | .search-hit-header { 127 | display: flex; 128 | flex-direction: column; 129 | align-items: flex-start; 130 | margin-bottom: 1rem; 131 | } 132 | 133 | .search-hit-header img { 134 | margin-right: auto; 135 | margin-left: auto; 136 | max-width: 100%; 137 | height: auto; 138 | } 139 | 140 | .search-hit-avatar { 141 | border-radius: 50%; 142 | width: 40px; 143 | height: 40px; 144 | margin-bottom: 1rem; 145 | object-fit: cover; 146 | } 147 | 148 | .search-hit-info { 149 | flex-grow: 1; 150 | margin-inline-start: 0.5rem; 151 | } 152 | 153 | .search-hit-title { 154 | text-align: start; 155 | direction: rtl; 156 | margin: 0; 157 | font-size: 1.5rem; 158 | line-height: 1.4; 159 | color: #343a40; 160 | } 161 | 162 | .search-hit-link { 163 | text-decoration: none; 164 | color: #007bff; 165 | } 166 | 167 | .search-hit-meta { 168 | margin: 0.3125rem 0 0 0; 169 | color: #6c757d; 170 | font-size: 0.9rem; 171 | text-align: right; 172 | direction: rtl; 173 | } 174 | 175 | .search-hit-content { 176 | margin-bottom: 1rem; 177 | } 178 | 179 | .search-hit-text { 180 | margin: 0; 181 | color: #343a40; 182 | line-height: 1.6; 183 | text-align: right; 184 | direction: rtl; 185 | } 186 | 187 | .search-hit-footer { 188 | display: flex; 189 | direction: rtl; 190 | flex-direction: column; 191 | align-items: flex-start; 192 | border-top: 1px solid #e9ecef; 193 | padding-top: 0.5rem; 194 | } 195 | 196 | .search-hit-category { 197 | display: flex; 198 | flex-grow: 1; 199 | direction: rtl; 200 | text-align: right; 201 | margin-bottom: 0.5rem; 202 | color: #6c757d; 203 | width: 10rem; 204 | } 205 | 206 | .search-hit-tags { 207 | direction: rtl; 208 | text-align: right; 209 | flex-grow: 2; 210 | color: #6c757d; 211 | margin-bottom: 0.5rem; 212 | } 213 | 214 | .search-hit-tags a { 215 | text-decoration: none; 216 | color: #007bff; 217 | } 218 | 219 | .search-hit-tags span { 220 | background-color: #e9ecef; 221 | padding: 0.125rem 0.5rem; 222 | border-radius: 0.9375rem; 223 | margin: 0 0.25rem 0.25rem 0; 224 | display: inline-block; 225 | } 226 | 227 | .search-hit-read-more { 228 | text-align: left; 229 | margin-inline-end: 0.5rem; 230 | } 231 | 232 | .search-hit-link { 233 | text-decoration: none; 234 | color: #007bff; 235 | } 236 | 237 | #pagination { 238 | margin: 2rem auto; 239 | display: flex; 240 | justify-content: space-around; 241 | flex-wrap: wrap; 242 | } 243 | 244 | .ais-Pagination-link { 245 | font-size: 0.75rem; 246 | padding: 0 0.5rem; 247 | flex: 1; 248 | text-align: center; 249 | margin: 0.5rem 0; 250 | } 251 | 252 | @media(min-width: 600px) { 253 | .ais-Pagination-link { 254 | font-size: 0.875rem; 255 | padding: 0 0.75rem; 256 | } 257 | } 258 | 259 | @media(min-width: 900px) { 260 | .ais-Pagination-link { 261 | font-size: 1rem; 262 | padding: 0 1rem; 263 | } 264 | } 265 | 266 | @media(min-width: 37.5rem) { 267 | .search-hit-header, .search-hit-footer { 268 | direction: rtl; 269 | text-align: right; 270 | flex-direction: row; 271 | align-items: center; 272 | } 273 | 274 | .search-hit-avatar { 275 | margin-bottom: 0; 276 | margin-right: 1rem; 277 | } 278 | 279 | .search-hit-category { 280 | margin-right: 0.5rem; 281 | margin-bottom: 0; 282 | } 283 | 284 | .search-hit-tags { 285 | margin-bottom: 0; 286 | } 287 | } 288 | 289 | .ais-SearchBox-form::before, .ais-SearchBox-form::before { 290 | background: initial; 291 | top: initial; 292 | width: initial; 293 | height: initial; 294 | margin-top: initial; 295 | position: initial; 296 | content: initial; 297 | left: initial; 298 | } 299 | 300 | .ais-SearchBox-form::after { 301 | background: transparent url(data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%235a5e9a%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2211%22%20cy%3D%2211%22%20r%3D%228%22%3E%3C%2Fcircle%3E%3Cline%20x1%3D%2221%22%20y1%3D%2221%22%20x2%3D%2216.65%22%20y2%3D%2216.65%22%3E%3C%2Fline%3E%3C%2Fsvg%3E) repeat scroll 0 0; 302 | content: ""; 303 | height: 1rem; 304 | right: 1rem; 305 | margin-top: -0.5rem; 306 | position: absolute; 307 | top: 50%; 308 | width: 1rem; 309 | } 310 | 311 | .ais-SearchBox-form { 312 | direction: rtl; 313 | } 314 | 315 | .ais-SearchBox-form .ais-SearchBox-input { 316 | text-align: right; 317 | padding-right: 2.5rem !important; 318 | padding-left: 0 !important; 319 | } 320 | 321 | .ais-SearchBox-submit, .ais-SearchBox-reset { 322 | order: -1; 323 | left: 1rem !important; 324 | } 325 | 326 | .ais-SearchBox-reset { 327 | margin-right: auto; 328 | } 329 | 330 | .ais-RefinementList-list { 331 | direction: rtl; 332 | text-align: right; 333 | } 334 | 335 | .ais-GeoSearch-input, .ais-RefinementList-checkbox { 336 | margin: 0; 337 | margin-left: 0.5rem !important; 338 | margin-right: 0 !important; 339 | } 340 | 341 | .ais-RefinementList-count { 342 | margin-right: 0.5rem !important; 343 | margin-left: 0 !important; 344 | } 345 | 346 | .ais-Panel-header { 347 | direction: rtl; 348 | text-align: right; 349 | } 350 | --------------------------------------------------------------------------------