Je crée une simple calculatrice dans React (Gatsby) et je voulais donner une couleur d'arrière-plan à n'importe quel bouton d'opération sur lequel je clique tout en supprimant tous les styles des autres boutons précédemment cliqués.

Je comprends que je pourrais attribuer un état à chaque bouton, puis écrire un conditionnel pour supprimer leur style, mais cela semble compliqué. J'aimerais savoir s'il existe un moyen plus organique et plus évolutif d'y parvenir.

MErci d'avance pour votre coopération.

import React, {useState} from "react";

export default () => {
  const [ input1, setInput1 ] = useState();
  const [ input2, setInput2 ] = useState();
  const [ operation, setOperation ] = useState("add");
  const [ bg, setBg] = useState("");

  const handleInput1 = (event) => {
    const newInput = event.target.value;

    setInput1(prevResult => newInput);
  };

  const handleInput2 = (event) => {
    const newInput = event.target.value;

    setInput2(prevResult => newInput);
  };

  const operationSwitch = () => {
    switch (operation) {
      case "add":
        return parseInt(input1) + parseInt(input2);
        break;
      case "subtract":
        return parseInt(input1) - parseInt(input2);
        break;
      case "multiply":
        return parseInt(input1) * parseInt(input2);
        break;
      case "divide":
        return parseInt(input1) / parseInt(input2);
        break;
    }
  };

  const handleBackground = (event) => {
    event.currentTarget.style.backgroundColor === "red" ? event.currentTarget.style.backgroundColor = "" : event.currentTarget.style.backgroundColor = "red";
  };

  const buttonOperation = {
    add: (event) => {
      event.preventDefault();
      setOperation(newOperation => newOperation = "add");
      handleBackground(event);
    },
    subtract: (event) => {
      event.preventDefault();
      setOperation(newOperation => newOperation = "subtract");
      handleBackground(event);
    },
    multiply: (event) => {
      event.preventDefault();
      setOperation(newOperation => newOperation = "multiply");
      handleBackground(event);
    },
    divide: (event) => {
      event.preventDefault();
      setOperation(newOperation => newOperation = "divide");
      handleBackground(event);
    }
  };

  return (
    <div id="main-grid">
      <main>
        <div id="main-content">
          <input id="input1" type="number" name="input1" value={input1} onChange={handleInput1} />
          <div id="opBtns">
            <button id="add" onClick={buttonOperation.add}>+</button>
            <button id="subtract" onClick={buttonOperation.subtract}>-</button>
            <button id="multiply" onClick={buttonOperation.multiply}>*</button>
            <button id="divide" onClick={buttonOperation.divide}>/</button>
          </div>
          <input id="input2" type="number" name="input2" value={input2} onChange={handleInput2} />
          <h1>=</h1>
          <input id="result" type="number" name="result" value={operationSwitch()}  />
        </div>
      </main>
      <footer>
        Footer
      </footer>
    </div>
  );  
};
/* CSS Reset */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

/* Global CSS */
* {
  box-sizing: border-box;
}

body {
  background: #114357;  /* fallback for old browsers */
  background: -webkit-radial-gradient(circle, #98c064, #576829);  /* Chrome 10-25, Safari 5.1-6 */
  background: radial-gradient(circle, #98c064, #576829); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
  font-family: 'Roboto Mono', monospace;
}

#main-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-template-rows: repeat(9, 10vh) 10vh;
  grid-template-areas: 
    "main main main main main main main main main main main main"
    "main main main main main main main main main main main main"
    "main main main main main main main main main main main main"
    "main main main main main main main main main main main main"
    "main main main main main main main main main main main main"
    "main main main main main main main main main main main main"
    "main main main main main main main main main main main main"
    "main main main main main main main main main main main main"
    "main main main main main main main main main main main main"
    "footer footer footer footer footer footer footer footer footer footer footer footer";
}

main {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(12, minmax(1fr, 2fr));
  grid-template-rows: repeat(12, 1fr);
  grid-template-areas:
    ". . . . . . . . . . . ."
    ". . . . . . . . . . . ."
    ". . . . . . . . . . . ."
    ". . . . . . . . . . . ."
    ". . . . . main-content main-content . . . . ."
    ". . . . . main-content main-content . . . . ."
    ". . . . . main-content main-content . . . . ."
    ". . . . . main-content main-content . . . . ."
    ". . . . . . . . . . . ."
    ". . . . . . . . . . . ."
    ". . . . . . . . . . . ."
    ". . . . . . . . . . . .";
}

#main-content {
  /* background-color: royalblue; */
  grid-area: main-content;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
}

