@@ -13,7 +13,17 @@ import { Styled } from './styled'
1313
1414type TSearchProps = {
1515 cluster : string
16- updateCurrentSearch : ( { resources, name, labels } : { resources ?: string [ ] ; name ?: string ; labels ?: string [ ] } ) => void
16+ updateCurrentSearch : ( {
17+ resources,
18+ name,
19+ labels,
20+ fields,
21+ } : {
22+ resources ?: string [ ]
23+ name ?: string
24+ labels ?: string [ ]
25+ fields ?: string [ ]
26+ } ) => void
1727}
1828
1929export const Search : FC < TSearchProps > = ( { cluster, updateCurrentSearch } ) => {
@@ -23,17 +33,20 @@ export const Search: FC<TSearchProps> = ({ cluster, updateCurrentSearch }) => {
2333
2434 const FIELD_NAME = 'kinds'
2535 const FIELD_NAME_STRING = 'name'
26- const FIELD_NAME_MULTIPLE = 'labels'
36+ const FIELD_NAME_LABELS = 'labels'
37+ const FIELD_NAME_FIELDS = 'fields'
2738
2839 const TYPE_SELECTOR = 'TYPE_SELECTOR'
2940
3041 const QUERY_KEY = 'kinds' // the query param name
3142 const NAME_QUERY_KEY = 'name'
3243 const LABELS_QUERY_KEY = 'labels'
44+ const FIELDS_QUERY_KEY = 'fields'
3345
3446 const watchedKinds = Form . useWatch < string [ ] | undefined > ( FIELD_NAME , form )
3547 const watchedName = Form . useWatch < string | undefined > ( FIELD_NAME_STRING , form )
36- const watchedMultiple = Form . useWatch < string [ ] | undefined > ( FIELD_NAME_MULTIPLE , form )
48+ const watchedLabels = Form . useWatch < string [ ] | undefined > ( FIELD_NAME_LABELS , form )
49+ const watchedFields = Form . useWatch < string [ ] | undefined > ( FIELD_NAME_FIELDS , form )
3750 const watchedTypedSelector = Form . useWatch < string | undefined > ( TYPE_SELECTOR , form )
3851
3952 // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -63,26 +76,35 @@ export const Search: FC<TSearchProps> = ({ cluster, updateCurrentSearch }) => {
6376
6477 // labels
6578 const fromLabels = getArrayParam ( searchParams , LABELS_QUERY_KEY )
66- const currentLabels = form . getFieldValue ( FIELD_NAME_MULTIPLE ) as string [ ] | undefined
79+ const currentLabels = form . getFieldValue ( FIELD_NAME_LABELS ) as string [ ] | undefined
6780 const labelsDiffer =
6881 ( fromLabels . length || 0 ) !== ( currentLabels ?. length || 0 ) || fromLabels . some ( ( v , i ) => v !== currentLabels ?. [ i ] )
6982
83+ // labels
84+ const fromFields = getArrayParam ( searchParams , FIELDS_QUERY_KEY )
85+ const currentFields = form . getFieldValue ( FIELD_NAME_FIELDS ) as string [ ] | undefined
86+ const fieldsDiffer =
87+ ( fromFields . length || 0 ) !== ( currentFields ?. length || 0 ) || fromFields . some ( ( v , i ) => v !== currentFields ?. [ i ] )
88+
7089 // decide type from params
7190 const currentType = form . getFieldValue ( TYPE_SELECTOR )
7291 let inferredType : string | undefined
7392 if ( fromName ) {
7493 inferredType = 'name'
7594 } else if ( fromLabels . length > 0 ) {
7695 inferredType = 'labels'
96+ } else if ( fromFields . length > 0 ) {
97+ inferredType = 'fields'
7798 }
7899 const typeDiffer = inferredType !== currentType
79100
80101 // Only update the form if URL differs from form (prevents loops)
81- if ( kindsDiffer || nameDiffer || labelsDiffer ) {
102+ if ( kindsDiffer || nameDiffer || labelsDiffer || fieldsDiffer ) {
82103 form . setFieldsValue ( {
83104 [ FIELD_NAME ] : kindsDiffer ? fromKinds : currentKinds ,
84105 [ FIELD_NAME_STRING ] : nameDiffer ? fromName : currentName ,
85- [ FIELD_NAME_MULTIPLE ] : labelsDiffer ? fromLabels : currentLabels ,
106+ [ FIELD_NAME_LABELS ] : labelsDiffer ? fromLabels : currentLabels ,
107+ [ FIELD_NAME_FIELDS ] : fieldsDiffer ? fromFields : currentFields ,
86108 [ TYPE_SELECTOR ] : typeDiffer ? inferredType : currentType ,
87109 } )
88110 }
@@ -105,6 +127,11 @@ export const Search: FC<TSearchProps> = ({ cluster, updateCurrentSearch }) => {
105127 setSearchParams ( next , { replace : true } )
106128 } , 250 )
107129
130+ const debouncedPushFields = useDebouncedCallback ( ( values : string [ ] ) => {
131+ const next = setArrayParam ( searchParams , FIELDS_QUERY_KEY , values )
132+ setSearchParams ( next , { replace : true } )
133+ } , 250 )
134+
108135 useEffect ( ( ) => {
109136 debouncedPush ( watchedKinds || [ ] )
110137 // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -116,23 +143,34 @@ export const Search: FC<TSearchProps> = ({ cluster, updateCurrentSearch }) => {
116143 } , [ watchedName ] )
117144
118145 useEffect ( ( ) => {
119- debouncedPushLabels ( watchedMultiple || [ ] )
146+ debouncedPushLabels ( watchedLabels || [ ] )
120147 // eslint-disable-next-line react-hooks/exhaustive-deps
121- } , [ watchedMultiple ] )
148+ } , [ watchedLabels ] )
149+
150+ useEffect ( ( ) => {
151+ debouncedPushFields ( watchedFields || [ ] )
152+ // eslint-disable-next-line react-hooks/exhaustive-deps
153+ } , [ watchedFields ] )
122154
123155 useEffect ( ( ) => {
124156 if ( watchedTypedSelector === 'name' ) {
125157 // Clear labels when switching to "name"
126- const cur = form . getFieldValue ( FIELD_NAME_MULTIPLE ) as string [ ] | undefined
127- if ( cur ?. length ) {
128- form . setFieldsValue ( { [ FIELD_NAME_MULTIPLE ] : [ ] } )
129- }
158+ // const cur = form.getFieldValue(FIELD_NAME_LABELS ) as string[] | undefined
159+ // if (cur?.length) {
160+ form . setFieldsValue ( { [ FIELD_NAME_LABELS ] : [ ] , [ FIELD_NAME_FIELDS ] : [ ] } )
161+ // }
130162 } else if ( watchedTypedSelector === 'labels' ) {
131163 // Clear name when switching to "labels"
132- const cur = ( form . getFieldValue ( FIELD_NAME_STRING ) as string | undefined ) ?? ''
133- if ( cur ) {
134- form . setFieldsValue ( { [ FIELD_NAME_STRING ] : '' } )
135- }
164+ // const cur = (form.getFieldValue(FIELD_NAME_STRING) as string | undefined) ?? ''
165+ // if (cur) {
166+ form . setFieldsValue ( { [ FIELD_NAME_STRING ] : '' , [ FIELD_NAME_FIELDS ] : [ ] } )
167+ // }
168+ } else if ( watchedTypedSelector === 'fields' ) {
169+ // Clear name when switching to "labels"
170+ // const cur = (form.getFieldValue(FIELD_NAME_STRING) as string | undefined) ?? ''
171+ // if (cur) {
172+ form . setFieldsValue ( { [ FIELD_NAME_STRING ] : '' , [ FIELD_NAME_LABELS ] : [ ] } )
173+ // }
136174 }
137175 // Optional: if undefined (e.g., initial), choose a default behavior:
138176 // else { form.setFieldsValue({ [FIELD_NAME_STRING]: '', [FIELD_NAME_MULTIPLE]: [] }) }
@@ -197,20 +235,25 @@ export const Search: FC<TSearchProps> = ({ cluster, updateCurrentSearch }) => {
197235 options = { [
198236 { label : 'Name' , value : 'name' } ,
199237 { label : 'Labels' , value : 'labels' } ,
238+ { label : 'Fields' , value : 'fields' } ,
200239 ] }
201240 defaultValue = "name"
202241 filterOption = { filterSelectOptions }
203242 showSearch
204243 />
205244 </ Form . Item >
206- < Styled . HideableContainer $isHidden = { watchedTypedSelector === 'labels' } >
245+ < Styled . HideableContainer $isHidden = { watchedTypedSelector === 'labels' || watchedTypedSelector === 'fields' } >
207246 < Form . Item name = { FIELD_NAME_STRING } label = "Name" >
208247 < Input allowClear />
209248 </ Form . Item >
210249 </ Styled . HideableContainer >
211- < Styled . HideableContainer $isHidden = { watchedTypedSelector === 'name' || watchedTypedSelector === undefined } >
250+ < Styled . HideableContainer
251+ $isHidden = {
252+ watchedTypedSelector === 'name' || watchedTypedSelector === 'fields' || watchedTypedSelector === undefined
253+ }
254+ >
212255 < Form . Item
213- name = { FIELD_NAME_MULTIPLE }
256+ name = { FIELD_NAME_LABELS }
214257 label = "Labels"
215258 validateTrigger = "onBlur"
216259 rules = { [
@@ -239,11 +282,51 @@ export const Search: FC<TSearchProps> = ({ cluster, updateCurrentSearch }) => {
239282 />
240283 </ Form . Item >
241284 </ Styled . HideableContainer >
285+ < Styled . HideableContainer
286+ $isHidden = {
287+ watchedTypedSelector === 'name' || watchedTypedSelector === 'labels' || watchedTypedSelector === undefined
288+ }
289+ >
290+ < Form . Item
291+ name = { FIELD_NAME_FIELDS }
292+ label = "Fields"
293+ validateTrigger = "onBlur"
294+ rules = { [
295+ ( ) => ( {
296+ validator ( _ , value ) {
297+ if (
298+ Array . isArray ( value ) &&
299+ value . every ( str => typeof str === 'string' && str . includes ( '=' ) && ! str . startsWith ( '=' ) )
300+ ) {
301+ return Promise . resolve ( )
302+ }
303+ return Promise . reject ( new Error ( 'Please enter key=value style' ) )
304+ } ,
305+ } ) ,
306+ ] }
307+ >
308+ < Select
309+ mode = "tags"
310+ allowClear
311+ placeholder = "Select"
312+ // dropdownStyle={{ display: 'none' }}
313+ tokenSeparators = { [ ',' , ' ' , ' ' ] }
314+ suffixIcon = { null }
315+ filterOption = { filterSelectOptions }
316+ tagRender = { tagRender }
317+ />
318+ </ Form . Item >
319+ </ Styled . HideableContainer >
242320 < Form . Item label = "Search" >
243321 < Button
244322 type = "primary"
245323 onClick = { ( ) =>
246- updateCurrentSearch ( { resources : watchedKinds , name : watchedName , labels : watchedMultiple } )
324+ updateCurrentSearch ( {
325+ resources : watchedKinds ,
326+ name : watchedName ,
327+ labels : watchedLabels ,
328+ fields : watchedFields ,
329+ } )
247330 }
248331 >
249332 Search
@@ -254,7 +337,8 @@ export const Search: FC<TSearchProps> = ({ cluster, updateCurrentSearch }) => {
254337 { /* Example of "watching" the value for display or side-effects */ }
255338 < div > Current: { ( watchedKinds || [ ] ) . join ( ', ' ) } </ div >
256339 < div > Current name: { watchedName } </ div >
257- < div > Current labels: { ( watchedMultiple || [ ] ) . join ( ', ' ) } </ div >
340+ < div > Current labels: { ( watchedLabels || [ ] ) . join ( ', ' ) } </ div >
341+ < div > Current fields: { ( watchedFields || [ ] ) . join ( ', ' ) } </ div >
258342 </ div >
259343 )
260344}
0 commit comments