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

@lemonadejs/schedule

v5.0.5

Published

A reactive schedule and calendar component for managing events and appointments with drag-and-drop, time ranges, and weekly views

Downloads

313

Readme

LemonadeJS Schedule

A reactive schedule and calendar component for managing events and appointments with drag-and-drop support, time range validation, and multiple view modes.

Compatible with Vanilla JavaScript, LemonadeJS, React, Vue, and Angular.

Features

  • 📅 Multiple View Modes: Day, week, and weekdays views
  • 🔄 Drag & Drop: Move and resize events with mouse
  • Time Validation: Define valid working hours and read-only time ranges
  • 📊 Weekly Schedule: Create recurring weekly schedules without specific dates
  • ⌨️ Keyboard Support: Arrow keys, delete, copy/paste (Ctrl+C/V), undo/redo (Ctrl+Z/Y)
  • 🎨 Customizable: Event colors, grid intervals, and overlap control
  • Accessibility: Full keyboard navigation support
  • 📱 Responsive: Adapts to different screen sizes

Installation

NPM

npm install @lemonadejs/schedule

CDN

<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/schedule/dist/index.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/schedule/dist/style.min.css" />

Basic Usage

Simple Schedule

import Schedule from '@lemonadejs/schedule';

const root = document.getElementById('schedule');

const schedule = Schedule(root, {
    type: 'week',
    value: '2025-01-15',
    data: [
        {
            title: 'Team Meeting',
            start: '09:00',
            end: '10:00',
            date: '2025-01-15',
            color: '#3f51b5'
        },
        {
            title: 'Lunch Break',
            start: '12:00',
            end: '13:00',
            date: '2025-01-15',
            color: '#4caf50'
        }
    ]
});

Weekly Recurring Schedule

Perfect for class schedules, gym timetables, or any recurring weekly events:

const schedule = Schedule(root, {
    type: 'weekdays',
    weekly: true,
    grid: 30, // 30-minute intervals
    data: [
        {
            title: 'Math Class',
            start: '09:00',
            end: '10:30',
            weekday: 1, // Monday (0=Sunday, 1=Monday, ..., 6=Saturday)
            color: '#2196f3'
        },
        {
            title: 'Physics Lab',
            start: '14:00',
            end: '16:00',
            weekday: 3, // Wednesday
            color: '#ff9800'
        }
    ]
});

School Schedule with Working Hours

const schedule = Schedule(root, {
    type: 'weekdays',
    weekly: true,
    grid: 30,
    validRange: ['08:00', '18:00'], // Only show 8 AM to 6 PM
    readOnlyRange: [
        ['08:00', '09:00'], // Before school
        ['12:00', '13:00'], // Lunch break
        ['17:00', '18:00']  // After school
    ],
    overlap: false, // Prevent overlapping classes
    data: []
});

Framework Integration

React

The Schedule component includes a React wrapper for seamless integration.

Installation

npm install @lemonadejs/schedule react

Basic React Example

import React, { useRef } from 'react';
import Schedule from '@lemonadejs/schedule/dist/react';
import '@lemonadejs/schedule/dist/style.css';

export default function App() {
    const scheduleRef = useRef();

    const events = [
        {
            title: 'Team Meeting',
            start: '09:00',
            end: '10:00',
            date: '2025-01-15',
            color: '#3f51b5'
        },
        {
            title: 'Lunch',
            start: '12:00',
            end: '13:00',
            date: '2025-01-15',
            color: '#4caf50'
        }
    ];

    return (
        <div style={{ width: '100%', height: '600px' }}>
            <Schedule
                ref={scheduleRef}
                type="week"
                value="2025-01-15"
                data={events}
                grid={30}
                oncreate={(instance, events) => {
                    console.log('Event created:', events);
                }}
            />
        </div>
    );
}

React with State Management

import React, { useRef, useState } from 'react';
import Schedule from '@lemonadejs/schedule/dist/react';
import '@lemonadejs/schedule/dist/style.css';

