Merhabalar.

Bu yazımda, dışarıdan bir icon sınıfı nasıl kullanılır ve delete işlemi nasıl gerçekleştirilir bunlara bakacağız.

Calculator uygulamamızda işlemlerin listesini tuttuğumuz bir History bölümümüz vardı. Bu History bölümünde her bir elemanın sağ tarafına bir delete iconu koyacağız ve bu silme icon’larının onClick event’ine silme operasyonlarımızı bağlayacağız.

Bu uygulamayla beraber UI’da biraz değişiklik yapıyoruz. O yüzden size aşağıda verdiğim dosyaların kodlarını öncelikle güncelleyiniz.

App.js

import './App.css';
import ProductCard from './Components/Product/ProductCard';
import HelloWorld from './Components/HelloWorld/HelloWorld';
import Welcome from './Components/Welcome/Welcome';
import Calculator from './Components/Calculator/Calculator';

function App() {

  return (

    <div className="Outer">

      <div className="Appp">

        { /* <HelloWorld /> <ProductCard /> */}

        <Calculator />

      </div>

    </div>


  );
}

export default App;

App.css

.App {
  text-align: center;
  padding: 40px;
  background: rgba(34, 86, 165, 0.25);
  transition: background-color 0.5s, transform 0.5s;
  backdrop-filter: blur(2.5px);
  -webkit-backdrop-filter: blur(var(2.5px));
  border: 3px solid;
  border-image-source: linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet);
  border-image-slice: 3;
  animation: borderAnimation 3s linear infinite;
  box-shadow: 0 28px 32px 0 rgba(15, 17, 46, 0.3);

}

@keyframes borderAnimation {
  0% {
    border-image-source: linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet);
  }

  100% {
    border-image-source: linear-gradient(45deg, violet, indigo, blue, green, yellow, orange, red);
  }
}

body {
  
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #f5f5f5;
  font-family: Arial, sans-serif;
  margin: 0;
  background: #ffffff;
  background-image: url('https://wallpaper.dog/large/20459078.jpg');
  background-size: cover;

}

.Outer {
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50px;
  background-color: #ffffff;
  width: 190vh;
  height: 95vh;
  background: rgba(29, 95, 139, 0.288);
  box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.30);
  backdrop-filter: blur(1px);
  -webkit-backdrop-filter: blur(1px);

}

Calculator.css

.historyContainer{
    display: flex;
    flex-direction: column;
    justify-content: start;
    align-items: center;
    border: 1px solid #ddd;
    box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
    border-radius: 20px;
    margin-bottom: 10px;
    max-height: 300px;
    overflow-y: auto;
}

.historyInfo {
    display: none;
}

.historyInfo.open {
    display: block;
}



.calculator{

    display: flex;
    flex-direction: column;

    border: 1px solid #ddd;
    border-radius: 20px;
    margin-top: 10px;
    padding: 10px;

}
form {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 20px;
    border: 1px solid #ee2727;
    border-radius: 10px;
    width: 300px;
    height: 210px;
    background: rgba(29, 73, 139, 0.25);
    transition: background-color 1s, transform 1s;
    backdrop-filter: blur(1px);
    -webkit-backdrop-filter: blur(1px);
    border: 1px solid rgba(255, 255, 255, 0.18);
}

input, select, button {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 15px;
    font-size: 18px;
    text-align: center;
}

input:focus, select:focus {
    
    border: 1px solid #007bff;
}

button {
    background-color: #007bff;
    color: #fff;
    cursor: pointer;
    background: rgba(3, 31, 71, 0.25);
    transition: background-color 0.5s, transform 0.3s;
    backdrop-filter: blur(2.5px);
    -webkit-backdrop-filter: blur(2.5px);
    border: 1px solid rgba(233, 228, 228, 0.3);
}

button:hover {
    background-color: #0056b3;
}

.result {
    position: relative;
    text-align: center;
    border-radius: 20px;
    height: 75px;
    color: #fff;
    margin-bottom: 20px;
    background: rgb(2,0,36);
background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(69,79,210,1) 100%);
    transition: background-color 1s, transform 1s;
    backdrop-filter: blur(2.5px);
    -webkit-backdrop-filter: blur(2.5px);
}

.result h1 {
    font-size: 35px;
}

h4{
    color: white;
}

.result span {
    position: absolute;
    right: 20px;
    bottom: 10px;
    font-size: 13px;
    font-weight: bold;
    color: rgb(185, 221, 26);
}

.operation-info {
    display: none;
}

.operation-info.open {
    display: block;
}

.operation-info span {
    
    padding: 10px;
    margin-bottom: 10px;
    color: white;
    margin:auto;
    

}

.operation-info p:hover {
    background: rgb(22,33,208);
    background: linear-gradient(90deg, rgba(22,33,208,1) 0%, rgba(59,9,32,1) 100%);
}

.info-container {
    margin-top: 20px;;
    width: 94%;
    margin:10px;
    
}

