New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@bernierllc/calendar

Package Overview
Dependencies
Maintainers
2
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bernierllc/calendar

A calendar package for the tools monorepo.

latest
Source
npmnpm
Version
1.2.2
Version published
Maintainers
2
Created
Source

/* Copyright (c) 2025 Bernier LLC

This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC. */

@bernierllc/calendar

A highly customizable, accessible React calendar component for Next.js projects with support for template-driven and render prop-based customization.

Features

  • 📅 Month and Week Views - Toggle between calendar views
  • 🎨 Flexible Styling - Custom colors, pills, and styling logic
  • 🧩 Template Tags - String-based customization with {{fieldName}} syntax
  • ⚛️ Render Props - Full React component customization
  • 📋 Event List - Sidebar list of events with custom rendering
  • 🔍 Dynamic Filters - Custom filter UI with render props
  • Accessible - WCAG 2.1 AA compliant with keyboard navigation
  • 📤 ICS Export - Generate iCalendar files with recurring events
  • 🎯 TypeScript - Full type safety and IntelliSense support

Installation

npm install @bernierllc/calendar

Quick Start

import { Calendar } from '@bernierllc/calendar';
import type { CalendarEvent } from '@bernierllc/calendar';

const events: CalendarEvent[] = [
  {
    uid: '1',
    summary: 'Team Meeting',
    description: 'Weekly team sync',
    start: '2024-01-15T10:00:00Z',
    end: '2024-01-15T11:00:00Z',
    categories: ['work'],
    location: 'Conference Room A'
  }
];

function App() {
  return (
    <Calendar 
      events={events}
      categoryColors={{ work: 'bg-blue-500' }}
    />
  );
}

API Reference

Calendar Props

PropTypeDescription
eventsCalendarEvent[]Array of calendar events
categoryColorsRecord<string, string>Predefined color mapping for categories
initialView'week' | 'month'Initial calendar view (default: 'month')
filtersReactNode | FunctionCustom filter UI or render prop
getEventColor(event: CalendarEvent) => stringCustom color logic function
getEventPill(event: CalendarEvent) => { label: string, color: string } | nullCustom pill logic for event list
ellipsizeTitlesbooleanAlways ellipsize event titles (default: true)

Template-Driven Customization

PropTypeDescription
eventDescriptionTemplatestringTemplate for event description/body
eventModalTemplatestringTemplate for event modal content
eventCellTemplatestringTemplate for calendar grid cells
eventListRowTemplatestringTemplate for event list rows

Render Prop Customization

PropTypeDescription
eventModalRenderer(event: any) => ReactNodeCustom modal rendering
eventCellRenderer(event: any, context: { date: Date }) => ReactNodeCustom calendar cell rendering
eventListRowRenderer(event: any) => ReactNodeCustom event list row rendering

CalendarEvent Interface

interface CalendarEvent {
  uid: string;                    // Unique identifier
  summary: string;                // Event title
  description?: string;           // Event description
  location?: string;              // Event location
  start: string;                  // ISO 8601 start date
  end: string;                    // ISO 8601 end date
  recurrenceRule?: string;        // RRULE string for recurring events
  status?: 'CONFIRMED' | 'TENTATIVE' | 'CANCELLED';
  organizer?: string;             // Organizer email/name
  attendees?: string[];           // Attendee emails
  categories?: string[];          // Event categories for color coding
  [key: string]: any;             // Custom fields supported
}

Usage Examples

Basic Usage

import { Calendar } from '@bernierllc/calendar';

const events = [
  {
    uid: '1',
    summary: 'Team Meeting',
    start: '2024-01-15T10:00:00Z',
    end: '2024-01-15T11:00:00Z',
    categories: ['work']
  }
];

<Calendar 
  events={events}
  categoryColors={{ work: 'bg-blue-500', personal: 'bg-green-500' }}
/>

Template Tags

<Calendar 
  events={events}
  eventDescriptionTemplate="Mentor: {{mentor}}, Location: {{location}}, Type: {{experienceType}}"
  eventModalTemplate="<h3>{{summary}}</h3><p>Mentor: {{mentor}}</p><p>Experience: {{experienceType}}</p>"
  eventCellTemplate="{{summary}} ({{mentor}})"
  eventListRowTemplate="{{summary}} - {{mentor}} - {{experienceType}}"
/>

Render Props

<Calendar 
  events={events}
  eventModalRenderer={(event) => (
    <div className="custom-modal">
      <h2>{event.summary}</h2>
      <p>Mentor: {event.mentor}</p>
      <p>Experience: {event.experienceType}</p>
      <button onClick={() => handleEdit(event)}>Edit</button>
    </div>
  )}
  eventCellRenderer={(event, { date }) => (
    <div className={`event-cell ${event.experienceType}`}>
      <span className="title">{event.summary}</span>
      <span className="mentor">{event.mentor}</span>
    </div>
  )}
