( items: T[], searchValue: string, options: SearchOptions<T> )
| 374 | * Sorts items to prioritize `startsWith` matches, then `includes` matches, then tag matches. |
| 375 | */ |
| 376 | export function sortSearchItems<T>( |
| 377 | items: T[], |
| 378 | searchValue: string, |
| 379 | options: SearchOptions<T> |
| 380 | ): T[] { |
| 381 | if (!searchValue.trim()) { |
| 382 | return items; |
| 383 | } |
| 384 | |
| 385 | const searchLower = searchValue.toLowerCase(); |
| 386 | const {getName, getDate, shouldUseDateSort} = options; |
| 387 | |
| 388 | return [...items].sort((a, b) => { |
| 389 | const aName = getName(a).toLowerCase(); |
| 390 | const bName = getName(b).toLowerCase(); |
| 391 | const aNameStartsWith = aName.startsWith(searchLower); |
| 392 | const bNameStartsWith = bName.startsWith(searchLower); |
| 393 | const aNameIncludes = aName.includes(searchLower); |
| 394 | const bNameIncludes = bName.includes(searchLower); |
| 395 | |
| 396 | // Check if either item should use date sorting |
| 397 | const aUseDateSort = shouldUseDateSort ? shouldUseDateSort(a) : false; |
| 398 | const bUseDateSort = shouldUseDateSort ? shouldUseDateSort(b) : false; |
| 399 | const bothUseDateSort = aUseDateSort && bUseDateSort; |
| 400 | |
| 401 | // Prioritize startsWith matches |
| 402 | if (aNameStartsWith && !bNameStartsWith) { |
| 403 | return -1; |
| 404 | } |
| 405 | if (!aNameStartsWith && bNameStartsWith) { |
| 406 | return 1; |
| 407 | } |
| 408 | |
| 409 | // If both start with, sort by date (if both use date sort) or alphabetically |
| 410 | if (aNameStartsWith && bNameStartsWith) { |
| 411 | if (bothUseDateSort && getDate) { |
| 412 | const aDate = getDate(a); |
| 413 | const bDate = getDate(b); |
| 414 | if (aDate && bDate) { |
| 415 | return new Date(bDate).getTime() - new Date(aDate).getTime(); |
| 416 | } else if (aDate && !bDate) { |
| 417 | return 1; |
| 418 | } else if (!aDate && bDate) { |
| 419 | return -1; |
| 420 | } |
| 421 | } |
| 422 | return aName.localeCompare(bName); |
| 423 | } |
| 424 | |
| 425 | // Prioritize includes matches over tag matches |
| 426 | if (aNameIncludes && !bNameIncludes) { |
| 427 | return -1; |
| 428 | } |
| 429 | if (!aNameIncludes && bNameIncludes) { |
| 430 | return 1; |
| 431 | } |
| 432 | |
| 433 | // If both match by name (includes), sort by date (if both use date sort) or alphabetically |
no test coverage detected