npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vuepkg/calendar

v1.0.0

Published

Vue 3 schedule calendar — Month / Week / Day / List views, zero extra dependencies

Readme

@vuepkg/calendar

npm license bundle size

Vue 3 schedule calendar — Month / Week / Day / List views, zero extra dependencies (vue peer only).

Tailwind/shadcn 친화적 — scoped slot으로 부분 커스터마이징하거나, 스타일드 컴포넌트 없이 로직만 쓰는 @vuepkg/calendar/headless로 완전히 직접 그릴 수 있습니다.

📖 문서 사이트 · 🚀 시작하기 · 📦 npm · ⚡️ Playground


English Quick Start

npm install @vuepkg/calendar
// main.ts
import '@vuepkg/calendar/style.css'
<script setup lang="ts">
import { ref } from 'vue'
import { ScheduleCalendar, useScheduleCalendarHost, type Schedule } from '@vuepkg/calendar'

const schedules = ref<Schedule[]>([])
const { view, date, listFilterDate, viewScope, scheduleTypes, calendarListeners } =
  useScheduleCalendarHost({
    onQueryChange: async ({ range }) => {
      schedules.value = await fetchSchedules(range.start, range.end)
    },
  })
</script>

<template>
  <div style="height: 100vh">
    <ScheduleCalendar
      :schedules="schedules"
      v-model:view="view"
      v-model:date="date"
      v-model:list-filter-date="listFilterDate"
      v-model:view-scope="viewScope"
      v-model:schedule-types="scheduleTypes"
      v-on="calendarListeners"
    />
  </div>
</template>

Key props: schedules, holidays, view, date, monthWeekCount, scheduleTypeOptions, fetchPublicHolidays, hideToolbar

Key emits: query-change, schedule-click, time-slot-select, schedule-move, schedule-resize, navigate

Browser support: Latest Chrome / Firefox / Safari / Edge (Evergreen) — IE not supported

For full API docs → vuepkg.github.io/calendar


주요 기능

  • 4가지 뷰 — Month / Week / Day / List
  • 월간 동적 레이아웃 — 6주 행이 부모 높이에 맞춰 균등 분할
  • 월간 +N 팝오버 — 숨긴 일정 목록
  • 멀티데이 spanning 바 — 2일 이상 종일 일정 바 표시
  • 월간 뷰 변형monthWeekCount: 2 | 3 | 6 prop으로 2주/3주 컴팩트 뷰 전환
  • Week/Day 시간 슬롯 선택 — 빈 셀 드래그로 시간 범위 선택
  • 드래그&드롭 — Week/Day 뷰 일정 이동(schedule-move) · 리사이즈(schedule-resize)
  • 반복 일정 — 매일/매주(요일 지정)/매월/매년, 횟수·종료일 조건
  • 일정 CRUD 모달ScheduleFormModal 컴포넌트 (반복 일정 UI 내장)
  • 공휴일·기념일 — 사내 기념일 prop + 한국 공공 API opt-in
  • 커스텀 일정 타입scheduleTypeOptions로 도메인 타입·색상 등록
  • CSS 변수 테마--vp-* 변수로 색상·크기·간격 전면 커스터마이징, 다크 모드 지원
  • TypeScript — 완전한 타입 선언 포함

적합한 사용처

  • Admin Dashboard — 내부 운영 도구의 일정 관리 패널
  • Booking / Reservation — 예약 시스템의 시간대 배정 UI
  • Company Groupware — 팀 일정 공유 캘린더
  • Task Management — 마감일 기반 작업 트래킹

다른 라이브러리와 비교

| 기능 | @vuepkg/calendar | FullCalendar | vue-cal | | ---- | :---: | :---: | :---: | | Vue 3 Composition API 네이티브 | ✅ | ⚠️ 프레임워크 어댑터(@fullcalendar/vue3) | ⚠️ props/emit 중심 | | TypeScript | ✅ | ✅ | ✅ | | Zero dependency | ✅ (vue peer만) | ⚠️ core + 플러그인 다중 조합 | ✅ | | Headless (컴포넌트 없이 로직만) | ✅ /headless | ⚠️ | ❌ | | Slot 커스터마이징 | ✅ toolbar/day-cell/event/month-overflow-item | ⚠️ | ✅ | | Drag & Drop 이동·리사이즈 | ✅ | ✅ | ✅ | | 반복 일정 | ✅ 무료 | ✅ RRule 플러그인, 무료 | ❌ | | Timeline / Resource 뷰 | ✅ 무료 (v1: 읽기 전용, /timeline) | ✅ Premium 유료 | ❌ | | 번들 사이즈 | 21.78KB (brotli, size-limit CI 게이트) | core+플러그인 조합 — bundlephobia에서 직접 비교 | bundlephobia에서 직접 비교 |