export default function ScheduleComponent() {
    const scheduleRef = useRef();
    const [events, setEvents] = useState([
        {
            title: 'Morning Standup',
            start: '09:00',
            end: '09:30',
            date: '2025-01-15',
            color: '#2196f3'
        }
    ]);

    const handleCreateEvent = (instance, newEvents) => {
        console.log('New event created:', newEvents);
        // Update your state or backend
        setEvents([...instance.data]);
    };

    const handleDeleteEvent = (instance, event) => {
        console.log('Event deleted:', event);
        setEvents([...instance.data]);
    };

    const addNewEvent = () => {
        if (scheduleRef.current) {
            scheduleRef.current.addEvents({
                title: 'New Event',
                start: '14:00',
                end: '15:00',
                date: '2025-01-15',
                color: '#ff9800'
            });
        }
    };

    return (
        <div>
            <button onClick={addNewEvent}>Add Event</button>
            <div style={{ width: '100%', height: '600px' }}>
                <Schedule
                    ref={scheduleRef}
                    type="week"
                    value="2025-01-15"
                    data={events}
                    oncreate={handleCreateEvent}
                    ondelete={handleDeleteEvent}
                />
            </div>
        </div>
    );
}

React Weekly Schedule (Gym/School)

import React, { useRef } from 'react';
import Schedule from '@lemonadejs/schedule/dist/react';
import '@lemonadejs/schedule/dist/style.css';

export default function GymSchedule() {
    const scheduleRef = useRef();

    const classes = [
        {
            title: 'Yoga',
            start: '07:00',
            end: '08:00',
            weekday: 1, // Monday
            color: '#9c27b0'
        },
        {
            title: 'Spinning',
            start: '18:00',
            end: '19:00',
            weekday: 2, // Tuesday
            color: '#f44336'
        },
        {
            title: 'Yoga',
            start: '07:00',
            end: '08:00',
            weekday: 3, // Wednesday
            color: '#9c27b0'
        },
        {
            title: 'CrossFit',
            start: '18:00',
            end: '19:30',
            weekday: 4, // Thursday
            color: '#ff5722'
        }
    ];

    return (
        <div style={{ width: '100%', height: '600px' }}>
            <Schedule
                ref={scheduleRef}
                type="weekdays"
                weekly={true}
                grid={30}
                data={classes}
                validRange={['06:00', '21:00']}
                overlap={false}
            />
        </div>
    );
}

Vue

The Schedule component also includes a Vue 3 wrapper.

Installation

npm install @lemonadejs/schedule vue

Basic Vue Example

<template>
    <div style="width: 100%; height: 600px">
        <Schedule
            type="week"
            :value="currentDate"
            :data="events"
            :grid="30"
            @oncreate="handleCreate"
        />
    </div>
</template>

<script>
import { ref } from 'vue';
import Schedule from '@lemonadejs/schedule/dist/vue';
import '@lemonadejs/schedule/dist/style.css';

export default {
    name: 'App',
    components: {
        Schedule
    },
    setup() {
        const currentDate = ref('2025-01-15');
        const events = ref([
            {
                title: 'Team Meeting',
                start: '09:00',
                end: '10:00',
                date: '2025-01-15',
                color: '#3f51b5'
            },
            {
                title: 'Lunch',
                start: '12:00',
                end: '13:00',
                date: '2025-01-15',
                color: '#4caf50'
            }
        ]);

        const handleCreate = (instance, newEvents) => {
            console.log('Event created:', newEvents);
        };

        return {
            currentDate,
            events,
            handleCreate
        };
    }
};
</script>

Vue with Reactive State

<template>
    <div>
        <button @click="addEvent">Add Event</button>
        <button @click="goToNextWeek">Next Week</button>
        <button @click="goToPrevWeek">Previous Week</button>

        <div style="width: 100%; height: 600px">
            <Schedule
                ref="scheduleRef"
                type="week"
                :value="currentDate"
                :data="events"
                :validRange="['08:00', '18:00']"
                @oncreate="handleCreate"
                @ondelete="handleDelete"
            />
        </div>
    </div>
