GanttHandle
`ref` 핸들이 제공하는 메서드와 메서드가 받는 옵션 타입
GanttHandle은 차트에 건 ref로 받는 객체예요. GanttScrollApi와 GanttDetailApi,
GanttTaskCreateApi를 합친 타입이에요. 그래서 스크롤과 줌 메서드, 상세 패널 메서드 둘과
addTask를 담고 있어요. 이 페이지의 다섯 타입은 모두 패키지가 타입으로만 내보내요.
import type {
GanttDetailApi,
GanttHandle,
GanttScrollApi,
GanttScrollOptions,
GanttTaskCreateApi,
} from '@jaeungkim/gantt-chart';GanttHandle
/** ref로 노출되는 명령형 API */
export interface GanttHandle
extends GanttScrollApi,
GanttDetailApi,
GanttTaskCreateApi {}자체 멤버는 선언하지 않아요. 아래 메서드는 모두 GanttHandle이 확장하는 세 인터페이스에 정의돼
있어요. 차트는 스크롤 API 객체를 펼치고 상세 메서드 둘과 addTask를 더해 핸들을 만들어요.
멤버
| 멤버 | 시그니처 | 동작 |
|---|---|---|
scrollToDate | (date: string | Date | Dayjs, options?: GanttScrollOptions) => void | 그 날짜가 요청한 위치에 오도록 가로로 스크롤해요. 날짜가 렌더링된 타임라인 밖이면 아무 일도 하지 않아요. |
scrollToToday | (options?: GanttScrollOptions) => void | 현재 시각으로 호출하는 scrollToDate예요. |
scrollToTask | (taskId: string, options?: GanttScrollOptions) => void | 작업 막대로 가로 스크롤하고 행이 화면 밖일 때만 세로로도 움직여요. 어떤 행에도 없는 id면 아무 일도 하지 않아요. |
setScale | (scale: GanttScaleKey) => void | 배율을 바꿔요. 가운데 날짜는 그 자리에 남아요. |
zoomToFit | () => void | 프로젝트 전체가 타임라인 너비에 들어가는 가장 촘촘한 배율로 바꾸고 가장 이른 작업 날짜를 왼쪽 가장자리에 고정해요. |
getScrollElement | () => HTMLDivElement | null | 차트의 스크롤 컨테이너 엘리먼트를 반환해요. 차트가 마운트되지 않았으면 null이에요. |
openDetail | (taskId: string) => void | 그 작업으로 상세 패널을 열어요. tasks에 없는 id이거나 패널이 꺼져 있으면 아무 일도 하지 않아요. |
closeDetail | () => void | 상세 패널을 닫아요. |
addTask | () => void | 현재 배율에서 오늘 날짜의 눈금 하나 길이로 초안을 만들어 onTaskCreate를 호출해요. 작업 생성이 꺼져 있으면 아무 일도 하지 않아요. |
getScrollElement를 뺀 모든 메서드는 void를 반환해요.
GanttScrollApi
/** 명령형 스크롤과 줌 API */
export interface GanttScrollApi {
/** 지정한 날짜로 가로 스크롤 */
scrollToDate: (date: string | Date | Dayjs, options?: GanttScrollOptions) => void;
/** 오늘로 가로 스크롤 */
scrollToToday: (options?: GanttScrollOptions) => void;
/** 지정한 작업으로 가로와 세로로 스크롤 */
scrollToTask: (taskId: string, options?: GanttScrollOptions) => void;
/** 가운데 날짜를 그대로 두고 배율 전환. 모르는 키는 무시 */
setScale: (scale: GanttScaleKey) => void;
/** 프로젝트 전체가 들어가는 가장 촘촘한 배율로 전환하고 화면에 보이도록 스크롤 (작업이 없으면 아무 일도 하지 않음) */
zoomToFit: () => void;
/** 스크롤 컨테이너 DOM 노드 (없으면 null) */
getScrollElement: () => HTMLDivElement | null;
}Dayjs는 dayjs의 객체 타입이에요. 패키지가 다시 내보내지 않으니 dayjs에서 직접 가져오세요.
문자열이나 Date를 넘기면 UTC로 파싱해요. 작업 데이터를 참고하세요.
GanttScaleKey는 다섯 배율 키의 유니온이에요. 배율과 테마 타입에 정리돼 있어요.
setScale이 인자를 검사할 때 쓰는 배율 표는 내부용이라 내보내지 않아요.
GanttDetailApi
/** 차트 밖에서 상세 패널을 열고 닫기 */
export interface GanttDetailApi {
/** 작업 하나로 상세 패널 열기. 모르는 id와 패널이 없는 차트(`renderDetail`, `showDetail` 없음)는 무시 */
openDetail: (taskId: string) => void;
/** 상세 패널 닫기 */
closeDetail: () => void;
}패널은 showDetail이 true일 때 켜져요. showDetail을 생략하면 renderDetail을 넘겼을 때
켜져요. 두 prop과 나머지 상세 패널 prop은 GanttProps에 있어요. 패널이 꺼져 있으면 두
메서드 모두 아무 일도 하지 않아요. 그래서 openDetail에 연결한 툴바 버튼에는 가드를 두지 않아도
돼요.
GanttTaskCreateApi
/** 차트 밖에서 새 작업 제안하기 */
export interface GanttTaskCreateApi {
/** "Add task" 버튼처럼 오늘 눈금 하나 길이의 초안을 `onTaskCreate`로 전달. `onTaskCreate`가 없거나 `allowTaskCreate`가 false면 아무 일도 하지 않음 */
addTask: () => void;
}addTask()는 작업 목록 아래 Add task 버튼이 하는 일을 ref에서 그대로 하는 메서드예요. 같은
초안을 같은 콜백으로 보내요. 생성이 꺼져 있으면 버튼과 똑같이 아무 일도 하지 않아요. 오늘이
렌더링된 타임라인 밖이면 초안은 첫 눈금에 놓여요. 새 행은 onTaskCreate가 만든 tasks 배열에 그
작업이 들어올 때 나타나요. 초안에 무엇이 담기고 무엇이 생성을 끄는지는
작업 생성을 참고하세요.
GanttScrollOptions
/** scrollTo* 메서드의 옵션 */
export interface GanttScrollOptions {
/** 스크롤에 애니메이션을 줄지 여부 (기본값 true) */
smooth?: boolean;
/** 대상이 뷰포트 안 어디에 놓일지 (기본값 'center') */
align?: "start" | "center";
}| 필드 | 타입 | 기본값 | 동작 |
|---|---|---|---|
smooth | boolean | true | false면 behavior: "auto"로 스크롤해요. undefined를 포함한 나머지 값은 behavior: "smooth"로 애니메이션해요. |
align | "start" | "center" | "center" | "start"는 대상을 타임라인 영역 왼쪽 가장자리에 둬요. "center"는 작업 목록 패널을 뺀 나머지 너비를 기준으로 타임라인 영역 가운데에 둬요. |
가로 목표값은 Math.max(0, ...)로 범위를 제한해요. 그래서 타임라인 원점보다 왼쪽에 있는 목표는
0으로 스크롤해요.
align은 가로 축에만 적용돼요. scrollToTask가 세로로 움직일 때는 언제나 행을 가운데에 맞춰요.
명령형 API에서 툴바에 핸들을 연결한 예제를 볼 수 있어요.
이른 호출
ref.current는 React가 차트를 마운트하기 전까지 null이고 언마운트된 뒤에도 다시 null이에요.
부모의 렌더 도중에 읽으면 null이에요. 부모의 useEffect나 useLayoutEffect가 실행될 때는
핸들이 준비돼 있어요. ! 대신 ?.로 가드하세요.
핸들이 생긴 뒤에는 필요한 데이터가 아직 없어도 메서드를 호출할 수 있어요. 아래 각 경우는 예외를 던지거나 콘솔 경고를 남기지 않고 그냥 반환해요.
| 호출 | 조건 | 결과 |
|---|---|---|
scrollToDate | 타임라인에 눈금이 없거나 날짜가 첫 눈금보다 앞이거나 마지막 눈금 끝을 지날 때 | 스크롤 없음 |
scrollToToday | 오늘이 렌더링된 범위 밖일 때 | 스크롤 없음 |
scrollToTask | 모르는 id이거나 접힌 부모 아래 숨어 있어서 어떤 행에도 없을 때 | 스크롤 없음 |
setScale | 키가 다섯 배율 중 하나가 아닐 때 | 배율 변화 없음 |
setScale | 스크롤 컨테이너가 마운트되지 않았거나 타임라인 영역 가운데가 눈금 위가 아닐 때 | 앵커 없이 배율만 바뀜 |
zoomToFit | tasks가 비었거나 스크롤 컨테이너가 마운트되지 않았을 때 | 배율 변화 없음, 스크롤 없음 |
getScrollElement | 차트가 마운트되지 않았을 때 | null |
openDetail | id가 tasks의 어떤 작업도 가리키지 않을 때 | 열리지 않음 |
openDetail | renderDetail도 showDetail도 켜지 않아서 패널이 꺼져 있을 때 | 열리지 않음 |
closeDetail | 패널이 꺼져 있거나 이미 닫혀 있을 때 | 아무 일도 없고 onDetailChange도 발생하지 않음 |
addTask | onTaskCreate가 없거나 allowTaskCreate가 false이거나 allowTaskCreate 없이 readOnly일 때 | 초안 없음, 콜백 없음 |
addTask | 작업도 없고 visibleStart와 visibleEnd로 고정한 표시 범위도 없어서 눈금이 없을 때 | 초안 없음, 콜백 없음 |
참고
- 스크롤 API와 상세 메서드 둘,
addTask중 하나라도 참조가 바뀌면 핸들을 다시 만들어요.getScrollElement는 호출할 때마다 ref를 읽으니 앞서 잡아 둔 핸들에서도 현재 엘리먼트를 반환해요. scrollToDate와scrollToToday는scrollTop을 바꾸지 않아요. 세로로 스크롤하는 메서드는scrollToTask뿐이고 대상 행이 뷰포트 밖일 때만 움직여요.- 마운트 뒤에 배율을 정하는 prop은 없어요. 코드에서 배율을 정하려면
setScale을 호출하세요.defaultScale은 스토어를 만들 때 한 번만 읽는 초기값이라 나중에 바꿔도 무시돼요. - 차트는 배율 선택기를 그리지 않아요. 앱에서 만든 선택기에서
setScale을 호출하세요. GanttProps의onScaleChangeprop은 어디에서 바꿨든 바뀐 배율을 알려줘요. zoomToFit은GanttScrollOptions를 받지 않아요. 스크롤은 애니메이션 없이 즉시 일어나요.zoomToFit은 다섯 배율을day,week,month,quarter,year순으로 시도해요. 프로젝트가 어느 배율에도 들어가지 않으면year로 정해요. 타임라인을 참고하세요.- 세로
scrollToTask계산에 쓰는 행 높이는 38px 고정이에요. openDetail은 접힌 행까지 포함해 모든 작업에서 id를 찾아요.scrollToTask는 렌더링된 행이 있어야 해요. 부모를 접으면 행만 숨고 그 작업의 패널은 닫히지 않아요.detailTaskId를 넘기면 제어 모드예요. 이때 두 상세 메서드는 패널을 직접 바꾸지 않아요.onDetailChange로 요청만 알리고detailTaskId가 바뀌기를 기다려요. 넘기지 않으면 패널을 직접 바꾸고onDetailChange도 함께 발생시켜요.addTask는 스크롤이나 배율을 바꾸지 않아요.onTaskCreate를 호출할 뿐이고 차트가tasks에 쓰는 일은 없어요.- 이 메서드들의 실제 사용법과 예제는 명령형 API를 참고하세요.