4
4
*-----------------------------------------------------------------------------------------------*/
5
5
import { Close , FileCopy , Launch , Search } from '@mui/icons-material' ;
6
6
import {
7
- Box , Button ,
7
+ Box ,
8
+ Button ,
9
+ Chip ,
8
10
Checkbox ,
9
11
Divider ,
10
12
FormControl ,
@@ -135,31 +137,105 @@ function RegistriesPicker(props: {
135
137
}
136
138
137
139
return (
138
- < Stack direction = "column" spacing = { 1 } marginY = { 2 } >
139
- < Typography variant = "body2" marginBottom = { 1 } >
140
- Devfile Registries
141
- </ Typography >
142
- < FormGroup >
143
- { props . registryEnabled . map ( ( registry ) => {
144
- return (
145
- < FormControlLabel
146
- control = {
147
- < Checkbox
148
- disabled = {
149
- registry . registryUrl === 'https://registry.devfile.io'
150
- }
151
- checked = { registry . enabled }
152
- onChange = { ( _e , checked ) =>
153
- onCheckboxClick ( registry . registryName , checked )
154
- }
155
- />
156
- }
157
- label = { registry . registryName }
158
- key = { registry . registryName }
159
- />
160
- ) ;
161
- } ) }
162
- </ FormGroup >
140
+ < FormGroup >
141
+ { props . registryEnabled . map ( ( registry ) => {
142
+ return (
143
+ < FormControlLabel
144
+ control = {
145
+ < Checkbox
146
+ size = 'small'
147
+ disabled = {
148
+ registry . registryUrl === 'https://registry.devfile.io'
149
+ }
150
+ checked = { registry . enabled }
151
+ onChange = { ( _e , checked ) =>
152
+ onCheckboxClick ( registry . registryName , checked )
153
+ }
154
+ />
155
+ }
156
+ label = { registry . registryName }
157
+ key = { registry . registryName }
158
+ />
159
+ ) ;
160
+ } ) }
161
+ </ FormGroup >
162
+ ) ;
163
+ }
164
+
165
+ function RegistryCapabilitiesPicker ( props : {
166
+ capabilityEnabled : { capabilityName : string ; enabled : boolean } [ ] ;
167
+ setCapabilityEnabled : React . Dispatch <
168
+ React . SetStateAction < { capabilityName : string ; enabled : boolean } [ ] >
169
+ > ;
170
+ } ) {
171
+ function onClick ( clickedCapability : string , checked : boolean ) {
172
+ const updatedList = [ ...props . capabilityEnabled ] //
173
+ . filter ( ( entry ) => entry . capabilityName !== clickedCapability ) ;
174
+ updatedList . push ( {
175
+ capabilityName : clickedCapability ,
176
+ enabled : checked ,
177
+ } ) ;
178
+ const filteredUpdatedList = updatedList
179
+ . sort ( ( capA , capB ) => {
180
+ return capA . capabilityName . localeCompare ( capB . capabilityName )
181
+ } ) ;
182
+ props . setCapabilityEnabled ( [ ...filteredUpdatedList ] ) ;
183
+ }
184
+
185
+ return (
186
+ < Stack spacing = { 1 } useFlexGap direction = 'row' flexWrap = 'wrap' >
187
+ { props . capabilityEnabled . map ( ( _cap ) => {
188
+ return (
189
+ < Chip
190
+ size = "small"
191
+ sx = { { borderSpacing : '3' , margin :'1' } }
192
+ clickable = { true }
193
+ color = { _cap . enabled ? 'success' :'default' }
194
+ onClick = { ( _ ) => { onClick ( _cap . capabilityName , ! _cap . enabled ) } }
195
+ label = { _cap . capabilityName }
196
+ key = { _cap . capabilityName }
197
+ />
198
+ ) ;
199
+ } ) }
200
+ </ Stack >
201
+ ) ;
202
+ }
203
+
204
+ function RegistryTagsPicker ( props : {
205
+ tagEnabled : { tagName : string ; enabled : boolean } [ ] ;
206
+ setTagEnabled : React . Dispatch <
207
+ React . SetStateAction < { tagName : string ; enabled : boolean } [ ] >
208
+ > ;
209
+ } ) {
210
+ function onClick ( clickedTag : string , checked : boolean ) {
211
+ const updatedList = [ ...props . tagEnabled ] //
212
+ . filter ( ( entry ) => entry . tagName !== clickedTag ) ;
213
+ updatedList . push ( {
214
+ tagName : clickedTag ,
215
+ enabled : checked ,
216
+ } ) ;
217
+ const filteredUpdatedList = updatedList
218
+ . sort ( ( tagA , tagB ) => {
219
+ return tagA . tagName . localeCompare ( tagB . tagName )
220
+ } ) ;
221
+ props . setTagEnabled ( [ ...filteredUpdatedList ] ) ;
222
+ }
223
+
224
+ return (
225
+ < Stack spacing = { 1 } useFlexGap direction = 'row' flexWrap = 'wrap' >
226
+ { props . tagEnabled . map ( ( _tag ) => {
227
+ return (
228
+ < Chip
229
+ size = "small"
230
+ sx = { { borderSpacing : '3' , margin :'1' } }
231
+ clickable = { true }
232
+ color = { _tag . enabled ? 'success' :'default' }
233
+ onClick = { ( _ ) => { onClick ( _tag . tagName , ! _tag . enabled ) } }
234
+ label = { _tag . tagName }
235
+ key = { _tag . tagName }
236
+ />
237
+ ) ;
238
+ } ) }
163
239
</ Stack >
164
240
) ;
165
241
}
@@ -391,6 +467,26 @@ export type DevfileSearchProps = {
391
467
goBack ?: ( ) => void ;
392
468
} ;
393
469
470
+ /**
471
+ * Calculates if specified devfile is to be included into search results
472
+ * based on devfile tags and tags filter. A devfile is to be included if:
473
+ * - it contains any of selected tags
474
+ * - always if there is no any selected tags
475
+ *
476
+ * @param tags1
477
+ * @param tags2
478
+ * @returns
479
+ */
480
+ function isToBeIncluded ( devfile : Devfile , tagFilter : string [ ] , debugSupportFilter : boolean , deploySupportFilter : boolean ) : boolean {
481
+ const includesDebugSupport = debugSupportFilter === false || debugSupportFilter === devfile . supportsDebug ;
482
+ const includesDeploySupport = deploySupportFilter === false || deploySupportFilter === devfile . supportsDeploy ;
483
+ const includesTags = tagFilter . length === 0 || devfile . tags . filter ( ( _devfileTag ) => {
484
+ return tagFilter . find ( ( _selectedTags ) => _devfileTag === _selectedTags ) !== undefined ;
485
+ } ) . length > 0 ;
486
+
487
+ return includesDebugSupport && includesDeploySupport && includesTags ;
488
+ }
489
+
394
490
export function DevfileSearch ( props : DevfileSearchProps ) {
395
491
const ITEMS_PER_PAGE = 6 ;
396
492
const QUARKUS_REGEX = / [ Q q ] u a r k u s / ;
@@ -401,6 +497,14 @@ export function DevfileSearch(props: DevfileSearchProps) {
401
497
const [ registryEnabled , setRegistryEnabled ] = React . useState <
402
498
{ registryName : string ; registryUrl : string ; enabled : boolean } [ ]
403
499
> ( [ ] ) ;
500
+ const [ devfileCapabilities , setDevfileCapabilities ] = React . useState < string [ ] > ( [ ] ) ;
501
+ const [ capabilityEnabled , setCapabilityEnabled ] = React . useState <
502
+ { capabilityName : string ; enabled : boolean } [ ]
503
+ > ( [ ] ) ;
504
+ const [ devfileTags , setDevfileTags ] = React . useState < string [ ] > ( [ ] ) ;
505
+ const [ tagEnabled , setTagEnabled ] = React . useState <
506
+ { tagName : string ; enabled : boolean } [ ]
507
+ > ( [ ] ) ;
404
508
const [ searchText , setSearchText ] = React . useState ( '' ) ;
405
509
406
510
function respondToMessage ( messageEvent : MessageEvent ) {
@@ -410,6 +514,14 @@ export function DevfileSearch(props: DevfileSearchProps) {
410
514
setDevfileRegistries ( ( _devfileRegistries ) => message . data ) ;
411
515
break ;
412
516
}
517
+ case 'devfileCapabilities' : {
518
+ setDevfileCapabilities ( ( _devfileCapabilities ) => message . data ) ;
519
+ break ;
520
+ }
521
+ case 'devfileTags' : {
522
+ setDevfileTags ( ( _devfileTags ) => message . data ) ;
523
+ break ;
524
+ }
413
525
default :
414
526
break ;
415
527
}
@@ -427,6 +539,28 @@ export function DevfileSearch(props: DevfileSearchProps) {
427
539
setRegistryEnabled ( ( _ ) => enabledArray ) ;
428
540
} , [ devfileRegistries ] ) ;
429
541
542
+ React . useEffect ( ( ) => {
543
+ const enabledArray = [ ] ;
544
+ for ( const capability of devfileCapabilities ) {
545
+ enabledArray . push ( {
546
+ capabilityName : capability ,
547
+ enabled : false , // All values set to false means that no filter is to be applied
548
+ } ) ;
549
+ }
550
+ setCapabilityEnabled ( ( _ ) => enabledArray ) ;
551
+ } , [ devfileCapabilities ] ) ;
552
+
553
+ React . useEffect ( ( ) => {
554
+ const enabledArray = [ ] ;
555
+ for ( const tag of devfileTags ) {
556
+ enabledArray . push ( {
557
+ tagName : tag ,
558
+ enabled : false , // All values set to false means that no filter is to be applied
559
+ } ) ;
560
+ }
561
+ setTagEnabled ( ( _ ) => enabledArray ) ;
562
+ } , [ devfileTags ] ) ;
563
+
430
564
React . useEffect ( ( ) => {
431
565
props . setSelectedDevfile ( selectedDevfile ) ;
432
566
} , [ selectedDevfile ] ) ;
@@ -442,21 +576,52 @@ export function DevfileSearch(props: DevfileSearchProps) {
442
576
window . vscodeApi . postMessage ( { action : 'getDevfileRegistries' } ) ;
443
577
} , [ ] ) ;
444
578
579
+ React . useEffect ( ( ) => {
580
+ window . vscodeApi . postMessage ( { action : 'getDevfileCapabilities' } ) ;
581
+ } , [ ] ) ;
582
+
583
+ React . useEffect ( ( ) => {
584
+ window . vscodeApi . postMessage ( { action : 'getDevfileTags' } ) ;
585
+ } , [ ] ) ;
586
+
445
587
React . useEffect ( ( ) => {
446
588
setCurrentPage ( ( _ ) => 1 ) ;
447
- } , [ registryEnabled , searchText ] ) ;
589
+ } , [ registryEnabled , capabilityEnabled , tagEnabled , searchText ] ) ;
448
590
449
591
if ( ! devfileRegistries ) {
450
592
return < LoadScreen title = "Retrieving list of Devfiles" /> ;
451
593
}
452
594
595
+ if ( ! devfileCapabilities ) {
596
+ return < LoadScreen title = "Retrieving list of Devfile Capabilities" /> ;
597
+ }
598
+
599
+ if ( ! devfileTags ) {
600
+ return < LoadScreen title = "Retrieving list of Devfile Tags" /> ;
601
+ }
602
+
453
603
const activeRegistries = registryEnabled //
454
604
. filter ( ( entry ) => entry . enabled ) //
455
605
. map ( ( entry ) => entry . registryName ) ;
456
606
607
+ const debugSupport = capabilityEnabled //
608
+ . filter ( ( _cap ) => _cap . capabilityName === 'Debug Support' ) //
609
+ . filter ( ( _cap ) => _cap . enabled ) //
610
+ . length > 0 ;
611
+
612
+ const deploySupport = capabilityEnabled //
613
+ . filter ( ( _cap ) => _cap . capabilityName === 'Deploy Support' ) //
614
+ . filter ( ( _cap ) => _cap . enabled ) //
615
+ . length > 0 ;
616
+
617
+ const activeTags = tagEnabled
618
+ . filter ( ( _tag ) => _tag . enabled ) //
619
+ . map ( ( _tag ) => _tag . tagName ) ;
620
+
457
621
const devfiles : Devfile [ ] = devfileRegistries //
458
622
. filter ( ( devfileRegistry ) => activeRegistries . includes ( devfileRegistry . name ) ) //
459
623
. flatMap ( ( devfileRegistry ) => devfileRegistry . devfiles ) //
624
+ . filter ( ( devfile ) => isToBeIncluded ( devfile , activeTags , debugSupport , deploySupport ) ) //
460
625
. filter ( ( devfile ) => {
461
626
const searchTerms = searchText . split ( / \s + / ) ;
462
627
return every (
@@ -490,17 +655,52 @@ export function DevfileSearch(props: DevfileSearchProps) {
490
655
< >
491
656
< Stack direction = "column" height = "100%" spacing = { 3 } >
492
657
< Typography variant = "h5" > { props . titleText } </ Typography >
493
- < Stack direction = "row" flexGrow = "1" spacing = { 2 } >
494
- { devfileRegistries . length > 1 && (
495
- < >
496
- < RegistriesPicker
497
- registryEnabled = { registryEnabled }
498
- setRegistryEnabled = { setRegistryEnabled }
499
- />
500
- < Divider orientation = "vertical" />
501
- </ >
502
- ) }
503
- < Stack direction = "column" sx = { { flexGrow : '1' , height : '100%' } } spacing = { 3 } >
658
+ < Stack direction = "row" spacing = { 2 } >
659
+ < Stack direction = "column" sx = { { height : 'calc(100vh - 200px - 5em)' , overflow : 'scroll' , maxWidth :'30%' } } spacing = { 0 } >
660
+ { devfileRegistries . length > 1 && (
661
+ < >
662
+ < Typography variant = "body2" marginBottom = { 1 } >
663
+ Devfile Registries
664
+ </ Typography >
665
+ < Stack direction = "column" sx = { { width : '100%' } } width = "100%" spacing = { 0 } marginBottom = { 3 } >
666
+ < RegistriesPicker
667
+ registryEnabled = { registryEnabled }
668
+ setRegistryEnabled = { setRegistryEnabled }
669
+ />
670
+ </ Stack >
671
+ </ >
672
+ ) }
673
+ { ( devfileCapabilities . length > 0 || devfileTags . length > 0 ) && (
674
+ < >
675
+ < Typography variant = "body2" marginBottom = { 2 } >
676
+ Filter by
677
+ </ Typography >
678
+ < Stack direction = "column" useFlexGap = { true } width = "100%" spacing = { 1 } >
679
+ { devfileCapabilities . length > 0 && (
680
+ < >
681
+ < RegistryCapabilitiesPicker
682
+ capabilityEnabled = { capabilityEnabled }
683
+ setCapabilityEnabled = { setCapabilityEnabled }
684
+ />
685
+ < Divider orientation = "horizontal" sx = { { width : '100%' } } />
686
+ </ >
687
+ ) }
688
+ { devfileTags . length > 0 && (
689
+ < RegistryTagsPicker
690
+ tagEnabled = { tagEnabled }
691
+ setTagEnabled = { setTagEnabled }
692
+ />
693
+ ) }
694
+ </ Stack >
695
+ </ >
696
+ ) }
697
+ < Stack direction = "column" sx = { { flexGrow : '1' , height : '100%' , width : '100%' } } spacing = { 0 } >
698
+ </ Stack >
699
+ </ Stack >
700
+ < Stack direction = "column" spacing = { 3 } >
701
+ < Divider orientation = "vertical" sx = { { height : 'calc(100vh - 200px - 5em)' } } />
702
+ </ Stack >
703
+ < Stack direction = "column" sx = { { flexGrow : '1' } } spacing = { 3 } >
504
704
< SearchBar
505
705
searchText = { searchText }
506
706
setSearchText = { setSearchText }
0 commit comments