suraj
suraj

Reputation: 316

How to toggle div focus using up and down key in react component (typescript + css)

I have data being rendered in the various div and I would like to select/focus the div when user press up/down arrow.

For selecting the div using up/down key I see using ref and ForewardRef for child component we can toggle the focus but still struggling to figure it out how to achieve this in typescript code.

I am new to this react so any suggestion or improvement on the below code is welcome.

Below is my code.

Option.ts

export interface option {
id:number;
label:string;
value:string;}

App.tsx

 import React, {useRef, useEffect} from 'react';
 import { option } from './option';
 import Options from './options';

 export default function App() {
  const options : option[] = [
    { id:0, label : "Option 1", value:"12000"},
    { id:1, label : "Option 2", value:"10000" },
    { id:2, label : "Option 3", value:"11000"},
    { id:3, label : "Option 4", value:"23000" }
   ];


   const [invTheme, setInvTheme] = React.useState("10000");
   const firstRef = React.createRef<HTMLDivElement>();
   const checkedRef = React.createRef<HTMLDivElement>();

  useEffect(() => {
      if (checkedRef.current) {
          checkedRef.current.focus();
      } else if (firstRef.current) {
      firstRef.current.focus();
   }
  }, []);

   return (
     <div className="App">
      {
      options.map((option, i) => {
        const {id, label, value} = option;
        const checked = invTheme === value;

        return (
          <div key={i}>
            <Options id={id} label={label} value={value} checked={checked}
             onChange={() =>{
              setInvTheme(value);
            }}/>
            <br></br>
          </div>
        )
      })
   }
</div>
  );
 }

Options.tsx

import React, {useEffect} from 'react'
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';

 const useStyles = makeStyles((theme: Theme) =>
  createStyles({
        root: {
         flexGrow: 1,
         },
         div: {
  padding: theme.spacing(2),
  margin: 'auto',
  maxWidth: 700,
  border:`1px solid Gray`
},
selectedDiv: {
  padding: theme.spacing(2),
  margin: 'auto',
  maxWidth: 700,
  border: `2px solid Blue`
}
}),);

 export default function Options(props:any) {
  const classes = useStyles();
  //const [selectedDiv, setSelectedDiv] = React.useState(false);

  //useEffect(()=>{
       //if(checked) setSelectedDiv(true);
     //},[])

 const handleOnClick = (e:any) => {
    if (e.type === "click" && e.clientX !== 0 && e.clientY !== 0) {
      //setSelectedDiv(true);
     
      onChange({target:value});
    }
  };

const { id, label, value, onChange, checked } = props;
console.log("id: "+ id+" checked "+ checked);

return (
  <div>
    <div className={checked ? classes.selectedDiv : classes.div}  onClick={handleOnClick}>
    <Grid container spacing={2}>
        <Grid item>
            <input id={id} type="radio" name="type" aria-label={label} checked={checked}  value={value} onChange={onChange}/>
        </Grid>
        <Grid item >
            {label}
        </Grid>
    </Grid>
  </div>
  </div>
);}

enter image description here

Upvotes: 1

Views: 709

Answers (1)

Joseph A
Joseph A

Reputation: 12

This is how I solved it.

I made use of the checked props to control the style property of the div.

find below the codes:

options.tsx:

import React, {useEffect} from 'react'
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';

 const useStyles = makeStyles((theme: Theme) =>
  createStyles({
        root: {
         flexGrow: 1,
         },
         div: {
  padding: theme.spacing(2),
  margin: 'auto',
  maxWidth: 700,
  border:`1px solid Gray`,
 
},
selectedDiv: {
  padding: theme.spacing(2),
  margin: 'auto',
  maxWidth: 700,
  border:`1px solid Gray`,
},
selected: {
  
}
}),);

 export default function Options(props:any) {
  const classes = useStyles();
  const [selectedDiv, setSelectedDiv] = React.useState(false);
  const { id, label, selected, value, onChange, checked } = props;
  useEffect(()=>{
       if(selected) setSelectedDiv(true);
     },[])

 const handleOnClick = (e:any) => {
    if (e.type === "click" && e.clientX !== 0 && e.clientY !== 0) {
      setSelectedDiv(true);
     
      onChange({target:value});
    }
  };



return (
  <div>
    <div className={selectedDiv  ?  classes.selectedDiv :classes.div}  onClick={handleOnClick}style = {checked && selectedDiv? {border: `2px solid Blue`,maxWidth: 700,}:{}} >
    <Grid container spacing={2} >
        <Grid item>
            <input id={id} type="radio" name="type" aria-label={label} checked={checked}  value={value} onChange={onChange}/>
        </Grid>
        <Grid item >
            {label}
        </Grid>
    </Grid>
  </div>
  </div>
);}

App.tsx:

import React, {useRef, useEffect} from 'react';
 import { option } from './option';
 import Options from './options' 


 

 export default function App() { 
  const options : option[] = [
    { id:0, label : "Option 1", selected : false, value:"12000"},
    { id:1, label : "Option 2", selected : true,  value:"10,000" },
    { id:2, label : "Option 3", selected : false, value:"10000"},
    { id:3, label : "Option 4", selected : false, value:"23000" }
   ];


   const [invTheme, setInvTheme] = React.useState("12000");
   const firstRef = React.createRef<HTMLDivElement>();
   const checkedRef = React.createRef<HTMLDivElement>(); 
 

  useEffect(() => {
      if (checkedRef.current) {
          checkedRef.current.focus();
      } else if (firstRef.current) {
      firstRef.current.focus();
   }
  }, []);


   return (
     <div className="App">
      {
      options.map((option, i) => {
        const {id, label, selected, value} = option;
        const checked = invTheme === value;
 
        return (
          <div key={i}  >
            <Options id={id} label={label} selected={selected} value={value} checked={checked}
             onChange={() =>{
              setInvTheme(value);
            }}   />
            <br></br>
          </div>
        )
      })
   }
</div>
  );
 }

Upvotes: 0

Related Questions