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

@volt-package/calendar-core

v0.1.1

Published

A pure JavaScript calendar engine providing core logic without UI rendering - state management, event handling, and lifecycle

Readme

@volt-package/calendar-core

The core package of Volt Calendar. Provides all essential features including state management, event system, date utilities, and interaction handling.

📦 Installation

npm install @volt-package/calendar-core
# or
pnpm add @volt-package/calendar-core

🏗️ Architecture

Main Components

@volt-package/calendar-core
├── VoltCalendar      # Main calendar class
├── Store             # State management (based on @volt-package/store)
├── EventEmitter      # Pub/Sub event system
├── InteractionPlugin # Drag-and-drop, coordinate conversion
├── Types             # TypeScript type definitions
├── Date Utilities    # Date utility functions
└── RRULE Helpers     # Recurring event helper functions

🚀 Quick Start

Basic Usage

import { VoltCalendar } from '@volt-package/calendar-core';
import { MonthPlugin } from '@volt-package/calendar-month';

const calendar = new VoltCalendar({
  plugins: [MonthPlugin],
  initialView: 'month',
  events: [
    {
      id: 'event-1',
      title: 'Meeting',
      start: '2025-12-15T10:00:00',
      end: '2025-12-15T11:00:00',
    },
  ],
});

// Subscribe to state changes
calendar.subscribe((state) => {
  console.log('Calendar state updated:', state);
});

// Navigate to next month
calendar.next();

// Add event
calendar.addEvent({
  id: 'event-2',
  title: 'Lunch',
  start: '2025-12-15T12:00:00',
  end: '2025-12-15T13:00:00',
});

📚 API Documentation

VoltCalendar

Main calendar class. Entry point for all calendar functionality.

Constructor

new VoltCalendar(config: VoltCalendarConfig)

Config Options:

interface VoltCalendarConfig {
  plugins: any[]; // View plugin array
  initialView: ViewType; // 'month' | 'week' | 'day'
  events: CalendarEventInput[]; // Initial event list
  locale?: string; // Locale (default: 'ko-KR')
  timeZone?: string; // Timezone (default: browser timezone)
  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6; // Week start day (0: Sunday)
}

Methods

Navigation
// Navigate to next period
calendar.next(): void

// Navigate to previous period
calendar.prev(): void

// Navigate to specific date
calendar.gotoDate(date: Date): void

// Change view
calendar.changeView(viewName: 'month' | 'week' | 'day'): void
Event Management
// Add event
calendar.addEvent(event: CalendarEventInput): void

// Update event
calendar.updateEvent(event: CalendarEventInput): void

// Remove event
calendar.removeEvent(eventId: string): void
Subscription
// Subscribe to state changes
calendar.subscribe(listener: (state: ViewState) => void): () => void

// Example usage
const unsubscribe = calendar.subscribe((state) => {
  console.log('State changed:', state);
});

// Unsubscribe
unsubscribe();
Low-level API
// Access Store instance
calendar.getStore(): Store

// Access EventEmitter instance
calendar.getEmitter(): EventEmitter

// Access specific plugin
calendar.getPlugin(name: string): any

Store

State management class. Built on @volt-package/store for automatic dependency tracking and immutability.

Methods

Date Management
// Get current date
store.getCurrentDate(): Date

// Set current date
store.setCurrentDate(date: Date): void

// Get current view type
store.getCurrentView(): ViewType

// Set view type
store.setCurrentView(view: ViewType): void
Event Management
// Get all events
store.getEvents(): CalendarEvent[]

// Get events in range (including recurring event expansion)
store.getEventsInRange(rangeStart: Date, rangeEnd: Date): CalendarEvent[]

// Add event
store.addEvent(event: CalendarEventInput): void

// Update event
store.updateEvent(event: CalendarEventInput): void

// Remove event
store.removeEvent(eventId: string): void

// Set events list
store.setEvents(events: CalendarEventInput[]): void
State Management
// Get view state
store.getState(): ViewState | null

// Set view state
store.setState(state: ViewState): void

// Get configuration
store.getConfig(): VoltCalendarConfig
Recurring Events
// Get count of recurrence instances
store.getRecurrenceInstanceCount(
  eventId: string,
  rangeStart: Date,
  rangeEnd: Date
): number

EventEmitter

Pub/Sub pattern-based event system.

Event Types

type EventType =
  | 'stateChange' // State change
  | 'viewChange' // View change
  | 'dateChange' // Date change
  | 'custom'; // Custom event

