레퍼런스

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";
}
필드타입기본값동작
smoothbooleantruefalse면 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스크롤 컨테이너가 마운트되지 않았거나 타임라인 영역 가운데가 눈금 위가 아닐 때앵커 없이 배율만 바뀜
zoomToFittasks가 비었거나 스크롤 컨테이너가 마운트되지 않았을 때배율 변화 없음, 스크롤 없음
getScrollElement차트가 마운트되지 않았을 때null
openDetailid가 tasks의 어떤 작업도 가리키지 않을 때열리지 않음
openDetailrenderDetail도 showDetail도 켜지 않아서 패널이 꺼져 있을 때열리지 않음
closeDetail패널이 꺼져 있거나 이미 닫혀 있을 때아무 일도 없고 onDetailChange도 발생하지 않음
addTaskonTaskCreate가 없거나 allowTaskCreate가 false이거나 allowTaskCreate 없이 readOnly일 때초안 없음, 콜백 없음
addTask작업도 없고 visibleStart와 visibleEnd로 고정한 표시 범위도 없어서 눈금이 없을 때초안 없음, 콜백 없음

참고

  • 스크롤 API와 상세 메서드 둘, addTask 중 하나라도 참조가 바뀌면 핸들을 다시 만들어요. getScrollElement는 호출할 때마다 ref를 읽으니 앞서 잡아 둔 핸들에서도 현재 엘리먼트를 반환해요.
  • scrollToDate와 scrollToToday는 scrollTop을 바꾸지 않아요. 세로로 스크롤하는 메서드는 scrollToTask뿐이고 대상 행이 뷰포트 밖일 때만 움직여요.
  • 마운트 뒤에 배율을 정하는 prop은 없어요. 코드에서 배율을 정하려면 setScale을 호출하세요. defaultScale은 스토어를 만들 때 한 번만 읽는 초기값이라 나중에 바꿔도 무시돼요.
  • 차트는 배율 선택기를 그리지 않아요. 앱에서 만든 선택기에서 setScale을 호출하세요. GanttProps의 onScaleChange prop은 어디에서 바꿨든 바뀐 배율을 알려줘요.
  • zoomToFit은 GanttScrollOptions를 받지 않아요. 스크롤은 애니메이션 없이 즉시 일어나요.
  • zoomToFit은 다섯 배율을 day, week, month, quarter, year 순으로 시도해요. 프로젝트가 어느 배율에도 들어가지 않으면 year로 정해요. 타임라인을 참고하세요.
  • 세로 scrollToTask 계산에 쓰는 행 높이는 38px 고정이에요.
  • openDetail은 접힌 행까지 포함해 모든 작업에서 id를 찾아요. scrollToTask는 렌더링된 행이 있어야 해요. 부모를 접으면 행만 숨고 그 작업의 패널은 닫히지 않아요.
  • detailTaskId를 넘기면 제어 모드예요. 이때 두 상세 메서드는 패널을 직접 바꾸지 않아요. onDetailChange로 요청만 알리고 detailTaskId가 바뀌기를 기다려요. 넘기지 않으면 패널을 직접 바꾸고 onDetailChange도 함께 발생시켜요.
  • addTask는 스크롤이나 배율을 바꾸지 않아요. onTaskCreate를 호출할 뿐이고 차트가 tasks에 쓰는 일은 없어요.
  • 이 메서드들의 실제 사용법과 예제는 명령형 API를 참고하세요.