Gatby + Tailwind.css:少し複雑なことをする

少し複雑なことをしたいと思います。

肝硬変の分類:Child-Pugh 分類

肝硬変の重症度を分類する Child-Pugh 分類と言うものがあります。

複雑な計算をするわけではないのですが、テーブルをクリックすると自動的に計算をさせようとするとかなり面倒なことをしなければなりません。

作ったのは以下のようなものです。

全部で 5 つの項目があって、該当する箇所をクリックするとその部分の背景色がオレンジに変わり、JavaScript がテーブルのオレンジ色の箇所を合計して重症度を自動的に計算してくれるというものです。

jQuery では比較的簡単に実装できていましたが、Tailwind.css ではかなり面倒です。

全コード

ほとんど chatGPT にお願いしました。

と言ってもなかなか正解に至らず、かなり時間がかかりました。
フリーの GPT 3.5 なので贅沢は言えないのですが。


import React, { useState } from "react";
import Layout from "../../components/layout"
import Image from "../../components/image"

const YourComponent = () => {
  // 各行の選択されたセルのインデックスを保持するステート
  const [selectedCellIndices, setSelectedCellIndices] = useState({});

  // セルがクリックされたときのハンドラー関数
  const handleClick = (rowIndex, cellIndex) => {
    // クリックされたセルの行番号
    const clickedRowIndex = parseInt(rowIndex);
    
    // クリックされたセルの列番号
    const clickedCellIndex = parseInt(cellIndex);
    
    // クリックされたセルが青でない場合、青く塗り替えて選択状態にする
    setSelectedCellIndices(prevState => {
      const updatedSelectedCellIndices = { ...prevState, [clickedRowIndex]: clickedCellIndex };
      return updatedSelectedCellIndices;
    });
  };

  // テーブル全体で青く塗られているセルの合計を計算
  let total = 0;
  Object.values(selectedCellIndices).forEach(cellIndex => {
    if (!isNaN(cellIndex)) {
      total += cellIndex + 1;
      if (total > 9){
        document.getElementById('child').innerText = 'C' ;
      }else if (total <7){
        document.getElementById('child').innerText = 'A' ;
      }else{
        document.getElementById('child').innerText = 'B' ;
      }
    }
  });

  return (

  <Layout>

      <h1 className="block text-2xl bg-slate-200 mt-3 p-3 border border-indigo-600">Child-Pugh スコアリング計算</h1>

      <div class="flex justify-center mt-5">

        <table class="table-auto text-center">

        <thead>
              <tr>
                  <th className="bg-cyan-800 text-white w-64 border border-white h-12"></th>
                  <th className="bg-cyan-800 text-white w-64 border border-white">1</th>
                  <th className="bg-cyan-800 text-white w-64 border border-white">2</th>
                  <th className="bg-cyan-800 text-white w-64 border border-white">3</th>
              </tr>
          </thead>

          <tbody>
              <tr>
                  <th className="bg-cyan-800 text-white border border-white h-12">アルブミン(g/dL)</th>
                  <td className="border border-slate-300" onClick={() => handleClick(0, 0)} style={{ backgroundColor: selectedCellIndices[0] === 0 ? "orange" : "white" }}>&gt; 3.5</td>
                  <td className="border border-slate-300" onClick={() => handleClick(0, 1)} style={{ backgroundColor: selectedCellIndices[0] === 1 ? "orange" : "white" }}>2.8 ~ 3.5</td>
                  <td className="border border-slate-300" onClick={() => handleClick(0, 2)} style={{ backgroundColor: selectedCellIndices[0] === 2 ? "orange" : "white" }}>&lt; 2.8</td>
              </tr>
              <tr>
                  <th className="bg-cyan-800 text-white border border-white h-12">ビリルビン(mg/dL)</th>
                  <td className="border border-slate-300" onClick={() => handleClick(1, 0)} style={{ backgroundColor: selectedCellIndices[1] === 0 ? "orange" : "white" }}>&lt; 2.0</td>
                  <td className="border border-slate-300" onClick={() => handleClick(1, 1)} style={{ backgroundColor: selectedCellIndices[1] === 1 ? "orange" : "white" }}>2.0 ~ 3.0</td>
                  <td className="border border-slate-300" onClick={() => handleClick(1, 2)} style={{ backgroundColor: selectedCellIndices[1] === 2 ? "orange" : "white" }}>&gt; 3.0</td>
              </tr>
              <tr>
                  <th className="bg-cyan-800 text-white border border-white h-12">腹水</th>
                  <td className="border border-slate-300" onClick={() => handleClick(2, 0)} style={{ backgroundColor: selectedCellIndices[2] === 0 ? "orange" : "white" }}>なし</td>
                  <td className="border border-slate-300" onClick={() => handleClick(2, 1)} style={{ backgroundColor: selectedCellIndices[2] === 1 ? "orange" : "white" }}>軽度・コントロール可能</td>
                  <td className="border border-slate-300" onClick={() => handleClick(2, 2)} style={{ backgroundColor: selectedCellIndices[2] === 2 ? "orange" : "white" }}>中等度以上・コントロール困難</td>
              </tr>
              <tr>
                  <th className="bg-cyan-800 text-white border border-white h-12">肝性脳症(度)</th>
                  <td className="border border-slate-300" onClick={() => handleClick(3, 0)} style={{ backgroundColor: selectedCellIndices[3] === 0 ? "orange" : "white" }}>なし</td>
                  <td className="border border-slate-300" onClick={() => handleClick(3, 1)} style={{ backgroundColor: selectedCellIndices[3] === 1 ? "orange" : "white" }}>Ⅰ・Ⅱ</td>
                  <td className="border border-slate-300" onClick={() => handleClick(3, 2)} style={{ backgroundColor: selectedCellIndices[3] === 2 ? "orange" : "white" }}>&gt; Ⅲ</td>
              </tr>
              <tr>
                  <th className="bg-cyan-800 text-white border border-white h-12">プロトロンビン時間(%)</th>
                  <td className="border border-slate-300" onClick={() => handleClick(4, 0)} style={{ backgroundColor: selectedCellIndices[4] === 0 ? "orange" : "white" }}>&gt; 70</td>
                  <td className="border border-slate-300" onClick={() => handleClick(4, 1)} style={{ backgroundColor: selectedCellIndices[4] === 1 ? "orange" : "white" }}>40 ~ 70</td>
                  <td className="border border-slate-300" onClick={() => handleClick(4, 2)} style={{ backgroundColor: selectedCellIndices[4] === 2 ? "orange" : "white" }}>&lt; 40</td>
              </tr>

          </tbody>
        </table>

      </div>   

      <div className="grid grid-cols-2 gap-4 w-96 mx-auto mt-6 text-lg text-center">
        <div className="bg-yellow-500 p-4">Child-Pugh</div>
        <div className="bg-gray-200 p-4 border border-stone-300"><div><span id="child"></span> ({total}点)</div></div>
      </div>

  </Layout>
  );
};

export default YourComponent;

class はコンポーネントとして作らない方がいいらしいので、こんな長いコードになりましたが、慣れないので効率の悪い部分があるかもしれません。