.toggle-button {
    width: 60px;
    height: 20px;
    position: relative;
    background: #e6250b;
    border-radius: 15px;
    cursor: pointer;
    transition: background 5s ease-in;
    margin-bottom: 20px;
}

.toggle-button::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 15px;
    height: 15px;
    background: rgb(255, 255, 255);
    border-radius: 50%;
    transition: left 0.3s ease;
    margin-bottom: 20px;
}

.toggle-button.active {
    background: rgb(64, 89, 202);
}

.toggle-button.active::after {
    left: 38px;
}

.mr{
    margin-top: -20px;
    margin-right: 10px;
    color: white;
}

.toggle-container{
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: 10px;
    position: sticky;
    top: 20px;

}

.header {
    margin: 20px;
    height: 50px;
    width: 98%;
    position: fixed;
    top: 0;
    left: 0;
    background: rgb(22,33,208);
background: radial-gradient(circle, rgba(22,33,208,1) 0%, rgba(177,131,44,1) 100%);
    color: white;
    border-radius: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
    transition: all 2s ease;
}

.history-item {
    margin-bottom: 15px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border: 1px solid #ddd;
    padding-left: 10px;
    padding-right: 15px;
    cursor:pointer;
    transition: background-color 0.3s ease, color 0.3s ease;
    border-radius: 20px;
    font-weight: bold;
}

.history-item:hover {
    background: rgb(22,33,208);
background: linear-gradient(90deg, rgba(22,33,208,1) 0%, rgba(59,9,32,1) 100%);
}

.history-item:hover .fa-trash {
    color: white;
}

.history-item .fa-trash {
    color: red;
    cursor: pointer;
    font-size: 20px;
}

Artık yavaştan Calculator.jsx dosyamızda güncellemelere başlayabiliriz.

Font Awesome Kullanımı

Font Awesome’ın icon setini kullanabilmek için, öncelikle terminali açıp bir install işlemi gerçekleştirmemiz gerekiyor.

Aşağıdaki komutu alıp çalıştıralım.

npm install --save @fortawesome/react-fontawesome @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons

Sonra kullanacağımız yerde import işlemlerini gerçekleştiriyoruz.

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrash } from '@fortawesome/free-solid-svg-icons';

JSX Kodları Güncelle – Delete Operasyonu İçin Trash Icon’unu Her Bir Liste Elemanının Sağına Yerleştir

Calculator.jsx’in güncel kodları aşağıdaki gibi olmalı.

import "./Calculator.css";
import React, { useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrash } from '@fortawesome/free-solid-svg-icons';

const Calculator = () => {

    // #region States

    const [inputState, setInputState] = useState({

        num1: 0,
        num2: 0,
        operation: '',
    });

    const [allState, setAllState] = useState({
        result: 0,
        count: 0,
        operationInfoOpen: false,
        alertMessage: '',
    });

    const [history, setHistory] = useState([]);

    // #endregion 

    // #region Functions

    const updateAlertMessage = (message) => {
        setAllState(prevState => ({
            ...prevState,
            alertMessage: message,
        }));
    }

    const updateResult = (result) => {

        setAllState(prevState => ({
            ...prevState,
            result: result,
            count: prevState.count + 1,
        }));

    }

    const updateOperationInfoOpen = (isOpen) => {

        setAllState(prevState => ({
            ...prevState,
            operationInfoOpen: isOpen,

        }));
    }

    const updateHistory = (operation, num1, num2, result) => {
        setHistory(prevList => [{ count: prevList.length + 1, operation, num1, num2, result }, ...prevList]);
    }

    const updateInputState = (event) => {

        const { name, value } = event.target;

        setInputState(prevState => ({
            ...prevState,
            [name]: value,
        }));
    }

    const initialInputState = () => {

        setInputState(prevState => ({

            num1: 0,
            num2: 0,
            operation: '',
        }));
    }

    const handleToggleOperationInfo = () => {

        if (history.length !== 0) {

            updateOperationInfoOpen(!allState.operationInfoOpen);
        }
        else {

            updateAlertMessage('There is no operation history');
            setTimeout(() => updateAlertMessage(''), 5000);
        }
    };

    const handleCalculate = (event) => {

        event.preventDefault();

        const form = event.target;
        const num1 = parseFloat(form.elements.num1.value);
        const num2 = parseFloat(form.elements.num2.value);
        const operation = form.elements.operation.value;

        let result;

        switch (operation) {
            case 'add':
                result = num1 + num2;
                updateHistory('+', num1, num2, result);
                break;
            case 'subtract':
                result = num1 - num2;
                updateHistory('-', num1, num2, result);
                break;
            case 'multiply':
                result = num1 * num2;
                updateHistory('*', num1, num2, result);
                break;
            case 'divide':
                if (num2 !== 0) {
                    result = num1 / num2;
                    updateHistory('/', num1, num2, result);
                } else {
                    updateAlertMessage('Cannot divide by zero');
                    setTimeout(() => updateAlertMessage(''), 5000);

                }
                break;
            default:
                updateAlertMessage('Invalid operation');
                setTimeout(() => updateAlertMessage(''), 5000);
        }

        updateResult(result);
        initialInputState();
    };

    const handleDeleteHistory = (index) => {
        setHistory(prevHistory => prevHistory.filter((item, i) => i !== index));

        if (history.length === 1) {
            updateOperationInfoOpen(false);
        }
    };

    // #endregion

    // #region Render

    return (

        <div className="container">

            {allState.alertMessage ?

                <div className="header">
                    <div className="alert">{allState.alertMessage}</div>
                </div> : null

            }

            <div className={`historyContainer historyInfo ${allState.operationInfoOpen ? 'open' : ''}`}>

                <h4>History</h4>

                <div className={`operation-info info-container ${allState.operationInfoOpen ? 'open' : ''}`}>

                    {history.map((process, index) => (
                        <div key={index} className="history-item">
                            <span>{process.count}: {process.num1} {process.operation} {process.num2} = {process.result} </span>
                            <FontAwesomeIcon icon={faTrash} onClick={() => handleDeleteHistory(index)} title="Delete" />

                        </div>

                    ))}

                </div>

            </div>

            <div className="calculator">

                <div className="toggle-container">

                    <span className="mr">{allState.operationInfoOpen ? 'Close' : 'Open'} History</span>
                    <button className={`toggle-button ${allState.operationInfoOpen ? 'active' : ''}`} onClick={handleToggleOperationInfo}>

                    </button>

                </div>

                <div className="result">
                    <h1 id="result">{allState.result}</h1>
                    <span>İşlem Adeti: {allState.count}</span>
                </div>

                <form onSubmit={handleCalculate}>
                    <input type="number" name="num1" value={inputState.num1} onChange={updateInputState} required />
                    <input type="number" name="num2" value={inputState.num2} onChange={updateInputState} required />
                    <select name="operation" value={inputState.operation} onChange={updateInputState} required>
                        <option value="">Select operation</option>
                        <option value="add">Add</option>
                        <option value="subtract">Subtract</option>
                        <option value="multiply">Multiply</option>
                        <option value="divide">Divide</option>
                    </select>
                    <button type="submit">Calculate</button>
                </form>

            </div>

        </div>

    )

    // #endregion
}

