간트차트는 대부분 유료당.... ㅠㅠㅠ
그나마 SVAR React Gantt가 무료인데, 아래 링크에 가면 열심히 설명한당.
https://docs.svar.dev/react/gantt/getting_started/
하지만 호환성 문제가 있당. 감정없는 AI는 터무니없이 호환 된단단당.
Supported Versions of React
The wx-react-gantt package is designed for React 18. Due to backward-incompatible changes
in the react-dom library, it is not compatible with React 19. We plan to release an updated version
that supports React 19 in the near future. Until then, the Gantt widget is limited to React 18.
현재는 그냥 React 18버젼이 개인적으로는 다비당.
당근 먼저 vite로 React/Javascript 프로젝트를 아무 빈 폴더에 셋업한당.
내가 한 설치 순서지롱
npx create-vite@latest .
package.json 파일에서 react 와 react-dom 버젼 아래처럼 18로 수정
npm i
npm i tailwindcss @tailwindcss/vite ## 나 이거 좋아함!
npm i wx-react-gantt ## SVAR React Gantt
npm i axios
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(),tailwindcss()],
})
index.css
@import "tailwindcss";
베이스 샘플 확인
index.html 에 wx-icons.css 파일 넣기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="stylesheet" href="https://cdn.svar.dev/fonts/wxi/wx-icons.css" />
<!-- 아래 껀 그냥 괘니 언젠가 쓸 필요가 있을까시퍼 -->
<script src="https://unpkg.com/default-passive-events"></script>
<title>E7E React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
dummyData.js 는 테스트용 가짜 데이터를 담은 파일
// 아이고 힘들당. 마치 전에도 찾았던 듯... ㅠㅠ
const myformat = (arg) => {
console.log("new Date의 Month에 속음", arg);
if (typeof arg == "object") {
return arg.toISOString().split("T")[0];
}
return arg;
};
export const tasks = [
{
id: 20,
text: "New Task",
start: "2024-05-11",
end: "2024-05-12",
// start: new Date(2024, 5, 11),
// end: new Date(2024, 5, 12),
duration: 1,
progress: 2,
type: "task",
lazy: false,
},
{
id: 47,
text: "[1] Master project",
start: "2024-05-12",
end: "2024-05-20",
// start: new Date(2024, 5, 12),
// end: new Date(2024, 5, 20),
duration: 8,
progress: 0,
parent: 0,
type: "summary",
},
{
id: 22,
text: "Task",
start: "2024-07-11",
end: "2024-07-19",
// start: new Date(2024, 7, 11),
// end: new Date(2024, 7, 19),
duration: 8,
progress: 0,
parent: 47,
type: "task",
},
{
id: 21,
text: "New Task 2",
start: "2024-07-10",
end: "2024-07-13",
// start: new Date(2024, 7, 10),
// end: new Date(2024, 7, 13),
duration: 3,
progress: 0,
type: "task",
lazy: false,
},
];
export const links = [{ id: 1, source: 20, target: 21, type: "e2e" }];
export const scales = [
{ unit: "month", step: 1, format: "yyyy-MM" },
{ unit: "day", step: 1, format: "d" },
];
export const columns = [
{ id: "text", header: "Task name", flexgrow: 2 },
{
id: "start",
header: "Start date",
flexgrow: 1,
align: "center",
template: myformat,
},
{
id: "duration",
header: "Duration",
align: "center",
flexgrow: 1,
},
{
id: "action",
header: "",
width: 50,
align: "center",
},
];
App.jsx
import { Gantt, Willow } from "wx-react-gantt";
import "wx-react-gantt/dist/gantt.css";
import { columns, links, scales, tasks } from "./dummyData";
function App() {
return (
<>
<h1 className="text-center bg-slate-700 text-white text-5xl p-5">
어떨수 없이 띠이작
</h1>
<Willow>
<Gantt tasks={tasks} columns={columns} scales={scales} />
</Willow>
<div className="h-10"></div> {/* 그저 빈 공간 */}
<div className="wx-willow-dark-theme">
<Gantt tasks={tasks} columns={columns} links={links} scales={scales} />
</div>
</>
);
}
export default App;
node_modules/wx-react-gantt/dist 아래에
gantt.d.ts
declare module 'wx-react-gantt';
분명 잘 될기당.
dummyData.js의 columns 부분을 아래처럼 바꿔보장.
(느끼미가 오진 않으면 안되지 아니 하지 않음을 아니 모를 수가 없당.)
export const columns = [
{ id: "text", header: "작업명", flexgrow: 2 },
{
id: "start",
header: "시작일",
flexgrow: 1,
align: "center",
template: myformat,
},
{
id: "duration",
header: "기간",
align: "center",
flexgrow: 1,
},
{
id: "action",
header: "",
width: 50,
align: "center",
},
];
dummyData.js의 scales 부분도 아래처럼 바꿔보장
[역시 느끼미를 느끼지 못하지 아니 아니 아니 할 수가 없당]
export const scales = [
{ unit: "year", step: 1, format: "yyyy년" },
{ unit: "month", step: 1, format: "yyyy년-MM월" },
{ unit: "week", step: 1, format: "w주" },
{ unit: "day", step: 1, format: "d일" },
];
scales의 format에는 callback 함수(매개변수 Date)를 줄 수 있으며
css속성에 class명이나 역시 함수(매개변수 Date)를 줄 수 있다.
아래 형식으로 간트 차트에 marker도 줄 수 있당.
const markers = [
{
start: new Date().setDate(new Date().getDate() + 1),
text: "내일",
},
{
start: new Date().setDate(new Date().getDate() + 10),
text: "10일 후",
},
];
<Gantt
markers={markes}
.... 생략
/>
New Task 새창도 아래 처럼 커스터마이징이 가능하다.(일부는 불가)
const editorShape = defaultEditorShape.map((elem) => {
console.log("elem", elem);
switch (elem.label) {
case "Name": {
elem.label = "작업명";
elem.config.placeholder = "새 작업명";
break;
}
case "Description": {
elem.label = "설명";
elem.config.placeholder = "설명 추가";
break;
}
case "Type": {
elem.label = "타입";
/* 요걸로 안됨
elem.options = [
{ id: "task", label: "작업" },
{ id: "summary", label: "작업 요약" },
{ id: "milestone", label: "마일스톤" },
];
console.log("타입체킁:", elem);
*/
break;
}
case "Start date": {
elem.label = "시작일";
break;
}
case "End date": {
elem.label = "종료일";
break;
}
case "Progress": {
elem.label = "진행율";
break;
}
default:
// console.log("없는 라벨인디...ㅠㅠ");
break;
}
return elem;
});
<Gantt
editorShape={editorShape}
.... 생략
/>
taskTyps 커스터마이징
const taskTypes = [
{ id: "task", label: "작업" },
{ id: "summary", label: "작업 요약" },
{ id: "milestone", label: "마일스톤" },
];
<Gantt
taskTypes={taskTypes}
... 생략
/>
쪼메만 더 나아가 보장.
src 아래에 api라고 폴더를 맹글고 그 소게 아래 가짜 데이터 파일 맹근당.
dummyData2.js
const tasks = [
{
id: 22,
text: "새 할일",
start: "2025-03-28",
end: "2025-04-01",
duration: 4,
progress: 60,
type: "task",
lazy: false,
},
{
id: 44,
text: "최종 프로젝트",
start: "2025-03-27",
end: "2025-05-17",
duration: 50,
progress: 0,
parent: 0,
type: "summary",
lazy:false
},
{
id: 66,
text: "개발환경 설정",
start: "2025-03-17",
end: "2025-03-19",
duration: 2,
progress: 10,
parent: 44,
type: "task",
},
{
id: 77,
text: "또 할일 2",
start: "2025-07-10",
end: "2025-07-13",
duration: 3,
progress: 0,
type: "task",
lazy: false,
},
];
const links = [{ id: 88, source: 22, target: 77, type: "e2s" }];
const dumData = { tasks,links}
export default dumData;
역시 src/api 소게 아래 fake 파일을 맹근당.
dummyApi.js
import dumData from "./dummyData2";
function dummyAPI(){
return new Promise(resolve =>{
let ranTime = Math.round(Math.random()*1000);
setTimeout(()=>{
resolve(dumData);
},ranTime)
})
}
export async function getData(){
return await dummyAPI();
}
App.jsx 을 아래처럼 쪼메만 수정하장. (거의 같으니 복사/붙여넣깅!)
import { Gantt } from "wx-react-gantt";
import "wx-react-gantt/dist/gantt.css";
import { columns, scales } from "./dummyData";
import { useState } from "react";
import { useEffect } from "react";
import { getData } from "./api/dummyApi";
function App() {
const [tasks, setTasks] = useState([]);
const [links, setLinks] = useState([]);
useEffect(() => {
getData().then((ddData) => {
console.log("체킁: ", ddData);
setTasks(ddData.tasks);
setLinks(ddData.links);
});
}, []);
return (
<>
<h1 className="text-center bg-slate-700 text-white text-5xl p-5">
어떨수 없이 띠이작
</h1>
<div className="wx-willow-theme h-50">
<Gantt tasks={tasks} columns={columns} links={links} scales={scales} />
</div>
<div className="h-10"></div> {/* 그저 빈 공간 */}
<div className="wx-willow-dark-theme h-50">
<Gantt tasks={tasks} columns={columns} links={links} scales={scales} />
</div>
</>
);
}
export default App;
나에겐 아래와 같은 결과가 주어져 버렸당.
사실 컬럼쪽 글자가 첫 프로젝트만 나오고, 그 뒤가 안 나오는 현상이 있어서,
요래조래 시간을 많이 빼앗겼당. 근본적인 해결책은 아니겠지만(내가 만들지 않았당)
현상에서 추측한 나의 해결책은 높이 height를 주었당.
readonly 와 zoom 기능은 단순히 아래처럼 기술하는 것으로 가능하다.
<Gantt
... 생략
readonly={true}
zoom
/>
zoom 기능은 ctrl 키를 누르고 마우스 휠을 움직여 보면 느끼미가 온당.
Toolbar도 제공되는데, 아래 처럼 하면 된당.
import { Gantt, Toolbar } from "wx-react-gantt";
... 생략
// Gantt API 사용을 위해 필요
const init = (api) => {
apiRef.current = api;
};
<Toolbar api={apiRef.current} />
<Gantt
... 생략
init={init}
apiRef={apiRef}
/>
결과는 처음엔 New task만 있다가, Gantt 차트에서 1개 선택하면 나머지가 보인당.
요 Toolbar 커스터마이징 설명에서 혹 했다가 소중한 시간이 시뿌연 미세먼지가 되었당.
문서 설명이 오타도 많은데다, 엉터리에다 설명에 큰 줄기도 없콩, 샘플 코드는
거의 대부분 실행 결과가 만족 스럽지 못하다.
결국 조금 섣부를수도 있지만, 개인적 결론을 내었다.
SVAR React Gantt(wx-react-gantt)는 아직 많이 미완성임을 인지하고 써야만 한당.
그동안 들인 시간이 아까워 원인을 추론해 보닝, 컴포넌트가 리액트 스타일로 만들어지지 않아서
그래서 자꾸 늪으로 방향을 잡는당.
Tooltip은 아예 없는데도, 문서에는 존재한다.
WillowDark 테마는 아예 소스 레벨에서 Willow를 export해서 Willow테마와 같다.
나름 최선의 방법은 아래 실제 데모 링크에서 F12를 눌러 소스 탭을 참고하는 것이다.
[ ContextMenu(오른쪽 마우스 메뉴)는 이 소스를 보아야만 할 수 있다....
문서 자체에 성의가 좀 더 담기기를 기대해 본다 ]
https://docs.svar.dev/react/gantt/samples/#/base/willow
ContextMenu도 제공은 된당. (손이 꽤 갈 꺼 같당)
import { Gantt, ContextMenu} from "wx-react-gantt";
... 생략
const contextMenu = useRef();
...생략
<div
className="wx-willow-dark-theme h-100"
onContextMenu={(ev) => contextMenu.current(ev)}
>
<ContextMenu
api={apiRef.current}
init={(v) => (contextMenu.current = v)}
/>
<Gantt
... 생략
init={init}
apiRef={apiRef}
/>
</div>
Gantt 태그의 init에 준 콜백함수의 매개변수로 받은 api가 나름 핵심이당.
이벤트와 액션을 이용할 수 있당. (아래 코드는 select 이벤트에 이벤트핸들러를 주었단당)
useEffect(() => {
if (apiRef.current) {
apiRef.current.on("select-task", (e) => {
console.log("체킁", e);
console.log("체킁2", apiRef.current.getState().selected);
setSelected(apiRef.current.getState().selected);
});
}
}, []);
아래는 add-task 액션 사용법 샘플이당.
apiRef.current.exec("add-task", {
task: {
text: "새 작업",
},
target: apiRef.current.getState().selected[0],
mode: "after",
});
4개의 Default값을 담은 상수를 제공해 준당. (import 후 console에 찍으면 느끼미가 온당)
defaultColumns
defaultEditorShape
defaultMenuOptions
defaultToolbarButtons
아직 갈길이 멀지만(설마 나만....), 공부에 욜씸인 사람들을 위해 먼저 오픈하겠당.
진실은 나역시 홈페이지의 가이드를 읽으면서 확인해 가는 중이당.~~ (^-^)
나의 진심은 홈페이지의 가이드가 업그레이드 안되면 요기까지가 끄시당..
Gantt 결국 넌 돈을 원하는 구낭.
Without money 라도
Gantt 우린 친구가 될 수 있엉!~~
https://www.youtube.com/watch?v=KNtJGQkC-WI
React(리액트) 티키타카 32 (motion) 애니메이션 (0) | 2025.03.26 |
---|---|
React(리액트) 티키타카 31 (Recharts) (0) | 2025.03.25 |
React(리액트) 티키타카 30 (SweetAlert2) (0) | 2025.03.24 |
React(리액트) 티키타카 29 (CkEditor) (0) | 2025.03.22 |
React(리액트) 티키타카 28 (tanstack useInfiniteQuery) (0) | 2025.03.19 |