상세 컨텐츠

본문 제목

React(리액트) 티키타카 16 (DHTMLX Gantt 차트)

React

by e7e 2024. 12. 21. 16:10

본문

누군가는 써보고 싶은 DHTMLX Gantt 차트당.

https://dhtmlx.com/

가면 많은 Component를 제공 하련당.

 

안타깝땅. 대부분 Pro 버젼으로 유료에, 무료버젼도 있지만 닭똥 눈물나겡 설명이 구닥다리당.

https://dhtmlx.com/blog/create-react-gantt-chart-component-dhtmlxgantt/

위 사이트에 가면 무료버젼을 이용해 react를 활용하는 예제가 있지만,

역시 구닥따리에 날짜 순서등이 한국식이 아니당. 가슴 아프당.

 

문서도 방대하고, 제대로 쓰려면 아무래도 시간 투자가 엄칭 많이 필요할꼬시당.

그래도 간단하게 쓴다면, 위 예제 정도로 충분하지 않을깡? 맴이 버럭 든당.

그래서 위 사이트 소스를 요듬 방식으로 고쳐 맹글어 볼꺼이당.(시름 말공...)

목표는 그저 베이스로 사용할 소스를 요즘식으로 변환하는 거시당. 

 

 

먼저 vite를 이용하여 프로젝트를 생성하고 아래 모듈을 추가하련당. 

npm install dhtmlx-gantt

 

파일은 폴더 맹글고 그러면 또 정신 없어지닝, src 폴더에 아래 파일들을 맹글거당.

 

css 파일은 그냥 가져다 복사/ 붙여넣기 하장.

index.css

.gantt-container {
    height: calc(100vh - 50px - 200px);
}
.message-area {
    background: #ebebeb;
    height: 200px;
    overflow: auto;
    padding: 10px;
    box-sizing:border-box;
}
.message-area ul{
    margin: 0;
    padding: 0;
    list-style: none;
}
.message-area li:before {
    content: "\003e";
    padding-right: 10px;
}

 

간트차트를 위한 임시 데이터당. (대략 필요한 속성들 의미만 눈과 머리로 받아들여보장)

dummyData.js

const data = {
  data: [
      { id: 1, text: '작업 #1', start_date: '2024-12-25', duration: 3, progress: 0.6},
      { id: 2, text: '작업 #2', start_date: '2024-12-28', duration: 3, progress: 0.4},
      { id: 3, text: '작업 #1-1', start_date: '2024-12-25', duration: 1, progress: 1 ,parent:1},
      { id: 4, text: '작업 #1-2', start_date: '2024-12-26', duration: 2, progress: 0.5,parent:1 },
  ],
  links: [
      { id: 1, source: 1, target: 2, type: '0' },
      { id: 2, source: 3, target: 4, type: '0' },
  ]
};

export default data;

 

툴바에서 기간을 시간/일/월 단위로 보여주기 위한 세팅값이다.

낯설지만 DHTMLX Gantt 차트는 이런 식으로 만들란당. (복사/붙여넣기 해서 쓰장)

zoomLevelSetting.js

const initLevels = {
    levels: [
        {
            name: '시간단위',
            scale_height: 60,
            min_column_width: 30,
            scales: [
                { unit: 'day', step: 1, format: '%M %d일' },
                { unit: 'hour', step: 1, format: '%H' }
            ]
        },
        {
            name: '날짜단위',
            scale_height: 60,
            min_column_width: 70,
            scales: [
                { unit: 'week', step: 1, format: 'Week %W주' },
                { unit: 'day', step: 1, format: '%M %d일' }
            ]
        },
                  {
            name: '월단위',
            scale_height: 60,
            min_column_width: 70,
            scales: [
                { unit: "month", step: 1, format: '%F' },
                { unit: 'week', step: 1, format: '%W주' }
            ]
        }
    ]
}

export default initLevels

 

이건 그냥 복사 붙여넣기 하장, log

main.jsx

import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

const root = createRoot(document.getElementById('root'))
root.render(
      <App />
)

 

이건 복사 붙여넣기 하고, 한번 눈으로 보장.

logDataupdate 함수는 MyGantt에 넘어가서 불리는 것이 포인트당.

App.jsx

import { useState } from "react";
import MessageArea from "./MessageArea";
import MyGantt from "./MyGantt";
import Toolbar from "./Toolbar";
import data from "./dummyData";