FullCalendar Premium 여부는 2026-07-02 공식 pricing·premium 문서 기준. 각 라이브러리의 최신 조건은 공식 문서를 확인하세요. 자체 번들 사이즈는 eager 로드되는 공유 UI 청크까지 포함해 측정한 수치입니다(과거 측정에서 누락돼 있었음 — roadmap.md 참고).


설치

npm install @vuepkg/calendar
// main.ts — CSS 전역 임포트 (필수)
import '@vuepkg/calendar/style.css'

빠른 시작

<script setup lang="ts">
import { ref } from 'vue'
import {
  ScheduleCalendar,
  useScheduleCalendarHost,
  type Schedule,
} from '@vuepkg/calendar'

const schedules = ref<Schedule[]>([
  {
    id: '1',
    title: '팀 회의',
    type: 'team_schedule',
    participantId: 'user-1',
    participantName: '홍길동',
    start: new Date(2026, 5, 15, 10, 0),
    end:   new Date(2026, 5, 15, 11, 0),
  },
])

const { view, date, listFilterDate, viewScope, scheduleTypes, calendarListeners } =
  useScheduleCalendarHost()
</script>

<template>
  <!-- 부모에 height 지정 필수 — 캘린더가 100% 채움 -->
  <div style="height: 100vh; display: flex; flex-direction: column;">
    <ScheduleCalendar
      :schedules="schedules"
      v-model:view="view"
      v-model:date="date"
      v-model:list-filter-date="listFilterDate"
      v-model:view-scope="viewScope"
      v-model:schedule-types="scheduleTypes"
      v-on="calendarListeners"
    />
  </div>
</template>

초보자 팁: useScheduleCalendarHostv-on="calendarListeners"가 낯설다면 시작하기 가이드에서 단계별 설명을 확인하세요.


API에서 일정 가져오기

뷰·날짜가 바뀔 때마다 query-change가 발생합니다. onQueryChange로 API 호출을 연결합니다:

const { view, date, listFilterDate, viewScope, scheduleTypes, calendarListeners } =
  useScheduleCalendarHost({
    onQueryChange: async (payload) => {
      schedules.value = await fetchSchedules(payload.range.start, payload.range.end)
    },
  })

일정 생성·수정 연결

ScheduleFormModal은 일정 생성/수정/삭제 폼을 제공하는 선택적 컴포넌트입니다. ScheduleCalendar와 마찬가지로 controlled — 모달의 열림 상태와 schedules 갱신은 항상 부모가 소유합니다.

<script setup lang="ts">
import { ref } from 'vue'
import {
  ScheduleCalendar,
  ScheduleFormModal,
  useScheduleCalendarHost,
  upsertSchedule,
  removeSchedule,
  type Schedule,
  type Participant,
} from '@vuepkg/calendar'

const schedules = ref<Schedule[]>([/* ... */])
const participants: Participant[] = [{ id: 'user-1', name: '홍길동' }]

const modalOpen = ref(false)
const modalMode = ref<'create' | 'edit'>('create')
const activeSchedule = ref<Schedule | null>(null)
const initialStart = ref<Date>()
const initialEnd = ref<Date>()

const { view, date, listFilterDate, viewScope, scheduleTypes, calendarListeners } =
  useScheduleCalendarHost({
    onTimeSlotSelect(payload) {
      modalMode.value = 'create'
      activeSchedule.value = null
      initialStart.value = payload.start
      initialEnd.value = payload.end
      modalOpen.value = true
    },
    onScheduleClick(payload) {
      const masterId = payload.schedule.recurrenceId ?? payload.schedule.id
      modalMode.value = 'edit'
      activeSchedule.value = schedules.value.find((s) => s.id === masterId) ?? payload.schedule
      modalOpen.value = true
    },
  })

function handleSubmit(schedule: Schedule) {
  schedules.value = upsertSchedule(schedules.value, schedule)
  modalOpen.value = false
}

function handleDelete(scheduleId: string) {
  schedules.value = removeSchedule(schedules.value, scheduleId)
  modalOpen.value = false
}
</script>