</template>

<script>
import { ref } from 'vue';
import Schedule from '@lemonadejs/schedule/dist/vue';
import '@lemonadejs/schedule/dist/style.css';

export default {
    name: 'ScheduleComponent',
    components: {
        Schedule
    },
    setup() {
        const scheduleRef = ref(null);
        const currentDate = ref('2025-01-15');
        const events = ref([
            {
                title: 'Morning Standup',
                start: '09:00',
                end: '09:30',
                date: '2025-01-15',
                color: '#2196f3'
            }
        ]);

        const handleCreate = (instance, newEvents) => {
            console.log('Event created:', newEvents);
            events.value = [...instance.data];
        };

        const handleDelete = (instance, event) => {
            console.log('Event deleted:', event);
            events.value = [...instance.data];
        };

        const addEvent = () => {
            scheduleRef.value?.current?.addEvents({
                title: 'New Event',
                start: '14:00',
                end: '15:00',
                date: currentDate.value,
                color: '#ff9800'
            });
        };

        const goToNextWeek = () => {
            scheduleRef.value?.current?.next();
        };

        const goToPrevWeek = () => {
            scheduleRef.value?.current?.prev();
        };

        return {
            scheduleRef,
            currentDate,
            events,
            handleCreate,
            handleDelete,
            addEvent,
            goToNextWeek,
            goToPrevWeek
        };
    }
};
</script>

Vue Weekly Schedule

<template>
    <div style="width: 100%; height: 600px">
        <Schedule
            type="weekdays"
            :weekly="true"
            :grid="30"
            :data="classes"
            :validRange="['06:00', '21:00']"
            :overlap="false"
        />
    </div>
</template>

<script>
import { ref } from 'vue';
import Schedule from '@lemonadejs/schedule/dist/vue';
import '@lemonadejs/schedule/dist/style.css';

export default {
    name: 'GymSchedule',
    components: {
        Schedule
    },
    setup() {
        const classes = ref([
            {
                title: 'Yoga',
                start: '07:00',
                end: '08:00',
                weekday: 1, // Monday
                color: '#9c27b0'
            },
            {
                title: 'Spinning',
                start: '18:00',
                end: '19:00',
                weekday: 2, // Tuesday
                color: '#f44336'
            },
            {
                title: 'Yoga',
                start: '07:00',
                end: '08:00',
                weekday: 3, // Wednesday
                color: '#9c27b0'
            },
            {
                title: 'CrossFit',
                start: '18:00',
                end: '19:30',
                weekday: 4, // Thursday
                color: '#ff5722'
            }
        ]);

        return {
            classes
        };
    }
};
</script>

API Reference

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | type | 'week' \| 'day' \| 'weekdays' | 'week' | View type for the schedule | | weekly | boolean | false | Enable weekly mode (recurring events without dates) | | value | string | Current date | Initial date in ISO format (YYYY-MM-DD) | | data | Event[] | [] | Array of events | | grid | number | 15 | Time grid interval in minutes | | overlap | boolean | false | Allow overlapping events | | validRange | string[] | [] | Valid time range ['HH:MM', 'HH:MM'] (hides other hours) | | readOnlyRange | string[] \| string[][] | [] | Read-only time ranges (single or multiple) | | onbeforecreate | function | - | Callback before creating events | | oncreate | function | - | Callback after creating events | | onchangeevent | function | - | Callback when event properties change | | ondelete | function | - | Callback when event is deleted | | onerror | function | - | Callback for error handling |

Event Object

