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와 매칭되지 않으면 표시되지 않음 |
기본 사용
<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 뷰 아래에 오늘의 리소스 배정 현황을 함께 보여주는 대시보드형 레이아웃.
<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 | 타입 | 필수 | 설명 |
|---|---|---|---|
date | Date | ✅ | 표시할 단일 날짜 |
resources | TimelineResource[] | ✅ | 리소스(행) 목록 |
schedules | Schedule[] | ✅ | 표시할 일정 — resourceId로 리소스와 연결 |
getTypeStyle | (type) => { color, backgroundColor } | ✅ | 일정 유형별 색상 |
showParticipant | boolean | 참가자 이름 표시 여부 (기본 false) | |
startHour / endHour | number | 시간 축 범위 (0~23, 기본 0~23) |
| 이벤트 | 페이로드 | 설명 |
|---|---|---|
schedule-click | CalendarScheduleClickPayload (source: 'timeline') | 일정 블록 클릭 |
Slot 커스터마이징
event scoped slot으로 이벤트 블록의 표시 콘텐츠를 대체할 수 있습니다 — ScheduleCalendar의 event slot과 동일한 원칙(위치·클릭 래퍼는 유지, 내부 콘텐츠만 교체)입니다.
<CalendarTimeline ...>
<template #event="{ schedule, typeStyle, showParticipant }">
<div :style="{ background: typeStyle.backgroundColor }">
{{ schedule.title }}
</div>
</template>
</CalendarTimeline>TimelineResource 타입
interface TimelineResource {
id: string
label: string
}헤드리스 사용
레이아웃 계산만 필요하면 layoutTimelineSchedules/getTimelineRowLaneCount를 직접 쓸 수 있습니다:
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를 참고하세요.