Переглянути джерело

Refactor GameCreationForm and Dropdown to have a bespoke CountryDropdown

Kirk Trombley 4 роки тому
батько
коміт
8c1e0d7ea5

+ 36 - 9
client/src/components/util/GameCreationForm/Dropdown.jsx

@@ -1,5 +1,6 @@
 import React, { useRef, useState, useCallback, useEffect } from 'react';
 import { CSSTransition } from 'react-transition-group';
+import flagLookup from '../../../domain/flagLookup';
 import styles from './Dropdown.module.css';
 
 export const Item = ({ value, display, onSelect, children }) => (
@@ -8,7 +9,7 @@ export const Item = ({ value, display, onSelect, children }) => (
   </div>
 );
 
-export const Dropdown = ({ selected, open, onSelect, onClick, forceDisplay, children }) => {
+export const Dropdown = ({ selected, open, onSelect, onClick, children }) => {
   const transitionRef = useRef(null);
   const [ displayed, setDisplayed ] = useState(null);
   const onSelectCallback = useCallback((value, display) => { 
@@ -17,34 +18,60 @@ export const Dropdown = ({ selected, open, onSelect, onClick, forceDisplay, chil
   }, [onSelect]);
 
   useEffect(() => {
-    if (forceDisplay) {
-      return;
-    }
-
     if (selected === undefined) {
       return;
     }
 
     let found = null;
-    children.forEach(element => {
+    React.Children.toArray(children).forEach(element => {
       if (React.isValidElement(element) && (found === null) && element.props.value === selected) {
         const { value, display } = element.props;
         found = display ?? value;
       }
     });
     setDisplayed(found);
-  }, [children, selected, forceDisplay]);
+  }, [children, selected]);
+  return (
+    <div className={styles.container}>
+      <div className={styles.button} onClick={onClick}>{displayed}</div>
+      <CSSTransition nodeRef={transitionRef} in={open} timeout={200} mountOnEnter unmountOnExit classNames={{
+        enter: styles['list-enter'],
+        enterActive: styles['list-enter-active'],
+        exit: styles['list-exit'],
+        exitActive: styles['list-exit-active'],
+      }}>
+        <div className={styles.list} ref={transitionRef}>
+          {React.Children.toArray(children).map((child, key) => React.cloneElement(child, { onSelect: onSelectCallback, key }))}
+        </div>
+      </CSSTransition>
+    </div>
+  )
+};
+
+export const CountryDropdown = ({ countries, selected, onSelect, onClick, open }) => {
+  const transitionRef = useRef(null);
+
   return (
     <div className={styles.container}>
-      <div className={styles.button} onClick={onClick}>{forceDisplay ?? displayed}</div>
+      <div className={styles.button} onClick={onClick}>{selected ? flagLookup(selected) : '🌎'}</div>
       <CSSTransition nodeRef={transitionRef} in={open} timeout={200} mountOnEnter unmountOnExit classNames={{
         enter: styles['list-enter'],
         enterActive: styles['list-enter-active'],
         exit: styles['list-exit'],
         exitActive: styles['list-exit-active'],
       }}>
+        {/* TODO redesign this */}
         <div className={styles.list} ref={transitionRef}>
-          {children.map((child, key) => React.cloneElement(child, { onSelect: onSelectCallback, key }))}
+          <div className={styles.item} onClick={() => onSelect(null)}>
+            All Countries
+          </div>
+          { 
+            countries.map(({ country, alpha2 }) => (
+              <div key={alpha2} className={styles.item} onClick={() => onSelect(alpha2)}>
+                { country }
+              </div>
+            )) 
+          }
         </div>
       </CSSTransition>
     </div>

+ 26 - 21
client/src/components/util/GameCreationForm/GameCreationForm.jsx

@@ -1,16 +1,18 @@
 import ms from 'pretty-ms';
+import iso from 'iso-3166-1';
 import { useCallback, useState } from 'react';
 import { createGame } from '../../../domain/apiMethods';
 import { RANDOM_STREET_VIEW, URBAN } from '../../../domain/genMethods';
 import { FROZEN, NORMAL, TIME_BANK, RACE, COUNTRY_RACE } from '../../../domain/ruleSets';
+import useAvailableGenerators from '../../../hooks/useAvailableGenerators';
 import Loading from '../Loading';
-import { Dropdown, DropdownGroup, Item } from './Dropdown';
+import { Dropdown, DropdownGroup, Item, CountryDropdown } from './Dropdown';
 import styles from './GameCreationForm.module.css';
 
 const DEFAULTS = {
   timer: 300,
   rounds: 5,
-  onlyAmerica: false,
+  countryLock: null,
   genMethod: RANDOM_STREET_VIEW,
   ruleSet: NORMAL,
 }
@@ -19,7 +21,7 @@ const PRESETS = {
   URBAN_AMERICA: {
     ...DEFAULTS,
     genMethod: URBAN,
-    onlyAmerica: true,
+    countryLock: 'us',
   },
   URBAN_GLOBAL: {
     ...DEFAULTS,
@@ -35,33 +37,40 @@ const PRESETS = {
 }
 
 const GameCreationForm = ({ afterCreate }) => {
+  const generators = useAvailableGenerators();
+
   const [ loading, setLoading ] = useState(false);
   const [ creationError, setCreationError ] = useState(false);
   const [ timer, setTimer ] = useState(DEFAULTS.timer);
   const [ rounds, setRounds ] = useState(DEFAULTS.rounds);
-  const [ onlyAmerica, setOnlyAmerica ] = useState(DEFAULTS.onlyAmerica);
+  const [ countryLock, setCountryLock ] = useState(DEFAULTS.countryLock);
   const [ genMethod, setGenMethod ] = useState(DEFAULTS.genMethod);
   const [ ruleSet, setRuleSet ] = useState(DEFAULTS.ruleSet);
 
   const setPreset = useCallback(({
-    timer, rounds, onlyAmerica, genMethod, ruleSet,
+    timer, rounds, countryLock, genMethod, ruleSet,
   }) => {
     setTimer(timer);
     setRounds(rounds);
-    setOnlyAmerica(onlyAmerica);
+    setCountryLock(countryLock);
     setGenMethod(genMethod);
     setRuleSet(ruleSet);
   }, []);
 
-  if (loading) {
+  if (loading || generators === null) {
     return <Loading />;
   }
 
+  const countries = generators[genMethod]
+    .map(c => iso.whereAlpha2(c))
+    .filter(c => c !== null && c !== undefined);
+  countries.sort((a, b) => a.country < b.country ? -1 : a.country > b.country ? 1 : 0);
+
   const onCreateGame = async () => {
     setLoading(true);
     let gameId;
     try {
-      gameId = await createGame(timer, rounds, onlyAmerica ? "us" : null, genMethod, ruleSet);
+      gameId = await createGame(timer, rounds, countryLock, genMethod, ruleSet);
     } catch (e) {
       setCreationError(true);
       setLoading(false);
@@ -75,6 +84,9 @@ const GameCreationForm = ({ afterCreate }) => {
   return (
     <div className={styles.form}>
       { creationError && <div className={styles.error}>Error! TODO</div> }
+      <button className={styles.start} onClick={onCreateGame}>
+        New Game
+      </button>
       <div className={styles.dropdowns}>
         <DropdownGroup>
           <Dropdown selected={timer} onSelect={setTimer} open='timer'>
@@ -89,15 +101,11 @@ const GameCreationForm = ({ afterCreate }) => {
             <Item value={5}>5 Rounds</Item>
             <Item value={10}>10 Rounds</Item>
           </Dropdown>
-          {/* TODO base this dynamically on available generators */}
-          <Dropdown selected={onlyAmerica} onSelect={setOnlyAmerica} open='america'>
-            <Item value={false} display='🌎'>All Countries</Item>
-            <Item value={true} display='🇺🇸'>Just America</Item>
-          </Dropdown>
           <Dropdown selected={genMethod} onSelect={setGenMethod} open='gen'>
             <Item value={RANDOM_STREET_VIEW} display='🎲'>Random Street View</Item>
             <Item value={URBAN} display='🏙️'>Urban Centers</Item>
           </Dropdown>
+          <CountryDropdown countries={countries} selected={countryLock} onSelect={setCountryLock} open='country'/>
           <Dropdown selected={ruleSet} onSelect={setRuleSet} open='rule'>
             <Item value={NORMAL} display='⏰'>Normal</Item>
             <Item value={TIME_BANK} display='🏦'>Time Bank</Item>
@@ -105,17 +113,14 @@ const GameCreationForm = ({ afterCreate }) => {
             <Item value={RACE} display='🏃'>Race</Item>
             <Item value={COUNTRY_RACE} display='🗾'>Country Race</Item>
           </Dropdown>
-          <Dropdown onSelect={setPreset} open='presets' forceDisplay='⭐'>
-            <Item value={DEFAULTS} display=''>Default</Item>
-            <Item value={PRESETS.URBAN_AMERICA} display=''>Urban America</Item>
-            <Item value={PRESETS.URBAN_GLOBAL} display=''>Urban Global</Item>
-            <Item value={PRESETS.FAST_FROZEN} display=''>Fast Frozen</Item>
+          <Dropdown selected={DEFAULTS} onSelect={setPreset} open='presets'>
+            <Item value={DEFAULTS} display=''>Default</Item>
+            <Item value={PRESETS.URBAN_AMERICA} display=''>Urban America</Item>
+            <Item value={PRESETS.URBAN_GLOBAL} display=''>Urban Global</Item>
+            <Item value={PRESETS.FAST_FROZEN} display=''>Fast Frozen</Item>
           </Dropdown>
         </DropdownGroup>
       </div>
-      <button className={styles.start} onClick={onCreateGame}>
-        New Game
-      </button>
     </div>
   );
 };

+ 1 - 1
client/src/components/util/GameCreationForm/GameCreationForm.module.css

@@ -1,6 +1,6 @@
 .form {
   display: flex;
-  flex-flow: column-reverse nowrap;
+  flex-flow: column nowrap;
   justify-content: space-between;
   align-self: center;
 }