function App() {
  const [zoom,setZoom] = useState("날짜단위")
  const [msgs,setMsgs] = useState([])

  const onZoomChange = (zoom)=>{
      setZoom(zoom)
  }

  const logMessage = (message) =>{
    msgs.length >= 5 ? setMsgs([message,...msgs.slice(0,4)]):setMsgs([message,...msgs]) 
  }

  const logDataupdate = (entityType, action, itemData, id) => {
    console.log("B/E 에 중요",entityType, action, itemData, id);
    
    let text = itemData && itemData.text ? ` (${itemData.text})`: '';
    let message = `${entityType} ${action}: ${id} ${text}`;
    if (entityType === 'link' && action !== 'delete' ) {
        message += ` ( source: ${itemData.source}, target: ${itemData.target} )`;
    }
    logMessage(message)
  }

  return (
    <>
      <Toolbar curzoom={zoom} onZoomChange = {onZoomChange} />
      <div className="gantt-container">
        <MyGantt tasks={data} curzoom={zoom} msgs={msgs} onDataUpdated={logDataupdate} />
      </div>
      <MessageArea msgs={msgs} />
    </>
  )
}

export default App

 

 

Gantt 차트에 변화가 생기는 걸 추적할 수 있도록 dataProcessor라는 걸 제공해 준당.

머리 안써도 된당. 매뉴얼대로 그저 세팅했을 뿐이당.(그냥 받아들이장~~) 

MyGantt.jsx

import { gantt } from "dhtmlx-gantt";
import "dhtmlx-gantt/codebase/dhtmlxgantt.css";
import { useEffect, useRef } from "react";
import initLevels from "./zoomLevelSetting";

function MyGantt({tasks,curzoom,onDataUpdated,msgs}){
    const ganttRef = useRef(null)

    let dataProcessor = null
    const initGanttDataProcessor = () => {
        dataProcessor = gantt.createDataProcessor((entityType, action, item, id) => {
            return new Promise((resolve, reject) => {
                if (onDataUpdated) {
                    onDataUpdated(entityType, action, item, id);
                }
                return resolve();
            });
        });
    }

    useEffect(()=>{
        gantt.i18n.setLocale("kr");
        gantt.config.date_format = "%Y-%m-%d %H:%i";
        gantt.config.scales = [
            {unit:"day",step:1, format:"%j,%D"}
        ]
        console.log("확인 lightbox",gantt.config.lightbox.sections);
        console.log("확인 다국어지원",gantt.locale.labels);

        gantt.config.lightbox.sections[1].time_format=["%Y","%m","%d"];  
        gantt.locale.labels.new_task="새 작업";
        gantt.config.task_date = "&nbsp;&nbsp;&nbsp;&nbsp;%Y년 %m월 %d일";


        gantt.ext.zoom.init(initLevels);
        console.log("확인2",gantt.config.lightbox)
        //initGanttDataProcessor(); 요기 있으면 message처리가 이상해짐??
        gantt.init(ganttRef.current);
        gantt.parse(tasks);

    },[])

    useEffect(()=>{
        gantt.ext.zoom.setLevel(curzoom);
        initGanttDataProcessor();

        return () =>{
            if(dataProcessor){
                dataProcessor.destructor();
                dataProcessor = null;
            }
        }
        // gantt.render();
    },[curzoom,msgs])

    return (
        <div
            ref={ganttRef}
            style={{width:"100%",height:"100%"}}     
        ></div>
    )
}

export default MyGantt

 

줌(Zoom) 설정을 위한 체크박스들을 담은 컴포넌트당. 

Toolbar.jsx

import React, { useContext } from 'react';

function Toolbar({curzoom, onZoomChange}) {

    const handleZoomChange = (e) =>{
        onZoomChange(e.target.value)
    }

    const zoomRadios = ['시간단위', '날짜단위', '월단위'].map(val => {
        const isActive = curzoom === val;

        return (
            <label key={val}>
                <input type='radio' 
                    checked={isActive}
                    onChange={handleZoomChange}
                    value={val}
                />
                {val}
            </label>
        )
    })

    return (
        <div className='tool-bar'>
            <b>줌 레벨 선택: </b>
            {zoomRadios}
        </div>
    )
}

export default Toolbar

 

DataProcessor에서 넘겨 받은 내용을 출력해주는 컴포넌트당.

MessageArea.jsx

function MessageArea({msgs}) {
  const messages = msgs.map(msg => <li key={Math.random()}>{msg}</li>)
  return (
    <div className='message-area'>
        <h3>발생로그:</h3>
        <ul>
            {messages}
        </ul>
    </div>
  )
}

export default MessageArea

 

 

실행 결과 화면

 

흐름만 받아들인다면 기본 베이스로 사용해서, 간트 차트의 핵심 기능은 분명 사용할끼당.

 

그냥 참고: unit으로 사용 가능 한 값

"minute", "hour", "day" (by default), "week", "quarter", "month", "year"


그때가 좋았어? 왜?

 

시스템이 허술하고, 급하지 않아서?

잘하는 개발자가 많지 않아서?

 

아니 그때 난 젊었어!

 

시간이 알려주는 마지막 답은 젊을 때 ! 

그때! 마주치는 모든 것에 뒷걸음 말고 다가가라

그리고 그것과 친구가 되어 즐길지어라. 

  

이젠 안당. 젊음 말고 대체 머가 더 필요행~~(^-^) 

 

https://www.youtube.com/watch?v=5RN97CV9I6k

 

관련글 더보기