5656 </v-btn >
5757 <v-btn
5858 icon
59+ :class =" { 'search--active': Boolean(searching && showSearch) }"
5960 @click ="
6061 showSearch = !showSearch
6162 searchQuery = ''
6263 " >
63- <v-icon >mdi-magnify</v-icon >
64+ <v-icon >
65+ {{
66+ searching && showSearch ? 'mdi-loading' : 'mdi-magnify'
67+ }}
68+ </v-icon >
6469 </v-btn >
6570 <v-menu
6671 v-if =" !showSearch"
183188 </template >
184189 </v-list >
185190 <v-card
186- v-else
191+ v-else-if = " !searching "
187192 flat
188193 tile
189194 :style =" {
@@ -321,6 +326,7 @@ import sortBy from 'lodash/sortBy'
321326import DialogImportBookmarks from ' ../../components/native/DialogImportBookmarks'
322327import Breadcrumbs from ' ../../components/native/Breadcrumbs.vue'
323328import Item from ' ../../components/native/Item.vue'
329+ import { yieldToEventLoop } from ' ../../../lib/yieldToEventLoop'
324330
325331export default {
326332 name: ' Tree' ,
@@ -354,6 +360,9 @@ export default {
354360 sortBy: ' index' ,
355361 syncProgress: 0 ,
356362 showSearch: false ,
363+ otherSearchItems: [],
364+ searchItems: [],
365+ searching: true ,
357366 }
358367 },
359368 computed: {
@@ -397,11 +406,8 @@ export default {
397406 return []
398407 }
399408 let items
400- if (this .searchQuery && this .searchQuery .trim ().length >= 2 ) {
401- return this .search (
402- this .searchQuery .toLowerCase ().trim (),
403- this .currentFolder
404- )
409+ if (this .searchQuery ) {
410+ return this .searchItems
405411 } else {
406412 items = this .currentFolder .children
407413 }
@@ -428,18 +434,6 @@ export default {
428434 return items
429435 }
430436 },
431- otherSearchItems () {
432- if (
433- ! this .currentFolder &&
434- (! this .searchQuery || this .searchQuery .trim ().length < 2 )
435- ) {
436- return []
437- }
438- return this .search (
439- this .searchQuery .toLowerCase ().trim (),
440- this .tree
441- ).filter ((item ) => ! this .items .includes (item))
442- },
443437 routes () {
444438 return routes
445439 },
@@ -483,6 +477,34 @@ export default {
483477 sortBy: current,
484478 })
485479 },
480+ async searchQuery (searchQuery ) {
481+ if (searchQuery .trim ().length < 3 ) {
482+ this .searchItems = []
483+ this .otherSearchItems = []
484+ return
485+ }
486+ this .searching = true
487+ this .searchItems = []
488+ this .otherSearchItems = []
489+ await this .search (
490+ this .searchItems ,
491+ searchQuery .toLowerCase ().trim (),
492+ this .currentFolder
493+ )
494+ await this .search (
495+ this .otherSearchItems ,
496+ searchQuery .toLowerCase ().trim (),
497+ this .tree ,
498+ (item ) => ! this .searchItems .includes (item)
499+ )
500+ this .searching = false
501+ },
502+ showSearch (showSearch , previous ) {
503+ if (previous && ! showSearch) {
504+ this .searchItems = []
505+ this .otherSearchItems = []
506+ }
507+ },
486508 },
487509 mounted () {
488510 this .$store .dispatch (actions .LOAD_TREE , this .$route .params .accountId )
@@ -541,110 +563,131 @@ export default {
541563 this .searchQuery = query
542564 }, 500 )
543565 },
544- search (query , tree ) {
545- return Object .values (tree .index .folder )
546- .filter ((item ) => {
547- const matchTitleFully = item .title
548- ? query .split (' ' ).every ((term ) =>
549- item .title
550- .toLowerCase ()
551- .split (' ' )
552- .some ((word ) => word === term)
553- )
554- : false
555- const matchTitlePartially = item .title
556- ? query
566+ async search (results, query, tree, filterFunction = (item ) => true ) {
567+ // Refactored to use for loops instead of Object.values/filter
568+ const folderResults = results
569+ for (const key in tree .index .folder ) {
570+ const item = tree .index .folder [key]
571+ if (! filterFunction (item)) {
572+ continue
573+ }
574+ let matchTitleFully = false
575+ let matchTitlePartially = false
576+ if (item .title ) {
577+ matchTitleFully = query .split (' ' ).every ((term ) =>
578+ item .title
579+ .toLowerCase ()
557580 .split (' ' )
558- .every ((term ) => item .title .toLowerCase ().includes (term))
559- : false
560- return matchTitleFully || matchTitlePartially
561- })
562- .sort ((a , b ) => {
563- const matchTitlePartiallyA = a .title
564- ? query
581+ .some ((word ) => word === term)
582+ )
583+ matchTitlePartially = query
584+ .split (' ' )
585+ .every ((term ) => item .title .toLowerCase ().includes (term))
586+ }
587+ if (matchTitleFully || matchTitlePartially) {
588+ folderResults .push (item)
589+ }
590+ }
591+
592+ // Sort folderResults by partial match, then by full match
593+ folderResults .sort ((a , b ) => {
594+ const matchTitlePartiallyA = a .title
595+ ? query
596+ .split (' ' )
597+ .every ((term ) => a .title .toLowerCase ().includes (term))
598+ : false
599+ const matchTitlePartiallyB = b .title
600+ ? query
601+ .split (' ' )
602+ .every ((term ) => b .title .toLowerCase ().includes (term))
603+ : false
604+ return matchTitlePartiallyA ? (matchTitlePartiallyB ? 0 : - 1 ) : 1
605+ })
606+ folderResults .sort ((a , b ) => {
607+ const matchTitleA = a .title
608+ ? query .split (' ' ).every ((term ) =>
609+ a .title
610+ .toLowerCase ()
565611 .split (' ' )
566- .every ((term ) => a .title .toLowerCase ().includes (term))
567- : false
568- const matchTitlePartiallyB = b .title
569- ? query
612+ .some ((word ) => word === term)
613+ )
614+ : false
615+ const matchTitleB = b .title
616+ ? query .split (' ' ).every ((term ) =>
617+ b .title
618+ .toLowerCase ()
570619 .split (' ' )
571- .every ((term ) => b .title .toLowerCase ().includes (term))
572- : false
573- return matchTitlePartiallyA ? (matchTitlePartiallyB ? 0 : - 1 ) : 1
574- })
575- .sort ((a , b ) => {
576- const matchTitleA = a .title
577- ? query .split (' ' ).every ((term ) =>
578- a .title
579- .toLowerCase ()
580- .split (' ' )
581- .some ((word ) => word === term)
582- )
583- : false
584- const matchTitleB = b .title
585- ? query .split (' ' ).every ((term ) =>
586- b .title
587- .toLowerCase ()
588- .split (' ' )
589- .some ((word ) => word === term)
590- )
591- : false
592- return matchTitleA ? (matchTitleB ? 0 : - 1 ) : 1
593- })
594- .concat (
595- Object .values (tree .index .bookmark )
596- .filter ((item ) => {
597- const matchTitleFully = item .title
598- ? query .split (' ' ).every ((term ) =>
599- item .title
600- .toLowerCase ()
601- .split (' ' )
602- .some ((word ) => word === term)
603- )
604- : false
605- const matchTitlePartially = item .title
606- ? query
607- .split (' ' )
608- .every ((term ) => item .title .toLowerCase ().includes (term))
609- : false
610- const matchUrl = query
611- .split (' ' )
612- .every ((term ) => item .url .toLowerCase ().includes (term))
613- return matchUrl || matchTitleFully || matchTitlePartially
614- })
615- .sort ((a , b ) => {
616- const matchTitlePartiallyA = a .title
617- ? query
618- .split (' ' )
619- .every ((term ) => a .title .toLowerCase ().includes (term))
620- : false
621- const matchTitlePartiallyB = b .title
622- ? query
623- .split (' ' )
624- .every ((term ) => b .title .toLowerCase ().includes (term))
625- : false
626- return matchTitlePartiallyA ? (matchTitlePartiallyB ? 0 : - 1 ) : 1
627- })
628- .sort ((a , b ) => {
629- const matchTitleA = a .title
630- ? query .split (' ' ).every ((term ) =>
631- a .title
632- .toLowerCase ()
633- .split (' ' )
634- .some ((word ) => word === term)
635- )
636- : false
637- const matchTitleB = b .title
638- ? query .split (' ' ).every ((term ) =>
639- b .title
640- .toLowerCase ()
641- .split (' ' )
642- .some ((word ) => word === term)
643- )
644- : false
645- return matchTitleA ? (matchTitleB ? 0 : - 1 ) : 1
646- })
647- )
620+ .some ((word ) => word === term)
621+ )
622+ : false
623+ return matchTitleA ? (matchTitleB ? 0 : - 1 ) : 1
624+ })
625+
626+ const bookmarkResults = []
627+ for (const key in tree .index .bookmark ) {
628+ const item = tree .index .bookmark [key]
629+ if (! filterFunction (item)) {
630+ continue
631+ }
632+ let matchTitleFully = false
633+ let matchTitlePartially = false
634+ let matchUrl = false
635+ if (item .title ) {
636+ matchTitleFully = query .split (' ' ).every ((term ) =>
637+ item .title
638+ .toLowerCase ()
639+ .split (' ' )
640+ .some ((word ) => word === term)
641+ )
642+ matchTitlePartially = query
643+ .split (' ' )
644+ .every ((term ) => item .title .toLowerCase ().includes (term))
645+ }
646+ if (item .url ) {
647+ matchUrl = query
648+ .split (' ' )
649+ .every ((term ) => item .url .toLowerCase ().includes (term))
650+ }
651+ if (matchUrl || matchTitleFully || matchTitlePartially) {
652+ bookmarkResults .push (item)
653+ }
654+ }
655+
656+ // Sort bookmarkResults by partial match, then by full match
657+ bookmarkResults .sort ((a , b ) => {
658+ const matchTitlePartiallyA = a .title
659+ ? query
660+ .split (' ' )
661+ .every ((term ) => a .title .toLowerCase ().includes (term))
662+ : false
663+ const matchTitlePartiallyB = b .title
664+ ? query
665+ .split (' ' )
666+ .every ((term ) => b .title .toLowerCase ().includes (term))
667+ : false
668+ return matchTitlePartiallyA ? (matchTitlePartiallyB ? 0 : - 1 ) : 1
669+ })
670+ bookmarkResults .sort ((a , b ) => {
671+ const matchTitleA = a .title
672+ ? query .split (' ' ).every ((term ) =>
673+ a .title
674+ .toLowerCase ()
675+ .split (' ' )
676+ .some ((word ) => word === term)
677+ )
678+ : false
679+ const matchTitleB = b .title
680+ ? query .split (' ' ).every ((term ) =>
681+ b .title
682+ .toLowerCase ()
683+ .split (' ' )
684+ .some ((word ) => word === term)
685+ )
686+ : false
687+ return matchTitleA ? (matchTitleB ? 0 : - 1 ) : 1
688+ })
689+
690+ return results .push .apply (results, bookmarkResults)
648691 },
649692 goBack () {
650693 if (this .isAddingBookmark ) {
@@ -777,6 +820,10 @@ export default {
777820 animation : spin 2s infinite linear ;
778821}
779822
823+ .search--active {
824+ animation : spin 2s infinite linear ;
825+ }
826+
780827@keyframes spin {
781828 0% {
782829 transform : rotate (360deg );
0 commit comments