11<script setup lang="ts">
22import type { SessionContext } from ' ../../../types/data'
3- import { computedWithControl } from ' @vueuse/core'
3+ import { useRoute , useRouter } from ' #app/composables/router'
4+ import { clearUndefined } from ' @antfu/utils'
5+ import { computedWithControl , debouncedWatch } from ' @vueuse/core'
46import Fuse from ' fuse.js'
57import { computed , reactive } from ' vue'
8+ import { parseReadablePath } from ' ../../../utils/filepath'
9+ import { getFileTypeFromModuleId , getFileTypeFromName } from ' ../../../utils/icon'
610
711const props = defineProps <{
812 session: SessionContext
913}>()
1014
11- const filters = reactive ({
12- search: ' ' ,
15+ interface Filters {
16+ search: string
17+ file_types: string [] | null
18+ node_modules: string [] | null
19+ }
20+
21+ const route = useRoute ()
22+ const router = useRouter ()
23+
24+ const filters = reactive <Filters >({
25+ search: (route .query .search || ' ' ) as string ,
26+ file_types: (route .query .file_types || null ) as string [] | null ,
27+ node_modules: (route .query .node_modules || null ) as string [] | null ,
28+ })
29+
30+ debouncedWatch (
31+ filters ,
32+ (f ) => {
33+ const query: any = {
34+ ... route .query ,
35+ search: f .search || undefined ,
36+ file_types: f .file_types || undefined ,
37+ node_modules: f .node_modules || undefined ,
38+ }
39+ router .replace ({
40+ query: clearUndefined (query ),
41+ })
42+ },
43+ { debounce: 500 },
44+ )
45+
46+ const parsedPaths = computed (() => props .session .modulesList .map ((mod ) => {
47+ const path = parseReadablePath (mod .id , props .session .rootDir )
48+ const type = getFileTypeFromModuleId (mod .id )
49+ return {
50+ mod ,
51+ path ,
52+ type ,
53+ }
54+ }))
55+
56+ // const allNodeModules = computed(() => {
57+ // const nodeModules = new Set<string>()
58+ // for (const mod of parsedPaths.value) {
59+ // if (mod.path.moduleName)
60+ // nodeModules.add(mod.path.moduleName)
61+ // }
62+ // return nodeModules
63+ // })
64+
65+ const allFileTypes = computed (() => {
66+ const fileTypes = new Set <string >()
67+ for (const mod of parsedPaths .value ) {
68+ fileTypes .add (mod .type .name )
69+ }
70+ return fileTypes
1371})
1472
73+ const filtered = computed (() => {
74+ let modules = parsedPaths .value
75+ if (filters .file_types ) {
76+ modules = modules .filter (mod => filters .file_types ! .includes (mod .type .name ))
77+ }
78+ if (filters .node_modules ) {
79+ modules = modules .filter (mod => mod .path .moduleName && filters .node_modules ! .includes (mod .path .moduleName ))
80+ }
81+ return modules .map (mod => mod .mod )
82+ })
83+
84+ function isFileTypeSelected(type : string ) {
85+ return filters .file_types == null || filters .file_types .includes (type )
86+ }
87+
88+ function toggleFileType(type : string ) {
89+ if (filters .file_types == null ) {
90+ filters .file_types = Array .from (allFileTypes .value )
91+ }
92+
93+ if (filters .file_types .includes (type )) {
94+ filters .file_types = filters .file_types .filter (t => t !== type )
95+ }
96+ else {
97+ filters .file_types .push (type )
98+ }
99+ if (filters .file_types .length === allFileTypes .value .size ) {
100+ filters .file_types = null
101+ }
102+ }
103+
15104const fuse = computedWithControl (
16- () => props . session . modulesList ,
17- () => new Fuse (props . session . modulesList , {
105+ () => filtered . value ,
106+ () => new Fuse (filtered . value , {
18107 includeScore: true ,
19108 keys: [' id' ],
20109 }),
21110)
22111
23- const filtered = computed (() => {
112+ const searched = computed (() => {
24113 if (filters .search === ' ' ) {
25- return props . session . modulesList
114+ return filtered . value
26115 }
27116 return fuse .value
28117 .search (filters .search )
@@ -32,15 +121,38 @@ const filtered = computed(() => {
32121
33122<template >
34123 <div flex =" ~ col gap-2" p4 >
35- <div >
36- <input
37- v-model =" filters.search"
38- border =" ~ base rounded-full"
39- p2 px4 w-full outline-none
40- placeholder =" Search"
41- >
124+ <div flex =" col gap-2" >
125+ <div >
126+ <input
127+ v-model =" filters.search"
128+ border =" ~ base rounded-full"
129+ p2 px4 w-full outline-none
130+ placeholder =" Search"
131+ >
132+ </div >
133+ <div flex =" ~ gap-2" py2 >
134+ <label
135+ v-for =" type of allFileTypes"
136+ :key =" type"
137+ border =" ~ base rounded" px2 py1
138+ flex =" ~ items-center gap-1"
139+ :title =" type"
140+ :class =" isFileTypeSelected(type) ? 'bg-active' : 'grayscale op50'"
141+ >
142+ <input
143+ type =" checkbox"
144+ :checked =" isFileTypeSelected(type)"
145+ mr1
146+ @change =" toggleFileType(type)"
147+ >
148+ <div :class =" getFileTypeFromName(type).icon" />
149+ <div text-sm >{{ getFileTypeFromName(type).description }}</div >
150+ </label >
151+ </div >
152+ <!-- TODO: should we add filters for node_modules? -->
153+ <!-- {{ allNodeModules }} -->
42154 </div >
43- <template v-for =" mod of filtered " :key =" mod " >
155+ <template v-for =" mod of searched " :key =" mod " >
44156 <DisplayModuleId
45157 :id =" mod.id"
46158 :session
0 commit comments