<template>
  <ScheduleCalendar
    :schedules="schedules"
    v-model:view="view"
    v-model:date="date"
    v-model:list-filter-date="listFilterDate"
    v-model:view-scope="viewScope"
    v-model:schedule-types="scheduleTypes"
    v-on="calendarListeners"
  />

  <ScheduleFormModal
    :open="modalOpen"
    :mode="modalMode"
    :schedule="activeSchedule"
    :initial-start="initialStart"
    :initial-end="initialEnd"
    :participants="participants"
    :existing-schedules="schedules"
    @close="modalOpen = false"
    @submit="handleSubmit"
    @delete="handleDelete"
  />
</template>

ScheduleFormModal이 원하는 폼 UI와 다르다면 데이터 헬퍼만 가져다 직접 다이얼로그를 구성할 수 있습니다:

import { upsertSchedule, removeSchedule, buildScheduleFromDraft } from '@vuepkg/calendar'

schedules.value = upsertSchedule(
  schedules.value,
  buildScheduleFromDraft(draft, participants, schedules.value),
)
schedules.value = removeSchedule(schedules.value, targetId)

ScheduleFormModal Props / Emits

| Prop | 타입 | 설명 | | ---- | ---- | ---- | | open | boolean | 모달 표시 여부 | | mode | 'create' \| 'edit' | 생성/수정 모드 | | schedule | Schedule \| null | edit 모드 수정 대상 | | initialStart / initialEnd | Date | create 모드 초기 시간 — time-slot-select payload 전달 | | participants | Participant[] | 참가자 선택지 + 이름 조회 | | existingSchedules | Schedule[] | create 모드 ID 충돌 방지용 (기본 []) | | scheduleTypeOptions | ScheduleTypeOption[] | 유형 선택지 (기본 SCHEDULE_TYPE_OPTIONS) | | labels | Partial<ScheduleFormLabels> | 폼 표시 텍스트 국제화 오버라이드 — 미지정 필드는 한국어 기본값 유지 |

| 이벤트 | 페이로드 | 설명 | | ------ | -------- | ---- | | close | — | 취소·Esc·배경 클릭 | | submit | Schedule | buildScheduleFromDraft로 완성된 일정 — upsertSchedule에 바로 전달 | | delete | string (scheduleId) | edit 모드 삭제 버튼 — removeSchedule에 바로 전달 |

국제화(i18n)

ScheduleFormModal의 기본 텍스트는 한국어입니다. labels prop에 원하는 필드만 넘기면 해당 항목만 덮어씁니다(나머지는 한국어 기본값 유지). 전체 필드 목록은 ScheduleFormLabels 타입, 기본값은 DEFAULT_SCHEDULE_FORM_LABELS 상수를 참고하세요.

<ScheduleFormModal
  :labels="{
    createTitle: 'Create Event',
    editTitle: 'Edit Event',
    titleField: 'Title',
    typeField: 'Type',
    participantField: 'Participant',
    startField: 'Start',
    endField: 'End',
    cancelButton: 'Cancel',
    saveButton: 'Save',
    createButton: 'Create',
    deleteButton: 'Delete',
    recurrenceWeekdayLabels: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
  }"
  ...
/>

라벨 오버라이드만으로 부족하다면(필드 재배치, 다른 위젯 등) buildScheduleFromDraft + 직접 구현한 폼을 권장합니다 — 위 "데이터 헬퍼만 가져다 직접 다이얼로그를 구성" 예제를 참고하세요.


반복 일정

Schedule.recurrenceRecurrenceRule을 지정하면 캘린더가 표시 중인 기간 내에서 자동으로 개별 회차를 생성합니다. 부모는 마스터 일정 한 건만 관리하면 됩니다.

import type { Schedule } from '@vuepkg/calendar'

const schedule: Schedule = {
  id: 's-standup',
  title: '주간 스탠드업',
  type: 'team_schedule',
  participantId: 'user-1',
  participantName: '홍길동',
  start: new Date(2026, 3, 6, 9, 0),
  end: new Date(2026, 3, 6, 9, 30),
  recurrence: {
    freq: 'weekly', // 'daily' | 'weekly' | 'monthly' | 'yearly'
    interval: 1,
    byWeekday: [1, 3, 5], // 0=일 ~ 6=토
    count: 20,             // count 또는 until 중 하나
  },
}
  • 각 회차는 id: "${마스터id}::YYYY-MM-DD", recurrenceId, isRecurrenceInstance: true를 가진 파생 객체입니다.
  • ScheduleFormModal은 반복 없음/매일/매주/매월/매년 선택 UI를 내장합니다.
  • 반복 일정 칩에는 ⟳ 아이콘이 표시됩니다.

