Skip to content

Timeline / Resource 뷰

회의실·장비·담당자 등 리소스별로 하루 일정을 가로로 비교하는 뷰입니다. ScheduleCalendar의 Month/Week/Day/List 탭과 별개로, @vuepkg/calendar/timeline 서브패스의 독립 컴포넌트로 제공됩니다.

완전히 분리된 서브패스

CalendarTimeline을 import하지 않으면 코어 번들(index.js/headless.js)에 바이트가 전혀 추가되지 않습니다 — usePublicHolidays(@vuepkg/calendar/locale-kr)와 같은 원칙입니다. ScheduleCalendar의 5번째 탭이 아니라 별도 컴포넌트인 이유이기도 합니다.

v1 범위

항목v1비고
가로축(시간)단일일, 시간 단위 (startHour~endHour)여러 날짜에 걸친 Gantt형 뷰는 이후 검토
인터랙션읽기 전용 + 클릭(schedule-click)드래그 이동/리사이즈는 v1.1 예정
리소스 연결Schedule.resourceId 필드resources 배열의 id와 매칭되지 않으면 표시되지 않음

기본 사용

vue
<script setup lang="ts">
import { ref } from 'vue'
import { CalendarTimeline } from '@vuepkg/calendar/timeline'
import type { TimelineResource } from '@vuepkg/calendar/timeline'
import { SCHEDULE_TYPE_OPTIONS } from '@vuepkg/calendar'
import type { Schedule } from '@vuepkg/calendar'

const date = ref(new Date())
const resources: TimelineResource[] = [
  { id: 'room-a', label: 'Meeting Room A' },
  { id: 'room-b', label: 'Meeting Room B' },
]

const schedules = ref<Schedule[]>([
  {
    id: '1',
    title: 'Design Review',
    type: 'team_schedule',
    resourceId: 'room-a', // ← resources의 id와 매칭
    start: new Date(2026, 3, 22, 9, 0),
    end: new Date(2026, 3, 22, 10, 30),
  },
])

function getTypeStyle(type: string) {
  const option = SCHEDULE_TYPE_OPTIONS.find((item) => item.type === type)
  return { color: option?.color ?? '#01579b', backgroundColor: option?.backgroundColor ?? '#e1f5fe' }
}
</script>

<template>
  <div style="height: 400px">
    <CalendarTimeline
      :date="date"
      :resources="resources"
      :schedules="schedules"
      :get-type-style="getTypeStyle"
      show-participant
      @schedule-click="(payload) => console.log(payload.schedule)"
    />
  </div>
</template>

resourceId가 없거나 resources의 어떤 id와도 매칭되지 않는 일정, allDay: true 일정은 Timeline에 그려지지 않습니다 — 순수 시간 단위 뷰라서입니다.

ScheduleCalendar와 나란히 배치

CalendarTimeline은 독립 컴포넌트라 자유롭게 배치할 수 있습니다. 예: Week 뷰 아래에 오늘의 리소스 배정 현황을 함께 보여주는 대시보드형 레이아웃.

vue
<template>
  <div style="display: flex; flex-direction: column; height: 100vh">
    <div style="flex: 2; min-height: 0">
      <ScheduleCalendar v-model:view="view" v-model:date="date" :schedules="schedules" v-on="calendarListeners" />
    </div>
    <div style="flex: 1; min-height: 0">
      <CalendarTimeline :date="date" :resources="resources" :schedules="schedules" :get-type-style="getTypeStyle" />
    </div>
  </div>
</template>

Props / Emits

Prop타입필수설명
dateDate표시할 단일 날짜
resourcesTimelineResource[]리소스(행) 목록
schedulesSchedule[]표시할 일정 — resourceId로 리소스와 연결
getTypeStyle(type) => { color, backgroundColor }일정 유형별 색상
showParticipantboolean참가자 이름 표시 여부 (기본 false)
startHour / endHournumber시간 축 범위 (0~23, 기본 0~23)
이벤트페이로드설명
schedule-clickCalendarScheduleClickPayload (source: 'timeline')일정 블록 클릭

Slot 커스터마이징

event scoped slot으로 이벤트 블록의 표시 콘텐츠를 대체할 수 있습니다 — ScheduleCalendarevent slot과 동일한 원칙(위치·클릭 래퍼는 유지, 내부 콘텐츠만 교체)입니다.

vue
<CalendarTimeline ...>
  <template #event="{ schedule, typeStyle, showParticipant }">
    <div :style="{ background: typeStyle.backgroundColor }">
      {{ schedule.title }}
    </div>
  </template>
</CalendarTimeline>

TimelineResource 타입

ts
interface TimelineResource {
  id: string
  label: string
}

헤드리스 사용

레이아웃 계산만 필요하면 layoutTimelineSchedules/getTimelineRowLaneCount를 직접 쓸 수 있습니다:

ts
import { layoutTimelineSchedules, getTimelineRowLaneCount } from '@vuepkg/calendar/timeline'

const layout = layoutTimelineSchedules(schedules, resource, date, { startHour: 0, endHour: 23 })
// layout: { schedule, left, width, top, height, lane, laneCount }[]
// left/width는 시간(%), top/height는 같은 리소스 내 겹침 레인(%)

설계 배경

@vuepkg/calendar/timeline이 왜 별도 서브패스인지, v1 범위를 왜 이렇게 잡았는지는 설계 RFC를 참고하세요.

Released under the MIT License.