#input1 {
  grid-area: input1;
  display: block;
  margin: 0 auto;
  border: none;
  border-radius: 5px;
  padding: 5px;
  outline: none;
}

#opBtns {
  grid-area: opBtns;
}

#add {
  grid-area: add;
  width: 25%;
  height: 100%;
}

#subtract {
  grid-area: subtract;
  width: 25%;
  height: 100%;
}

#multiply {
  grid-area: multiply;
  width: 25%;
  height: 100%;
}

#divide {
  grid-area: divide;
  width: 25%;
  height: 100%;
}

#input2 {
  grid-area: input2;
  display: block;
  margin: 0 auto;
  border: none;
  border-radius: 5px;
  padding: 5px;
  outline: none;
}

h1 {
  display: block;
  font-size: 2rem;
  margin: 0 auto;
}

#result {
  grid-area: result;
  display: block;
  margin: 0 auto;
  border: none;
  border-radius: 5px;
  padding: 5px;
  outline: none;
}

footer {
  background-color: rgb(128, 19, 0);
  grid-area: footer;
}

Je n'ai pas pu configurer une application Gatsby dans un extrait de code, je viens donc de fournir le JavaScript et le CSS. Si vous avez besoin, voici le référentiel Github et le site déployé en production.

1
Martineli 16 avril 2020 à 19:11

2 réponses

Meilleure réponse

Ce que je ferais, c'est ajouter un attribut conditionnel qui n'apparaît que sur le bouton sélectionné.

<div id="opBtns">
    <button id="add" onClick={buttonOperation.add} data-selected={operation === "add"}>+</button>
    <button id="subtract" onClick={buttonOperation.subtract} data-selected={operation === "subtract"}>-</button>
    <button id="multiply" onClick={buttonOperation.multiply} data-selected={operation === "multiply"}>*</button>
    <button id="divide" onClick={buttonOperation.divide} data-selected={operation === "divide"}>/</button>
</div>

Ensuite, vous pouvez cibler ce bouton avec css

button[data-selected] {
    color: red;
}
2
kaleidawave 16 avril 2020 à 16:32

Qu'en est-il de la création d'une classe dynamique avec le hook useState (comme vous le faites avec l'arrière-plan)?

  const [ classNames, setClassNames ] = useState('');

Ensuite, dans votre switch, vous pouvez personnaliser ce que vous voulez, par exemple:

  const buttonOperation = {
    add: (event) => {
      event.preventDefault();
      setOperation(newOperation => newOperation = "add");
      handleBackground(event);
      setClassNames('add');
    },
    subtract: (event) => {
      event.preventDefault();
      setOperation(newOperation => newOperation = "subtract");
      handleBackground(event);
      setClassNames('subtract');
    },
    multiply: (event) => {
      event.preventDefault();
      setOperation(newOperation => newOperation = "multiply");
      handleBackground(event);
      setClassNames('multiply');

    },
    divide: (event) => {
      event.preventDefault();
      setOperation(newOperation => newOperation = "divide");
      handleBackground(event);
      setClassNames('divide');
    }
  };

Enfin dans votre return():

  <div id="opBtns" className={classNames}>

Dans votre CSS / Sass, vous pouvez personnaliser l'arrière-plan ou la propriété de votre choix.

1
Ferran Buireu 16 avril 2020 à 17:04