export default Calculator;

Tüm bu işlemlerden sonra uygulamamız şu şekilde gözüküyor olmalı.

Her bir işlemin yanına delete icon’u koyduğumuz kodlarımız aşağıdaki gibi.

<div className={`operation-info info-container ${allState.operationInfoOpen ? 'open' : ''}`}>

                    {history.map((process, index) => (
                        <div key={index} className="history-item">
                            <span>{process.count}: {process.num1} {process.operation} {process.num2} = {process.result} </span>
                            <FontAwesomeIcon icon={faTrash} onClick={() => handleDeleteHistory(index)} title="Delete" />

                        </div>

                    ))}

                </div>

Her bir icon’un onClick event’ine handleDeleteHistory fonksiyonunu bağlıyoruz. Burada listenin değişkeninin uniq değeri olan index’i parametre olarak gönderiyoruz.

handleDeleteHistory Fonksiyonu

Ne yapıyor aşağıdaki fonksiyon bakalım.

History state’inde bir güncelleme yapacak. Güncelleme işlemine dahil edeceği değerler için şunu diyor, sana gönderdiğim index değerinin olmayanlarını filter ile bul, bulduklarını bu listenin elemanları olarak güncelle.

Böylece silmek için seçtiğimiz işlemin index değeriyle eşleşen değer liste içine dahil edilmemiş olacak. Böylece aslında listeden silinmiş olacak.

const handleDeleteHistory = (index) => {
        setHistory(prevHistory => prevHistory.filter((item, i) => i !== index));

        if (history.length === 1) {
            updateOperationInfoOpen(false);
        }
    };

Altında yaptığımız İşlem ise listenin son elemanı silinince History bölümünü kapatıp, toggleButton’u kapalı hale getirecek operationInfoOpen state’ini false ile güncelliyor.

Uygulama Testi

Sonuncuyu silmemle beraber History’i bölümü kapanıp, toggleButton close konumuna geldi

Alıştırma

Şimdi sizden şunu yapmanızı isteyeceğim. Gördüğünüz gibi işlemleri sildiğimde liste elemanları 1 tane kalmış olmasına rağmen 3. işlem bilgisi gibi gözüküyor. Her silme işleminden sonra liste elemanlarını yeniden sıralatalım istiyorum.

Bir sonraki yazımda görüşmek üzere.


Murat Bilginer
21 Şubat 1992'de doğdum. Endüstri Mühendisi olarak lisansımı 2016 yılında tamamladım. Industryolog Akademi - NGenius oluşumlarının kurucusuyum. Şu anda kendi şirketim Brainy Tech ile Web ve Mobil Geliştirme, AWS, Google Cloud Platform Sistemleri için DevOps, Big Data Analiz ve Görselleştirme hizmetleri sunmakta ve Online Eğitimler vermekteyiz.