behnoo-calendar
@dopedev/vue-calendar
A multi-calendar Vue 3 component supporting Gregorian, Jalaali (Persian), and Islamic calendar systems with full RTL support.
Features
- Three calendar systems: Gregorian, Jalaali (Persian), Islamic (Umm al-Qura)
- Three view modes: monthly, weekly, daily
- RTL layout support
- Fully customizable header via slot
- Event display with color, duration, and full-day support
- Dark mode via CSS custom properties
- TypeScript support
Installation
npm install @dopedev/vue-calendar
# or
pnpm add @dopedev/vue-calendar
Setup
Import the stylesheet once in your app entry point:
import "@dopedev/vue-calendar/style.css";
Nuxt:
// nuxt.config.ts
export default defineNuxtConfig({
css: ["@dopedev/vue-calendar/style.css"],
});
Basic Usage
<script setup lang="ts">
import { ref } from "vue";
import { VueCalendar } from "@dopedev/vue-calendar";
import type { CalendarEvent } from "@dopedev/vue-calendar";
const events = ref<CalendarEvent[]>([
{
id: 1,
title: "Team Meeting",
date: new Date(),
time: "10:00",
duration: 60,
color: "#3b82f6",
},
]);
</script>
<template>
<div style="height: 600px">
<VueCalendar :events="events" />
</div>
</template>
The component fills its container — give the parent a fixed height.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
events |
CalendarEvent[] |
[] |
Events to display |
mode |
CalendarMode |
'monthly' |
View mode: 'monthly', 'weekly', 'daily' |
locale |
string |
'en-US' |
BCP 47 locale tag used for day/month names |
calendarType |
CalendarType |
'gregory' |
Calendar system: 'gregory', 'persian', 'islamic-uma' |
startOfWeek |
number |
0 |
First day of week: 0 = Sunday … 6 = Saturday |
hours |
{ start: number; end: number } |
{ start: 0, end: 24 } |
Visible hour range in weekly/daily view |
initialDate |
Date |
new Date() |
Date the calendar opens on |
v-model
mode supports two-way binding:
<VueCalendar v-model:mode="mode" />
Events
| Event | Payload | Description |
|---|---|---|
update:mode |
CalendarMode |
Fired when the mode changes |
range-change |
{ start: Date; end: Date } |
Fired on mount and whenever the visible range changes |
event-click |
CalendarEvent |
Fired when an event is clicked |
Header Slot
Replace the default header entirely with the #header slot. All navigation and mode control is exposed through slot props:
<VueCalendar :events="events" v-model:mode="mode">
<template #header="{ mode, range, next, prev, setMode }">
<div class="flex items-center gap-2 p-3 border-b">
<button @click="prev">←</button>
<button @click="next">→</button>
<span>{{ range.start.toLocaleDateString() }}</span>
<div class="ml-auto flex gap-1">
<button
v-for="m in ['monthly', 'weekly', 'daily'] as const"
:key="m"
@click="setMode(m)"
:class="mode === m ? 'font-bold' : ''"
>
{{ m }}
</button>
</div>
</div>
</template>
</VueCalendar>
Slot props
| Prop | Type | Description |
|---|---|---|
mode |
CalendarMode |
Current view mode |
range |
{ start: Date; end: Date } |
Currently visible date range |
next |
() => void |
Navigate to the next period |
prev |
() => void |
Navigate to the previous period |
setMode |
(mode: CalendarMode) => void |
Switch view mode |
Calendar Systems
Gregorian (English)
<VueCalendar calendar-type="gregory" locale="en-US" :start-of-week="1" />
Jalaali / Persian (RTL)
Wrap the component in dir="rtl" to get correct RTL layout. Note that next and prev in the header slot remain semantically correct — swap their visual arrow directions instead.
<div dir="rtl">
<VueCalendar
calendar-type="persian"
locale="fa-IR"
:start-of-week="6"
/>
</div>
Islamic / Umm al-Qura (RTL)
<div dir="rtl">
<VueCalendar
calendar-type="islamic-uma"
locale="ar-SA"
:start-of-week="0"
/>
</div>
Customizing Event Display
You can override how events are rendered in the time‑grid (weekly/daily) and in the mobile monthly list.
- Desktop time‑grid: use the
#eventslot. - Mobile event list: use the
#event-mobileslot.
<VueCalendar :events="events">
<!-- Custom desktop event -->
<template #event="{ event }">
<div :style="{ background: event.color || '#3b82f6', padding: '4px', borderRadius: '6px' }">
{{ event.title }}
</div>
</template>
<!-- Custom mobile list event -->
<template #event-mobile="{ event }">
<div class="flex items-center gap-2">
<span class="font-bold">{{ event.title }}</span>
<span class="text-sm text-gray-500">{{ event.time }}</span>
</div>
</template>
</VueCalendar>
CalendarEvent
interface CalendarEvent {
id: number | string;
date: Date | string; // date of the event
time: string; // 'HH:mm' format
title: string;
duration?: number; // minutes (used in weekly/daily view)
color?: string; // hex or CSS value; defaults to --cal-primary
isFullDay?: boolean;
raw?: any; // attach your original API payload here
}
Theming
The calendar exposes a set of CSS custom properties for colours. You can override them on :root for the light theme and inside a .dark selector for the dark theme.
| CSS Variable | Description | Light default | Dark default |
|---|---|---|---|
--color-surface |
Main background | #ffffff |
#151823 |
--color-surface-variant |
Card / cell secondary background | #f8f8fa |
#242836 |
--color-on-surface |
Primary text colour | #2c2c2e |
#ffffff |
--color-primary |
Accent colour, event background fallback | #1a9d90 |
#40b8ac |
--color-error |
Error colour (weekends, holidays, pointer) | #e52b16 |
#ea5545 |
--color-outline |
Standard border colour | #d1d0d2 |
#605f63 |
--color-outline-variant |
Light border colour | #f0f0f0 |
#3a393c |
Today badge gradient
You can change the gradient used for the “today” circle:
| Variable | Light default | Dark default |
|---|---|---|
--cal-today-bg-light |
radial-gradient(circle at 70% 0%, #10121b, #393e4d) |
– |
--cal-today-bg-dark |
– | radial-gradient(circle at 70% 0%, #d1d0d2, #9b9a9d) |
Example customisation:
:root {
--color-surface: #ffffff;
--color-primary: #ff6b6b;
--cal-today-bg-light: radial-gradient(circle at 70% 0%, #4a00e0, #8e2de2);
}
.dark {
--color-surface: #111827;
--color-primary: #ff8787;
--cal-today-bg-dark: radial-gradient(circle at 70% 0%, #ffd700, #ff8c00);
}
Exposed Methods
When using a template ref, the component exposes:
const cal = ref<InstanceType<typeof VueCalendar>>();
cal.value?.goNext(); // navigate forward
cal.value?.goPrev(); // navigate backward
cal.value?.setMode("weekly"); // change view mode
cal.value?.currentRange; // { start: Date, end: Date }
TypeScript
All public types are exported from the package root:
import type {
CalendarEvent,
CalendarMode,
CalendarType,
CalendarDay,
CalendarDateRange,
CalendarTimeRange,
} from "@dopedev/vue-calendar";
Development
pnpm install
pnpm dev # dev sandbox at http://localhost:5173
pnpm type-check # TypeScript check
pnpm build:lib # build the distributable package
Run the Nuxt example
pnpm build:lib
cd examples/nuxt-example
pnpm install
pnpm dev # http://localhost:3000
Publishing
npm login # log in with the @dopedev org account
npm publish # prepublishOnly runs build:lib automatically
License
MIT
Related Packages
Multi-calendar Vue 3 component — Gregorian, Jalaali (Persian), and Islamic calen...
Vue 3 datepicker supporting Jalali, Hijri, Gregorian, Chinese calendars with ran...
Idiomatic Vue 3 bindings for Khorshid — composables + styled components for the...
A lightweight, zero-dependency Persian (Jalali / Shamsi) date and range picker f...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 1.0.2 | ^3.5.0 | MIT | |
| 1.0.1 | ^3.5.0 | MIT | |
| 1.0.0 | ^3.5.0 | MIT |