알려진 제약: 자체 RRULE 서브셋으로 iCal 전체 사양과 호환되지 않습니다(BYSETPOS 미지원, 월말 기준일 롤오버 등) — 상세: 반복 일정 가이드 § 알려진 제약


Props

| Prop | 타입 | 기본값 | 설명 | | ---- | ---- | ------ | ---- | | schedules | Schedule[] | [] | 표시할 일정 목록 | | holidays | Holiday[] | [] | 기념일·공휴일 목록 | | view | CalendarView | 'month' | 현재 뷰 ('month'|'week'|'day'|'list') | | date | Date | 오늘 | 현재 기준 날짜 | | monthWeekCount | 2 \| 3 \| 6 | 6 | 월간 뷰 표시 주 수 (2주·3주 컴팩트 모드) | | viewScope | ViewScope | 'company' | 일정 범위 필터 ('my'|'company') | | scheduleTypes | string[] \| null | null | 활성 일정 타입 필터 (null=전체) | | listFilterDate | Date \| null | null | List 뷰 날짜 필터 | | scheduleTypeOptions | ScheduleTypeOption[] | 기본 3종 | 커스텀 일정 타입 정의 | | fetchPublicHolidays | boolean | false | 한국 공공 API 공휴일 opt-in | | hideToolbar | boolean | false | 툴바 숨김 (뷰 고정 임베딩용) |

Emits

| 이벤트 | 페이로드 | 설명 | | ------ | -------- | ---- | | view-change | CalendarViewChangePayload | 뷰 탭 전환 | | date-select | CalendarDateSelectPayload | 날짜 클릭 | | navigate | CalendarNavigatePayload | ‹ › · Today 네비 | | schedule-click | CalendarScheduleClickPayload | 일정 클릭 | | time-slot-select | CalendarTimeSlotSelectPayload | Week/Day 빈 셀 드래그 (시간 범위 선택) | | schedule-move | CalendarScheduleMovePayload | Week/Day 일정 드래그 이동 | | schedule-resize | CalendarScheduleResizePayload | Week/Day 일정 하단 리사이즈 | | overflow-click | CalendarOverflowClickPayload | 월간 +N 클릭 | | list-filter-clear | — | List 날짜 필터 해제 | | query-change | ScheduleQueryChangePayload | 범위·필터 변경 (API 조회용) |

useScheduleCalendarHost를 사용하면 위 이벤트가 자동으로 연결됩니다. v-on="calendarListeners"만 추가하면 됩니다.


Headless 서브패스

ScheduleCalendar/ScheduleFormModal Vue 컴포넌트 없이 composable·타입·일정 CRUD 유틸만 필요하다면 @vuepkg/calendar/headless를 사용하세요. 스타일드 컴포넌트를 전혀 import하지 않아 커스텀 UI 위에 캘린더 로직만 재사용하고 싶을 때 유용합니다.

import {
  useCalendar,
  useTimeSlotSelection,
  useScheduleDrag,
  expandRecurringSchedules,
  buildScheduleFromDraft,
} from '@vuepkg/calendar/headless'

usePublicHolidays(한국 공공데이터포털 연동)는 별도 서브패스 @vuepkg/calendar/locale-kr로 분리되어 있습니다 — 아래 참고.

한국 공휴일 서브패스 (locale-kr)

ScheduleCalendarfetchPublicHolidays/publicHolidayServiceKey는 한국 공공데이터포털 API 연동입니다. 이 기능은 코어 번들(index.js/headless.js)에서 완전히 분리되어 있어 — 한국 공휴일을 쓰지 않는 소비자는 이 코드의 바이트를 전혀 지불하지 않고, fetchPublicHolidaystrue가 되는 시점에만 런타임에 로드됩니다.

import { usePublicHolidays } from '@vuepkg/calendar/locale-kr'

다른 나라 공휴일은 holidays prop을 직접 채우면 됩니다(국가 무관) — 자세한 레시피는 공휴일 연동 가이드를 참고하세요.