Methods

// Subscribe to event
emitter.on<T>(type: EventType, listener: (data: T) => void): () => void

// Emit event
emitter.emit<T>(type: EventType, data: T): void

// Subscribe to state changes (special handler)
emitter.subscribeToState(listener: (state: ViewState) => void): () => void

// Emit state change
emitter.emitStateChange(state: ViewState): void

// Clear all subscriptions
emitter.clear(): void

Usage Example

const emitter = calendar.getEmitter();

// Subscribe to view change event
const unsubscribe = emitter.on('viewChange', (data) => {
  console.log('View changed to:', data.view);
});

// Emit custom event
emitter.emit('custom', {
  type: 'myCustomEvent',
  payload: { foo: 'bar' },
});

InteractionPlugin

Provides interaction features such as drag-and-drop and coordinate conversion.

Methods

Container Setup
// Set calendar container element
plugin.setContainerElement(element: HTMLElement): void
Coordinate Conversion
// Convert screen coordinates to date/time
plugin.getDateFromPoint(
  pageX: number,
  pageY: number,
  viewType: string
): Date | null

// Convert screen coordinates to column index (for week view)
plugin.getColumnFromPoint(pageX: number): number | null

// Convert pixels to time (minutes)
plugin.calculateTimeFromPixels(
  pixels: number,
  totalHeight: number
): number
Drag and Drop
// Start drag
plugin.startDrag(event: MouseEvent, startDate: Date): void

// Move drag
plugin.moveDrag(event: MouseEvent, viewType: string): DragState

// End drag
plugin.endDrag(): DragState

// Get drag state
plugin.getDragState(): DragState

Usage Example

import { VoltCalendar, InteractionPlugin } from '@volt-package/calendar-core';

const calendar = new VoltCalendar({
  plugins: [MonthPlugin, InteractionPlugin],
  initialView: 'week',
  events: [],
});

const interaction = calendar.getPlugin('interaction') as InteractionPlugin;

// Set container
const container = document.getElementById('calendar-container');
interaction.setContainerElement(container!);

// Implement drag and drop
container.addEventListener('mousedown', (e) => {
  const date = interaction.getDateFromPoint(e.pageX, e.pageY, 'week');
  if (date) {
    interaction.startDrag(e, date);
  }
});

container.addEventListener('mousemove', (e) => {
  const dragState = interaction.moveDrag(e, 'week');
  if (dragState.isDragging) {
    console.log('Dragging to:', dragState.currentDate);
  }
});

container.addEventListener('mouseup', () => {
  const finalState = interaction.endDrag();
  if (finalState.startDate && finalState.currentDate) {
    console.log('Dropped:', finalState);
  }
});

📐 Types

CalendarEventInput

User-provided event data:

interface CalendarEventInput {
  id: string;
  title: string;
  start: string; // ISO 8601 format
  end: string; // ISO 8601 format
  isAllDay?: boolean;
  resourceId?: string;
  rrule?: {
    freq: 'daily' | 'weekly' | 'monthly' | 'yearly';
    interval?: number;
    until?: string;
    count?: number;
  };
  extendedProps?: Record<string, any>;
}

CalendarEvent

Extended event data processed internally:

interface CalendarEvent extends CalendarEventInput {
  _id: string; // Unique identifier (for recurring instances)
  _instanceDate?: Date; // Occurrence date for recurring instances
}

ViewState

Final rendering state of the view:

interface ViewState {
  viewType: ViewType;
  currentDate: Date;
  dateRange: {
    start: Date;
    end: Date;
  };
  grid: GridCell[];
  events: RenderedEvent[];
}

RenderedEvent

Event data for rendering:

interface RenderedEvent {
  def: CalendarEventInput;
  style: StyleAttributes;
  isStart: boolean;
  isEnd: boolean;
}

interface StyleAttributes {
  top: string; // "25.5%"
  height: string; // "12.5%"
  left: string; // "0%"
  width: string; // "50%"
  zIndex: number;
}

🗓️ Date Utilities

Convenient utility functions wrapping @volt-package/date.

Date Comparison

import { isSameDay, isSameWeek, isSameMonth, isToday } from '@volt-package/calendar-core';

const date1 = new Date('2025-12-15');
const date2 = new Date('2025-12-15');

isSameDay(date1, date2); // true
isSameMonth(date1, date2); // true
isToday(date1); // false (if not today)

Date Range

