1+ import { useMemo , useState } from 'react'
2+ import { Check , ChevronsUpDown , Loader2 } from 'lucide-react'
13import { Namespace } from 'kubernetes-types/core/v1'
24
35import { useResources } from '@/lib/api'
6+ import { cn } from '@/lib/utils'
7+ import { Button } from '@/components/ui/button'
48import {
5- Select ,
6- SelectContent ,
7- SelectItem ,
8- SelectTrigger ,
9- SelectValue ,
10- } from '@/components/ui/select'
9+ Command ,
10+ CommandEmpty ,
11+ CommandGroup ,
12+ CommandInput ,
13+ CommandItem ,
14+ CommandList ,
15+ } from '@/components/ui/command'
16+ import {
17+ Popover ,
18+ PopoverContent ,
19+ PopoverTrigger ,
20+ } from '@/components/ui/popover'
1121
1222export function NamespaceSelector ( {
1323 selectedNamespace,
@@ -18,36 +28,87 @@ export function NamespaceSelector({
1828 handleNamespaceChange : ( namespace : string ) => void
1929 showAll ?: boolean
2030} ) {
31+ const [ open , setOpen ] = useState ( false )
2132 const { data, isLoading } = useResources ( 'namespaces' )
2233
23- const sortedNamespaces = data ?. sort ( ( a , b ) => {
24- const nameA = a . metadata ?. name ?. toLowerCase ( ) || ''
25- const nameB = b . metadata ?. name ?. toLowerCase ( ) || ''
26- return nameA . localeCompare ( nameB )
27- } ) || [ { metadata : { name : 'default' } } ]
34+ const sortedNamespaces = useMemo ( ( ) => {
35+ if ( ! data ) return [ ]
36+ return [ ...data ] . sort ( ( a , b ) => {
37+ const nameA = a . metadata ?. name ?. toLowerCase ( ) || ''
38+ const nameB = b . metadata ?. name ?. toLowerCase ( ) || ''
39+ return nameA . localeCompare ( nameB )
40+ } )
41+ } , [ data ] )
2842
2943 return (
30- < Select value = { selectedNamespace } onValueChange = { handleNamespaceChange } >
31- < SelectTrigger className = "max-w-48" >
32- < SelectValue placeholder = "Select a namespace" />
33- </ SelectTrigger >
34- < SelectContent >
35- { isLoading && (
36- < SelectItem disabled value = "_loading" >
37- Loading namespaces...
38- </ SelectItem >
39- ) }
40- { showAll && (
41- < SelectItem key = "all" value = "_all" >
42- All Namespaces
43- </ SelectItem >
44- ) }
45- { sortedNamespaces ?. map ( ( ns : Namespace ) => (
46- < SelectItem key = { ns . metadata ! . name } value = { ns . metadata ! . name ! } >
47- { ns . metadata ! . name }
48- </ SelectItem >
49- ) ) }
50- </ SelectContent >
51- </ Select >
44+ < Popover open = { open } onOpenChange = { setOpen } >
45+ < PopoverTrigger asChild >
46+ < Button
47+ variant = "outline"
48+ role = "combobox"
49+ aria-expanded = { open }
50+ className = "w-[200px] justify-between shadow-sm"
51+ >
52+ < span className = "truncate" >
53+ { selectedNamespace === '_all'
54+ ? 'All Namespaces'
55+ : ( selectedNamespace || "Select namespace..." ) }
56+ </ span >
57+ < ChevronsUpDown className = "ml-2 h-4 w-4 shrink-0 opacity-50" />
58+ </ Button >
59+ </ PopoverTrigger >
60+
61+ < PopoverContent className = "w-[200px] p-0" align = "start" >
62+ < Command >
63+ < CommandInput placeholder = "Search..." className = "h-9" />
64+ < CommandList className = "max-h-[300px] overflow-x-hidden overflow-y-auto [ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" >
65+ { isLoading ? (
66+ < div className = "flex items-center justify-center p-6 text-sm" >
67+ < Loader2 className = "h-4 w-4 animate-spin mr-2" />
68+ Loading...
69+ </ div >
70+ ) : (
71+ < >
72+ < CommandEmpty > No results.</ CommandEmpty >
73+ < CommandGroup >
74+ { showAll && (
75+ < CommandItem
76+ value = "_all"
77+ onSelect = { ( ) => {
78+ handleNamespaceChange ( '_all' )
79+ setOpen ( false )
80+ } }
81+ >
82+ < Check className = { cn ( "mr-2 h-4 w-4 shrink-0" , selectedNamespace === '_all' ? "opacity-100" : "opacity-0" ) } />
83+ < span className = "truncate" > All Namespaces</ span >
84+ </ CommandItem >
85+ ) }
86+
87+ { sortedNamespaces . map ( ( ns : Namespace ) => {
88+ const name = ns . metadata ?. name || ''
89+ return (
90+ < CommandItem
91+ key = { name }
92+ value = { name }
93+ onSelect = { ( val ) => {
94+ handleNamespaceChange ( val )
95+ setOpen ( false )
96+ } }
97+ className = "flex items-center"
98+ >
99+ < Check className = { cn ( "mr-2 h-4 w-4 shrink-0" , selectedNamespace === name ? "opacity-100" : "opacity-0" ) } />
100+ < span className = "truncate flex-1 min-w-0" title = { name } >
101+ { name }
102+ </ span >
103+ </ CommandItem >
104+ )
105+ } ) }
106+ </ CommandGroup >
107+ </ >
108+ ) }
109+ </ CommandList >
110+ </ Command >
111+ </ PopoverContent >
112+ </ Popover >
52113 )
53- }
114+ }
0 commit comments