{
    title: string;           // Event title
    start: string;          // Start time in HH:MM format
    end: string;            // End time in HH:MM format
    date?: string;          // Date in ISO format (YYYY-MM-DD) - for normal mode
    weekday?: number;       // Day of week (0-6, Sunday is 0) - for weekly mode
    color?: string;         // Hex color code
    description?: string;   // Event description
    location?: string;      // Event location
    guests?: string;        // Guest list
    readonly?: boolean;     // Make event read-only
    guid?: string;         // Unique identifier (auto-generated)
}

Methods

// Add events
schedule.addEvents(event | events[]);

// Update event
schedule.updateEvent(guid, { title: 'New Title', start: '10:00' });

// Delete events
schedule.deleteEvents(guid | guid[] | event | event[]);

// Navigation
schedule.next();  // Next week/day
schedule.prev();  // Previous week/day

// History
schedule.undo();  // Undo last change
schedule.redo();  // Redo last change

// Time ranges
schedule.setRange(['08:00', '18:00']);
schedule.setReadOnly([['00:00', '08:00'], ['18:00', '23:59']]);

// Data management
schedule.getData();           // Get all events
schedule.setData(events);     // Set events data
schedule.getEvent(guid);      // Get event DOM element

// Render
schedule.render();  // Re-render schedule

Examples

Example 1: Office Hours Schedule

const officeSchedule = Schedule(root, {
    type: 'week',
    value: '2025-01-20',
    grid: 30,
    validRange: ['08:00', '18:00'],
    readOnlyRange: ['12:00', '13:00'], // Lunch break
    oncreate: (instance, events) => {
        console.log('New appointment created:', events);
    },
    onerror: (instance, message) => {
        alert(message);
    }
});

Example 2: Gym Class Schedule

const gymSchedule = Schedule(root, {
    type: 'weekdays',
    weekly: true,
    grid: 60,
    data: [
        {
            title: 'Yoga',
            start: '07:00',
            end: '08:00',
            weekday: 1, // Monday
            color: '#9c27b0'
        },
        {
            title: 'Spinning',
            start: '18:00',
            end: '19:00',
            weekday: 2, // Tuesday
            color: '#f44336'
        },
        {
            title: 'Yoga',
            start: '07:00',
            end: '08:00',
            weekday: 3, // Wednesday
            color: '#9c27b0'
        }
    ]
});

Example 3: Interactive Event Creation

const schedule = Schedule(root, {
    type: 'week',
    value: '2025-01-20',
    onbeforecreate: (instance, events) => {
        // Validate event before creation
        const event = Array.isArray(events) ? events[0] : events;

        if (event.start < '09:00') {
            alert('Cannot create events before 9 AM');
            return false; // Cancel creation
        }

        return true; // Allow creation
    },
    oncreate: (instance, events) => {
        // Event was created successfully
        console.log('Event created:', events);
    }
});

Example 4: Multiple Read-Only Time Ranges

const schedule = Schedule(root, {
    type: 'week',
    value: '2025-01-20',
    validRange: ['00:00', '23:59'], // Show all hours
    readOnlyRange: [
        ['00:00', '08:00'], // Early morning
        ['12:00', '13:00'], // Lunch
        ['18:00', '23:59']  // Evening
    ]
});

Keyboard Shortcuts

| Shortcut | Action | |----------|--------| | Ctrl+Z | Undo last change | | Ctrl+Y | Redo last change | | Ctrl+C | Copy selected events | | Ctrl+V | Paste copied events | | Delete | Delete selected events | | Arrow Keys | Navigate between events |

Styling

The schedule uses CSS custom properties for easy customization:

.lm-schedule {
    --lm-schedule-border: #e0e0e0;
    --lm-schedule-bg: #ffffff;
    --lm-schedule-text: #000000;
}

/* Customize event colors */
.lm-schedule-item {
    border-radius: 4px;
    padding: 4px 8px;
}

/* Disabled time slots */
.lm-schedule-disabled {
    background-color: #f5f5f5;
    cursor: not-allowed;
}

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

License

This component is part of the LemonadeJS plugins library.

Links

Contributing

We welcome contributions! Please see the main repository for contribution guidelines.