import { startOfMonth, endOfMonth, startOfWeek, endOfWeek } from '@volt-package/calendar-core';

const date = new Date('2025-12-15');

startOfMonth(date); // 2025-12-01 00:00:00
endOfMonth(date); // 2025-12-31 23:59:59
startOfWeek(date); // First day of week
endOfWeek(date); // Last day of week

Date Arithmetic

import { addDays, addWeeks, addMonths, daysBetween } from '@volt-package/calendar-core';

const date = new Date('2025-12-15');

addDays(date, 7); // 7 days later
addWeeks(date, 2); // 2 weeks later
addMonths(date, 1); // 1 month later

const days = daysBetween(new Date('2025-12-01'), new Date('2025-12-31')); // 30

Time Handling

import { getTimeInMinutes, timeToPercentage, getDateOnly } from '@volt-package/calendar-core';

const datetime = new Date('2025-12-15T14:30:00');

getTimeInMinutes(datetime); // 870 (14 * 60 + 30)
timeToPercentage(datetime); // 60.42 (14:30 is 60.42% of the day)
getDateOnly(datetime); // 2025-12-15 00:00:00

Time Overlap

import { timesOverlap } from '@volt-package/calendar-core';

const overlap = timesOverlap(
  new Date('2025-12-15T10:00:00'), // Event1 start
  new Date('2025-12-15T12:00:00'), // Event1 end
  new Date('2025-12-15T11:00:00'), // Event2 start
  new Date('2025-12-15T13:00:00') // Event2 end
); // true (overlaps)

🔁 RRULE Support

Supports RFC 5545 recurring events using @volt-package/rrule.

Creating Recurring Events

const event = {
  id: 'recurring-1',
  title: 'Weekly Meeting',
  start: '2025-12-01T10:00:00',
  end: '2025-12-01T11:00:00',
  rrule: {
    freq: 'weekly',
    interval: 1,
    until: '2025-12-31',
  },
};

calendar.addEvent(event);

Recurring Event Expansion

Store's getEventsInRange method automatically expands recurring events:

const store = calendar.getStore();
const events = store.getEventsInRange(new Date('2025-12-01'), new Date('2025-12-31'));
// events includes all recurring instances

RRULE Options

interface RRuleOptions {
  freq: 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY';
  dtstart?: Date; // Start date
  until?: Date; // End date
  count?: number; // Number of occurrences
  interval?: number; // Interval
  byMonth?: number[]; // Specific months
  byMonthDay?: number[]; // Specific days
  byDay?: Weekday[]; // Specific weekdays
  bySetPos?: number[]; // Position specification
}

Helper Functions

import { isRecurringEvent, generateInstanceId } from '@volt-package/calendar-core';

// Check if event is recurring
if (isRecurringEvent(event.rrule)) {
  console.log('This is a recurring event');
}

// Generate instance ID
const instanceId = generateInstanceId('event-1', new Date('2025-12-15')); // "event-1__2025-12-15"

🔌 External Dependencies

The core package uses these libraries:

All libraries are zero-dependency and focused on being lightweight.


🎯 Design Philosophy

1. Plugin Architecture

Core provides minimal functionality. Views are extended through plugins.

// Core alone cannot render
const calendar = new VoltCalendar({
  plugins: [], // No plugins
  initialView: 'month',
  events: [],
});

// Activate views by adding plugins
import { MonthPlugin } from '@volt-package/calendar-month';

const calendar = new VoltCalendar({
  plugins: [MonthPlugin], // Add plugin
  initialView: 'month',
  events: [],
});

2. Framework Independence

Core is framework-independent. Works with React, Vue, Vanilla JS, etc.

// Vanilla JS
const calendar = new VoltCalendar({
  /* ... */
});
calendar.subscribe((state) => {
  // Update DOM
  updateCalendarDOM(state);
});

// React
function Calendar() {
  const [state, setState] = useState<ViewState | null>(null);

  useEffect(() => {
    const calendar = new VoltCalendar({
      /* ... */
    });
    return calendar.subscribe(setState);
  }, []);

  return <CalendarView state={state} />;
}

3. Immutability and Reactivity

Store is based on @volt-package/store to ensure automatic dependency tracking and immutability.

// Internally immutable update
store.setCurrentDate(new Date('2025-12-15'));
// → Store automatically detects changes and notifies subscribers

📄 License

MIT


🤝 Contributing

Issues and PRs are always welcome!

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request