@@ -16,28 +16,34 @@ import {
1616 SliderThumb ,
1717 SliderTrack ,
1818 VStack ,
19- useColorMode
19+ useColorMode ,
2020} from "@chakra-ui/react" ;
2121import { useState } from "react" ;
2222import { useReducer } from "react" ;
2323import GraphData from "./graph-data.json" ;
2424import Expandable from "./ExpandableContent" ;
25- import getColor from "../Shared/colors"
25+ import getColor from "../Shared/colors" ;
2626
2727/**
2828 * The reducer for the dataKeys state variable
2929 *
3030 * @param {string[] } state the previous list of selected data keys
31- * @param {{action: string, key: string} }} new the action to perform and the key to add or remove
31+ * @param {{action: string, key: string, keys: string[] } }} new the action to perform and the key to add or remove
3232 * @returns the new state (or list of selected data keys)
3333 */
34- const dataKeysReducer = ( state , { action, key } ) => {
34+ const dataKeysReducer = ( state , { action, key, keys } ) => {
3535 switch ( action ) {
3636 case "add" :
3737 return state . concat ( [ key ] ) ;
3838 case "remove" :
3939 const index = state . indexOf ( key ) ;
4040 return state . slice ( 0 , index ) . concat ( state . slice ( index + 1 ) ) ;
41+ //subcategory actions: adds or removes an entire subcategory of keys
42+ case "addSubcategory" :
43+ return [ ...new Set ( [ ...state , ...keys ] ) ] ;
44+ case "removeSubcategory" :
45+ return state . filter ( ( k ) => ! keys . includes ( k ) ) ;
46+
4147 default :
4248 console . warn ( "DEFAULT CASE REACHED IN GRAPH MODAL" ) ;
4349 return [ ] ;
@@ -79,8 +85,8 @@ function GraphSelectModal(props) {
7985 const { isOpen, onClose, initialDatasets, initialHistoryLength, onSave } =
8086 props ;
8187
82- const { colorMode} = useColorMode ( )
83- const contentBg = getColor ( ' contentBg' , colorMode ) ;
88+ const { colorMode } = useColorMode ( ) ;
89+ const contentBg = getColor ( " contentBg" , colorMode ) ;
8490
8591 // a state variable that keeps track of which datasets are selected to be shown
8692 const [ dataKeys , changeDataKeys ] = useReducer (
@@ -98,36 +104,78 @@ function GraphSelectModal(props) {
98104 < ModalCloseButton />
99105 < ModalBody >
100106 < VStack align = "stretch" >
101- < hr />
107+ < hr />
102108 { GraphData . Output . map ( ( category ) => (
103109 < VStack align = "stretch" key = { category . category } >
104- < Expandable label = { category . category } contentBg = { contentBg } size = "md" >
105- { category . subcategories . map ( ( subcategory ) => (
106- < VStack align = "strech" key = { subcategory . subcategory } >
107- < Heading pt = "2" size = "sm" > { subcategory . subcategory } </ Heading >
108- < SimpleGrid columns = { 2 } >
109- { subcategory . values . map ( ( value ) => (
110+ < Expandable
111+ label = { category . category }
112+ contentBg = { contentBg }
113+ size = "md"
114+ >
115+ { category . subcategories . map ( ( subcategory ) => {
116+ //Find keys for this subcategory.
117+ const childKeys = subcategory . values . map (
118+ ( value ) => value . key
119+ ) ;
120+ //Check if all child checkboxes are selected.
121+ const allSelected = childKeys . every ( ( key ) =>
122+ dataKeys . includes ( key )
123+ ) ;
124+ // Check if some (but not all) child checkboxes are selected.
125+ const someSelected = childKeys . some ( ( key ) =>
126+ dataKeys . includes ( key )
127+ ) ;
128+ return (
129+ < VStack align = "stretch" key = { subcategory . subcategory } >
130+ < Heading pt = "2" size = "sm" >
110131 < Checkbox
111- defaultChecked = { dataKeys . includes ( value . key ) }
112- onInput = { ( e ) => {
132+ isChecked = { allSelected }
133+ isIndeterminate = { someSelected && ! allSelected }
134+ onChange = { ( e ) => {
113135 if ( e . target . checked ) {
114- // checked => add to checked datasets
115- changeDataKeys ( { action : "add" , key : value . key } ) ;
136+ changeDataKeys ( {
137+ action : "addSubcategory" ,
138+ keys : childKeys ,
139+ } ) ;
116140 } else {
117- // unchecked => remove from checked datasets
118- changeDataKeys ( { action : "remove" , key : value . key } ) ;
141+ changeDataKeys ( {
142+ action : "removeSubcategory" ,
143+ keys : childKeys ,
144+ } ) ;
119145 }
120146 } }
121- key = { value . key }
122147 >
123- { value . name }
148+ { subcategory . subcategory }
124149 </ Checkbox >
125- ) ) }
126- </ SimpleGrid >
127- </ VStack >
128- ) ) }
150+ </ Heading >
151+ < SimpleGrid columns = { 2 } >
152+ { subcategory . values . map ( ( value ) => (
153+ < Checkbox
154+ isChecked = { dataKeys . includes ( value . key ) }
155+ onChange = { ( e ) => {
156+ if ( e . target . checked ) {
157+ changeDataKeys ( {
158+ action : "add" ,
159+ key : value . key ,
160+ } ) ;
161+ } else {
162+ changeDataKeys ( {
163+ action : "remove" ,
164+ key : value . key ,
165+ } ) ;
166+ }
167+ } }
168+ key = { value . key }
169+ >
170+ { value . name }
171+ </ Checkbox >
172+ ) ) }
173+ </ SimpleGrid >
174+ </ VStack >
175+ ) ;
176+ } ) }
129177 </ Expandable >
130- < hr />
178+ < hr />
131179 </ VStack >
132180 ) ) }
133181 < VStack align = "stretch" pb = { 5 } >
0 commit comments