@@ -307,13 +307,14 @@ interface FilterCheckboxProps extends React.HTMLAttributes<HTMLElement> {
307
307
incompatibleTags ?: Tag [ ] ; // tags that are removed when this tag is added
308
308
dependentTags ?: Tag [ ] ; // tags that are removed when this tag is removed
309
309
baseTag ?: Tag ; // tag to add when all tags are removed
310
+ partial ?: boolean ; // if true, the checkbox can be partially selected
310
311
partiallySelected ?: boolean ;
311
312
checkboxStyle ?: "tab" | "button" ;
312
313
bsSize ?: "sm" | "lg" ;
313
314
}
314
315
315
316
const FilterCheckbox = ( props : FilterCheckboxProps ) => {
316
- const { tag, conceptFilters, setConceptFilters, tagCounts, checkboxStyle, incompatibleTags, dependentTags, baseTag, partiallySelected, ...rest } = props ;
317
+ const { tag, conceptFilters, setConceptFilters, tagCounts, checkboxStyle, incompatibleTags, dependentTags, baseTag, partial , partiallySelected, ...rest } = props ;
317
318
const [ checked , setChecked ] = useState ( conceptFilters . includes ( tag ) ) ;
318
319
319
320
useEffect ( ( ) => {
@@ -331,6 +332,7 @@ const FilterCheckbox = (props : FilterCheckboxProps) => {
331
332
? < StyledCheckbox { ...rest } id = { tag . id } checked = { checked }
332
333
onChange = { ( e : ChangeEvent < HTMLInputElement > ) => handleCheckboxChange ( e . target . checked ) }
333
334
label = { < span > { tag . title } { tagCounts && isDefined ( tagCounts [ tag . id ] ) && < span className = "text-muted" > ({ tagCounts [ tag . id ] } )</ span > } </ span > }
335
+ partial = { partial }
334
336
/>
335
337
: < StyledTabPicker { ...rest } id = { tag . id } checked = { checked }
336
338
onInputChange = { ( e : ChangeEvent < HTMLInputElement > ) => handleCheckboxChange ( e . target . checked ) }
@@ -454,8 +456,8 @@ export const GenericConceptsSidebar = (props: ConceptListSidebarProps) => {
454
456
< FilterCheckbox
455
457
checkboxStyle = "button" color = "theme" data-bs-theme = { subject } tag = { subjectTag } conceptFilters = { conceptFilters }
456
458
setConceptFilters = { setConceptFilters } tagCounts = { tagCounts } dependentTags = { descendentTags } incompatibleTags = { descendentTags }
457
- partiallySelected = { descendentTags . some ( tag => conceptFilters . includes ( tag ) ) } // not quite isPartial; this is also true if all descendents selected
458
- className = { classNames ( { "icon-checkbox-off" : ! isSelected , "icon icon-checkbox-partial-alt" : isSelected && isPartial , "icon-checkbox-selected" : isSelected && ! isPartial } ) }
459
+ partial partiallySelected = { descendentTags . some ( tag => conceptFilters . includes ( tag ) ) } // not quite isPartial; this is also true if all descendents selected
460
+ className = { classNames ( "icon" , { "icon-checkbox-off" : ! isSelected , "icon-checkbox-partial-alt" : isSelected && isPartial , "icon-checkbox-selected" : isSelected && ! isPartial } ) }
459
461
/>
460
462
{ isSelected && < div className = "ms-3 ps-2" >
461
463
{ descendentTags
@@ -1042,43 +1044,37 @@ export const ManageQuizzesSidebar = (props: ManageQuizzesSidebarProps) => {
1042
1044
} ;
1043
1045
1044
1046
export const EventsSidebar = ( props : SidebarProps ) => {
1047
+ const deviceSize = useDeviceSize ( ) ;
1045
1048
const history = useHistory ( ) ;
1046
1049
const query : EventsPageQueryParams = queryString . parse ( history . location . search ) ;
1047
1050
const user = useAppSelector ( selectors . user . orNull ) ;
1048
1051
1049
- return < ContentSidebar style = { { marginTop : "65px" } } buttonTitle = "Filter events" { ...props } >
1052
+ return < ContentSidebar buttonTitle = "Filter events" { ...props } >
1050
1053
< Form >
1054
+ { above [ "lg" ] ( deviceSize ) && < div className = "section-divider mt-5" /> }
1051
1055
< h5 className = "mb-3" > Event type</ h5 >
1052
1056
< ul >
1053
1057
{ Object . entries ( EventStatusFilter )
1054
1058
. filter ( ( [ _statusLabel , statusValue ] ) => ( user && user . loggedIn ) || statusValue !== EventStatusFilter [ "My booked events" ] )
1055
1059
. filter ( ( [ _statusLabel , statusValue ] ) => ( user && user . loggedIn && isTeacherOrAbove ( user ) ) || statusValue !== EventStatusFilter [ "My event reservations" ] )
1056
1060
. map ( ( [ statusLabel , statusValue ] ) =>
1057
- < li className = "list-unstyled" key = { statusValue } >
1058
- < Label className = "py-1 label-radio d-flex" >
1059
- < Input
1060
- id = { statusValue }
1061
- name = "event-status"
1062
- color = "primary"
1063
- type = "radio"
1064
- defaultChecked = {
1065
- ( ! isDefined ( query . event_status ) && statusValue === EventStatusFilter [ "Upcoming events" ] ) ||
1066
- ( query . show_booked_only && statusValue === EventStatusFilter [ "My booked events" ] ) ||
1067
- ( query . show_reservations_only && statusValue === EventStatusFilter [ "My event reservations" ] ) ||
1068
- ( query . event_status === "all" && statusValue === EventStatusFilter [ "All events" ] )
1069
- }
1070
- onChange = { ( ) => {
1071
- const selectedFilter = statusValue ;
1072
- query . show_booked_only = selectedFilter === EventStatusFilter [ "My booked events" ] ? true : undefined ;
1073
- query . show_reservations_only = selectedFilter === EventStatusFilter [ "My event reservations" ] ? true : undefined ;
1074
- query . event_status = selectedFilter == EventStatusFilter [ "All events" ] ? "all" : undefined ;
1075
- history . push ( { pathname : location . pathname , search : queryString . stringify ( query as any ) } ) ;
1076
- } }
1077
- />
1078
- < div className = "flex-fill overflow-x-auto" >
1079
- < span > { statusLabel } </ span >
1080
- </ div >
1081
- </ Label >
1061
+ < li key = { statusValue } >
1062
+ < StyledTabPicker
1063
+ id = { statusValue }
1064
+ checkboxTitle = { statusLabel }
1065
+ checked = {
1066
+ ( ! isDefined ( query . event_status ) && ! query . show_booked_only && ! query . show_reservations_only && statusValue === EventStatusFilter [ "Upcoming events" ] ) ||
1067
+ ( query . show_booked_only && statusValue === EventStatusFilter [ "My booked events" ] ) ||
1068
+ ( query . show_reservations_only && statusValue === EventStatusFilter [ "My event reservations" ] ) ||
1069
+ ( query . event_status === "all" && statusValue === EventStatusFilter [ "All events" ] )
1070
+ }
1071
+ onChange = { ( ) => {
1072
+ query . show_booked_only = statusValue === EventStatusFilter [ "My booked events" ] ? true : undefined ;
1073
+ query . show_reservations_only = statusValue === EventStatusFilter [ "My event reservations" ] ? true : undefined ;
1074
+ query . event_status = statusValue === EventStatusFilter [ "All events" ] ? "all" : undefined ;
1075
+ history . push ( { pathname : location . pathname , search : queryString . stringify ( query as any ) } ) ;
1076
+ } }
1077
+ />
1082
1078
</ li >
1083
1079
)
1084
1080
}
@@ -1088,23 +1084,15 @@ export const EventsSidebar = (props: SidebarProps) => {
1088
1084
< h5 className = "mb-3" > Groups</ h5 >
1089
1085
< ul >
1090
1086
{ Object . entries ( EventTypeFilter ) . map ( ( [ typeLabel , typeValue ] ) =>
1091
- < li className = "list-unstyled" key = { typeValue } >
1092
- < Label className = "py-1 label-radio d-flex" >
1093
- < Input
1094
- id = { typeValue }
1095
- name = "event-type"
1096
- color = "primary"
1097
- type = "radio"
1098
- defaultChecked = { query . types ? query . types === typeValue : typeValue === EventTypeFilter [ "All groups" ] }
1099
- onChange = { ( ) => {
1100
- const selectedType = typeValue ;
1101
- query . types = selectedType !== EventTypeFilter [ "All groups" ] ? selectedType : undefined ;
1102
- history . push ( { pathname : location . pathname , search : queryString . stringify ( query as any ) } ) ; } }
1103
- />
1104
- < div className = "flex-fill overflow-x-auto" >
1105
- < span > { typeLabel } </ span >
1106
- </ div >
1107
- </ Label >
1087
+ < li key = { typeValue } >
1088
+ < StyledTabPicker
1089
+ id = { typeValue }
1090
+ checkboxTitle = { typeLabel }
1091
+ checked = { query . types ? query . types === typeValue : typeValue === EventTypeFilter [ "All groups" ] }
1092
+ onChange = { ( ) => {
1093
+ query . types = typeValue !== EventTypeFilter [ "All groups" ] ? typeValue : undefined ;
1094
+ history . push ( { pathname : location . pathname , search : queryString . stringify ( query as any ) } ) ; } }
1095
+ />
1108
1096
</ li >
1109
1097
)
1110
1098
}
@@ -1114,23 +1102,16 @@ export const EventsSidebar = (props: SidebarProps) => {
1114
1102
< h5 className = "mb-3" > Stages</ h5 >
1115
1103
< ul >
1116
1104
{ Object . entries ( EventStageMap ) . map ( ( [ label , value ] ) =>
1117
- < li className = "list-unstyled" key = { value } >
1118
- < Label className = "py-1 label-radio d-flex" >
1119
- < Input
1120
- id = { value }
1121
- name = "event-stage"
1122
- color = "primary"
1123
- type = "radio"
1124
- defaultChecked = { query . show_stage_only ? query . show_stage_only === value : value === STAGE . ALL }
1125
- onChange = { ( ) => {
1126
- query . show_stage_only = value !== STAGE . ALL ? value : undefined ;
1127
- history . push ( { pathname : location . pathname , search : queryString . stringify ( query as any ) } ) ;
1128
- } }
1129
- />
1130
- < div className = "flex-fill overflow-x-auto" >
1131
- < span > { label } </ span >
1132
- </ div >
1133
- </ Label >
1105
+ < li key = { value } >
1106
+ < StyledTabPicker
1107
+ id = { value }
1108
+ checkboxTitle = { label }
1109
+ checked = { query . show_stage_only ? query . show_stage_only === value : value === STAGE . ALL }
1110
+ onChange = { ( ) => {
1111
+ query . show_stage_only = value !== STAGE . ALL ? value : undefined ;
1112
+ history . push ( { pathname : location . pathname , search : queryString . stringify ( query as any ) } ) ;
1113
+ } }
1114
+ />
1134
1115
</ li >
1135
1116
)
1136
1117
}
0 commit comments