메인 엔트리(@vuepkg/calendar)에서도 동일한 이름으로 그대로 가져올 수 있습니다 — /headless는 별도 export가 추가된 것이 아니라 같은 로직을 컴포넌트 없이 가져오는 경로입니다.


Timeline / Resource 뷰 서브패스

회의실·장비·담당자별로 하루 일정을 가로로 비교하는 뷰입니다. ScheduleCalendar의 5번째 탭이 아니라 @vuepkg/calendar/timeline의 완전히 분리된 독립 컴포넌트입니다 — import하지 않으면 코어 번들에 0바이트 영향입니다. v1은 단일일·시간 단위·읽기 전용(드래그는 v1.1 예정)입니다.

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

const resources: TimelineResource[] = [{ id: 'room-a', label: 'Meeting Room A' }]
</script>

<template>
  <div style="height: 400px">
    <CalendarTimeline
      :date="date"
      :resources="resources"
      :schedules="schedules"
      :get-type-style="getTypeStyle"
    />
  </div>
</template>

일정을 리소스 행에 연결하려면 Schedule.resourceIdresourcesid와 매칭시키세요 — 매칭되지 않는 일정(또는 resourceId 미지정)은 표시되지 않습니다. 자세한 내용은 Timeline 가이드를 참고하세요.


커스텀 일정 타입

import { SCHEDULE_TYPE_OPTIONS, type ScheduleTypeOption } from '@vuepkg/calendar'

const typeOptions: ScheduleTypeOption[] = [
  ...SCHEDULE_TYPE_OPTIONS,
  { type: 'project', label: '프로젝트', color: '#fff', backgroundColor: '#6366f1' },
  { type: 'leave',   label: '휴가',     color: '#fff', backgroundColor: '#f59e0b' },
]
<ScheduleCalendar :schedule-type-options="typeOptions" ... />

테마 커스터마이징

모든 색상·크기·간격은 CSS 변수(--vp-*)로 정의되어 있어 한 줄만 덮어쓰면 전체 테마가 변경됩니다.

:root {
  --vp-color-primary: #7c3aed;
  --vp-today-badge-bg: #7c3aed;
  --vp-month-cell-selected-bg: #f5f3ff;
}

다크 모드 — 시스템 설정(prefers-color-scheme: dark) 자동 반영 또는 .vp-dark 클래스로 수동 전환:

document.documentElement.classList.toggle('vp-dark')

토큰 전체 목록은 테마 커스터마이징 가이드를 참고하세요.


접근성 (a11y)

  • 뷰 탭role="group" + aria-pressed, 화살표 키 네비게이션(roving tabindex)
  • 월간 셀role="grid", 날짜 aria-label
  • 팝오버 — focus trap, Esc 닫기, 외부 클릭 닫기, 닫힘 시 트리거로 focus 복원
  • 데이터 테이블(List 뷰) — 행 Enter/Space 활성화, aria-label
  • 키보드 포커스 — 모든 인터랙티브 요소에 :focus-visible 아웃라인
  • 자동 검증@axe-core/playwright로 Month/Week/Day/List/Timeline 뷰 + 일정 폼 모달을 라이트·다크 모드 각각 CI에서 매 push마다 스캔(e2e/accessibility.spec.ts)

TypeScript

import type {
  Schedule,
  Holiday,
  CalendarView,
  ViewScope,
  ScheduleTypeOption,
  RecurrenceRule,
  ScheduleQueryChangePayload,
  CalendarScheduleClickPayload,
  CalendarTimeSlotSelectPayload,
  CalendarScheduleMovePayload,
  CalendarScheduleResizePayload,
} from '@vuepkg/calendar'

기술 스택

  • Vue 3 + TypeScript
  • 커스텀 HTML/CSS (외부 UI 라이브러리 의존 없음)
  • Vitest · Playwright E2E

Contributing

github.com/vuepkg/calendar · 상세 절차·RFC 프로세스는 CONTRIBUTING.md 참고

요구 사항: Node 24+ · pnpm 9+

pnpm install                          # 의존성 설치 + Husky pre-push 훅 등록
pnpm --filter @vuepkg/calendar dev    # 개발 서버
pnpm verify:push                      # push 전 검증 (lint + typecheck + vitest)
pnpm turbo run build:lib              # 전체 빌드
pnpm test:e2e:ci                      # 기능 E2E (CI와 동일)
pnpm test:e2e:visual                  # 시각 회귀 (UI/CSS 변경 시)

License

MIT © vuepkg