/>

Custom Styling Logic

<Calendar 
  events={events}
  getEventColor={(event) => {
    if (event.experienceType === 'mentorship') return 'bg-purple-500';
    if (event.experienceType === 'workshop') return 'bg-orange-500';
    return 'bg-gray-500';
  }}
  getEventPill={(event) => ({
    label: event.experienceType,
    color: event.experienceType === 'mentorship' ? 'bg-purple-100 text-purple-800' : 'bg-orange-100 text-orange-800'
  })}
/>

Custom Filters

<Calendar 
  events={events}
  filters={({ visibleEvents, filters, setFilters }) => (
    <div className="filters">
      <select 
        value={filters.experienceType || ''} 
        onChange={(e) => setFilters({ ...filters, experienceType: e.target.value })}
      >
        <option value="">All Types</option>
        <option value="mentorship">Mentorship</option>
        <option value="workshop">Workshop</option>
      </select>
      <span>{visibleEvents.length} events</span>
    </div>
  )}
/>

ICS Export

import { generateICS } from '@bernierllc/calendar/utils/ics';

const handleExport = () => {
  const icsString = generateICS(events);
  const blob = new Blob([icsString], { type: 'text/calendar' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'calendar.ics';
  a.click();
};

Advanced Patterns

Combining Template Tags and Render Props

Template tags are used as fallbacks when render props aren't provided:

<Calendar 
  events={events}
  eventModalTemplate="<h3>{{summary}}</h3><p>{{description}}</p>"
  eventModalRenderer={(event) => {
    // Custom logic for specific events
    if (event.experienceType === 'mentorship') {
      return <MentorshipModal event={event} />;
    }
    // Fallback to template for other events
    return null; // Will use template
  }}
/>

Custom Fields with Type Safety

interface CustomEvent extends CalendarEvent {
  mentor: string;
  experienceType: 'mentorship' | 'workshop' | 'networking';
  difficulty: 'beginner' | 'intermediate' | 'advanced';
  maxParticipants?: number;
}

const events: CustomEvent[] = [
  {
    uid: '1',
    summary: 'React Workshop',
    start: '2024-01-15T10:00:00Z',
    end: '2024-01-15T12:00:00Z',
    mentor: 'John Doe',
    experienceType: 'workshop',
    difficulty: 'intermediate',
    maxParticipants: 20
  }
];

Dynamic Filtering with Custom Fields

<Calendar 
  events={events}
  filters={({ visibleEvents, filters, setFilters }) => (
    <div className="space-y-2">
      <select 
        value={filters.experienceType || ''} 
        onChange={(e) => setFilters({ ...filters, experienceType: e.target.value })}
        className="border rounded px-2 py-1"
      >
        <option value="">All Experience Types</option>
        <option value="mentorship">Mentorship</option>
        <option value="workshop">Workshop</option>
        <option value="networking">Networking</option>
      </select>
      
      <select 
        value={filters.difficulty || ''} 
        onChange={(e) => setFilters({ ...filters, difficulty: e.target.value })}
        className="border rounded px-2 py-1"
      >
        <option value="">All Difficulties</option>
        <option value="beginner">Beginner</option>
        <option value="intermediate">Intermediate</option>
        <option value="advanced">Advanced</option>
      </select>
      
      <div className="text-sm text-gray-600">
        {visibleEvents.length} events found
      </div>
    </div>
  )}
/>

Accessibility

The calendar is built with accessibility in mind:

  • Keyboard Navigation - Full keyboard support for navigation and interaction
  • ARIA Labels - Proper ARIA attributes for screen readers
  • Focus Management - Logical tab order and focus indicators
  • Semantic HTML - Proper use of table elements and landmarks
  • Color Contrast - Meets WCAG 2.1 AA contrast requirements

Styling

The calendar uses Tailwind CSS classes. You can customize the appearance by:

  • Overriding Tailwind classes in your CSS
  • Using render props for complete control
  • Using template tags for simple text customization
  • Using getEventColor and getEventPill for dynamic styling

Browser Support

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

License

ISC License - see LICENSE file for details.

Contributing

See CONTRIBUTING.md for development guidelines.

Monorepo Workspaces & Dependency Management

This package is part of a monorepo using npm workspaces. All dependencies are hoisted to the root. Always run npm install from the root directory.

React 19 and Testing Library Compatibility

This package uses React 19.1.0. If you see peer dependency warnings with Testing Library, use:

npm install --legacy-peer-deps

This is a temporary workaround until official support is released.

FAQs

Package last updated on 10 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts