@@ -10,29 +10,37 @@ import {
1010 MenuDivider ,
1111 MenuOptionGroup ,
1212 MenuItemOption ,
13+ chakra ,
1314} from "@chakra-ui/react" ;
1415import { useEffect , useMemo } from "react" ;
1516import { countBy } from "lodash-es" ;
1617
1718// Map from value to number of pulls that have that value
1819type ValueGetter = ( pull : Pull ) => string ;
1920
21+ const SHOWALL = "SHOWALL" ;
22+
2023type FilterMenuProps = {
2124 urlParam : string ;
2225 buttonText : string ;
2326 extractValueFromPull : ValueGetter ;
27+ defaultExculdedValues ?: string [ ] ;
2428} ;
2529
2630export function FilterMenu ( {
2731 urlParam,
2832 buttonText,
2933 extractValueFromPull,
34+ defaultExculdedValues,
3035} : FilterMenuProps ) {
3136 const pulls = useAllOpenPulls ( ) ;
3237 // Default is empty array that implies show all pulls (no filtering)
3338 const [ selectedValues , setSelectedValues ] = useArrayUrlState ( urlParam , [ ] ) ;
34- // Nothing selected == show everything, otherwise, it'd be empty
35- const showAll = selectedValues . length === 0 ;
39+ // Nothing selected == show the default values (everything except the excluded values)
40+ const showDefault = selectedValues . length === 0 ;
41+ // Show every single value if the magic SHOWALL string is selected
42+ const showAll = notEmpty ( defaultExculdedValues ) ? selectedValues . includes ( SHOWALL ) : selectedValues . length === 0 ;
43+
3644 // List from url may contain values we have no pulls for
3745 const urlValues = useConst ( ( ) => new Set ( selectedValues ) ) ;
3846 const setPullFilter = useSetFilter ( ) ;
@@ -41,50 +49,87 @@ export function FilterMenu({
4149 const allValues = useMemo ( ( ) => {
4250 // All values of open pulls
4351 const pullValues = new Set < string > ( pulls . map ( extractValueFromPull ) ) ;
44- return sortValues ( [ ...new Set ( [ ...pullValues , ...urlValues ] ) ] ) ;
52+ const allValuesSet = new Set ( [ ...pullValues , ...urlValues ] ) ;
53+ allValuesSet . delete ( SHOWALL ) ;
54+ return sortValues ( [ ...allValuesSet ] ) ;
4555 } , [ pulls ] ) ;
56+
4657 const valueToPullCount = useMemo (
4758 ( ) => countBy ( pulls , extractValueFromPull ) ,
4859 [ pulls ]
4960 ) ;
5061
62+ const defaultSelectedValues = arrayDiff ( allValues , defaultExculdedValues || [ ] ) ;
63+
5164 useEffect ( ( ) => {
5265 const selectedValuesSet = new Set ( selectedValues ) ;
5366 setPullFilter (
5467 urlParam ,
55- selectedValuesSet . size === 0
68+ showAll
5669 ? null
57- : ( pull ) => selectedValuesSet . has ( extractValueFromPull ( pull ) )
70+ : ( showDefault ? ( pull ) => ! defaultExculdedValues ?. includes ( extractValueFromPull ( pull ) )
71+ : ( pull ) => selectedValuesSet . has ( extractValueFromPull ( pull ) ) )
5872 ) ;
59- } , [ selectedValues ] ) ;
73+ } , [ selectedValues , defaultExculdedValues ] ) ;
74+
75+ const numberText = showDefault ? "" : ( showAll ? allValues . length : selectedValues . length ) ;
6076
6177 return (
6278 < Menu closeOnSelect = { false } >
6379 < MenuButton
6480 as = { Button }
6581 colorScheme = "blue"
6682 size = "sm"
67- variant = { showAll ? "outline" : null }
83+ variant = { showDefault ? "outline" : null }
6884 >
69- { buttonText } { selectedValues . length ? `(${ selectedValues . length } )` : "" }
85+ { buttonText } { numberText ? `(${ numberText } )` : "" }
7086 </ MenuButton >
7187 < MenuList minWidth = "240px" >
72- < MenuItemOption
73- key = "Show All"
74- onClick = { ( ) => setSelectedValues ( [ ] ) }
75- isChecked = { showAll }
76- >
77- Show All
78- </ MenuItemOption >
88+ { notEmpty ( defaultExculdedValues ) && (
89+ < >
90+ < MenuItemOption
91+ key = "Show All"
92+ onClick = { ( ) => setSelectedValues ( [ SHOWALL ] ) }
93+ >
94+ Show All
95+ </ MenuItemOption >
96+ < MenuItemOption
97+ key = "Show Default"
98+ onClick = { ( ) => setSelectedValues ( [ ] ) }
99+ >
100+ Show Default
101+ </ MenuItemOption >
102+ </ > )
103+ }
104+ { empty ( defaultExculdedValues ) &&
105+ < MenuItemOption
106+ key = "Show All"
107+ onClick = { ( ) => setSelectedValues ( [ ] ) }
108+ >
109+ Show All
110+ </ MenuItemOption >
111+ }
79112 < MenuDivider />
80113 < MenuOptionGroup
81114 type = "checkbox"
82- value = { showAll ? [ ] : selectedValues }
115+ value = { showAll ? allValues : ( showDefault ? defaultSelectedValues : selectedValues ) }
83116 onChange = { setSelectedValues }
84117 >
85118 { allValues . map ( ( value ) => (
86- < MenuItemOption key = { value } value = { value } >
119+ < MenuItemOption className = "filterOption" key = { value } value = { value } >
87120 { value } ({ valueToPullCount [ value ] || 0 } )
121+ < chakra . span
122+ visibility = "hidden"
123+ float = "right"
124+ _hover = { { textDecoration :"underline" } }
125+ mt = { 1 }
126+ fontSize = "xs"
127+ sx = { { '.filterOption:hover &' : { visibility : 'visible' } } }
128+ onClick = { ( e ) => {
129+ setSelectedValues ( [ value ] ) ;
130+ e . stopPropagation ( ) ;
131+ }
132+ } > only</ chakra . span >
88133 </ MenuItemOption >
89134 ) ) }
90135 </ MenuOptionGroup >
@@ -98,3 +143,15 @@ function sortValues(values: string[]): string[] {
98143 a . localeCompare ( b , undefined , { sensitivity : "base" } )
99144 ) ;
100145}
146+
147+ function arrayDiff < T > ( a : T [ ] , b : T [ ] ) : T [ ] {
148+ return a . filter ( ( x ) => ! b . includes ( x ) ) ;
149+ }
150+
151+ function empty < T > ( array : T [ ] | undefined ) : boolean {
152+ return ! array || array . length === 0 ;
153+ }
154+
155+ function notEmpty < T > ( array : T [ ] | undefined ) : boolean {
156+ return ! ! array && array . length > 0 ;
157+ }
0 commit comments