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

works-calendar

v2.0.0

Published

Drop-in embeddable React calendar with filtering, scheduling workflows, themes, and external-form support.

Readme

WorksCalendar

Embeddable scheduling engine for teams, assets, and operations. Drop it into a React app and get a working calendar, dispatch board, request queue, and approval pipeline — all driven by one config object.

Website: workscalendar.com · Repository: github.com/workscalendar/calendarthatworks

WorksCalendar provides the building blocks for advanced scheduling. Applications are expected to configure and extend these systems to fit their workflow.

Core features (fully working)

  • Multiple calendar modes: month, week, day, agenda, schedule, timeline
  • Event lifecycle states (draft → pending → approved → scheduled → completed) surfaced everywhere
  • Conflict engine with hard-block / soft-warning modes, live inline feedback in the editor, and conflict highlights on the calendar
  • Request queue with approve / deny / finalize / revoke actions wired to a tamper-evident audit chain
  • Dispatch readiness board with per-row "Why?" breakdown — driver / pilot / pool shortfalls explained in plain English
  • Schema-driven filtering, saved views, themeable UI with packaged themes
  • Backend-agnostic: feed events via events prop, fetchEvents callback, or the built-in Supabase connector
  • Written in strict TypeScript; ships with generated .d.ts so consumer types stay in lockstep with the implementation

Extensible systems (configurable)

  • Approval workflow DSL — multi-tier approvals, SLA timers + escalation, parallel branches with quorum joins (requireAll / requireAny / requireN), and pluggable notification channels (Slack, email, webhook, or your own adapter)
  • Resource pools with a query DSL (capability + distance filters), pool resolution strategies, and per-pool readiness evaluation
  • Requirement templates — declare per-event-type role / pool needs and let evaluateRequirements gate the booking
  • Custom resource types, roles, labels, and capability schemas

Profiles

WorksCalendar ships starter profiles so the same engine fits multiple industries via configuration:

| Profile | Resource label | Event label | Default roles | | ------------------- | -------------- | ----------- | ------------------------------------------------------------ | | air_medical | Aircraft | Mission | Pilot in Command, Flight Paramedic, Flight Nurse, Dispatcher | | aviation | Aircraft | Flight | Pilot in Command, Second in Command, Dispatcher | | trucking | Truck | Load | Driver, Dispatcher | | equipment_rental | Equipment | Rental | Yard Attendant, Delivery Driver, Dispatcher | | scheduling | Room | Booking | Organizer, Attendee | | custom | Resource | Event | (none) |

Apply a profile via the setup wizard's "What are you scheduling?" step or programmatically:

import { applyProfilePreset } from 'works-calendar';

const config = applyProfilePreset('air_medical');
// config.labels.resource === 'Aircraft'
// config.roles → [pilot-in-command, flight-paramedic, flight-nurse, …]

Switching profiles changes terminology and defaults without changing logic — the conflict engine, requirement evaluator, and approval reducer all read off the same config.

New here? Start with the Setup guide

Plain-language walkthrough from npm install to a working, connected calendar — pick only the steps you need.

Installation

npm install works-calendar

Peer dependencies: React 18 or 19 is required. Install separately if you don't have them:

npm install react react-dom

Bundler requirement: WorksCalendar is an ES module library. It works out of the box with Vite, webpack, Parcel, Rollup, and any other modern bundler. It does not work when loaded via a plain <script> tag alongside a separately-bundled React app — that creates two React instances and breaks hooks. If you need script-tag usage, load React itself from a CDN first and import this package via an ESM-capable <script type="module">.

Quick start

CSS required. The calendar renders unstyled without it. Import the base styles once, anywhere in your app.

import { WorksCalendar } from 'works-calendar';
import 'works-calendar/styles';        // required — base styles
import 'works-calendar/styles/ocean';  // optional — theme

export function App() {
  const events = [
    {
      id: 'shift-1',
      title: 'Morning shift',
      start: new Date('2026-05-05T08:00:00'),
      end:   new Date('2026-05-05T16:00:00'),
      resource: 'emp-alice',   // links to an employee / resource id
      category: 'operations',
    },
  ];

  return (
    <WorksCalendar
      events={events}
      initialView="week"
      theme="ocean"
    />
  );
}

Event shape

interface WorksCalendarEvent {
  id?:            string;
  title:          string;
  start:          Date | string;       // ISO string or Date object
  end?:           Date | string;
  allDay?:        boolean;
  resource?:      string;              // employee / asset / resource id
  category?:      string;
  color?:         string;              // CSS colour overrides colorRules
  status?:        'confirmed' | 'tentative' | 'cancelled';
  lifecycle?:     'draft' | 'pending' | 'approved' | 'scheduled' | 'completed';
  visualPriority?: 'muted' | 'high';
  rrule?:         string;              // RFC 5545 RRULE string
  exdates?:       Array<Date | string>;
  meta?:          Record<string, unknown>; // arbitrary host data
}

resource (not resourceId) is the field that links an event to an employee or asset. The value should match the id of a record in your employees prop array.

Key props

| Prop | Type | Description | |------|------|-------------| | events | WorksCalendarEvent[] | Static event array | | fetchEvents | () => Promise<WorksCalendarEvent[]> | Dynamic loader — called on mount and view change | | employees | EmployeeRecord[] | Team members shown in scheduling views | | initialView | 'month' \| 'week' \| 'day' \| 'agenda' \| 'schedule' \| 'map' | Starting view | | theme | string | Theme name (see Theming) | | role | 'owner' \| 'scheduler' \| 'viewer' | Permission level — 'owner' unlocks settings & config | | devMode | boolean | Local development only. When true, treats the user as an owner regardless of role, bypassing every role check. Never pass true in production. | | density | 'comfortable' \| 'compact' | Force the compact chrome (narrow toolbar, hidden right panel) regardless of container width. Default lets the calendar's own width drive the layout via container queries. | | calendarId | string | Namespace key for localStorage persistence (default: 'default') | | onEventSave | (event) => void | Called when a user saves an event in the editor | | onEventDelete | (id) => void | Called when a user deletes an event | | onEventMove | (event, newStart, newEnd) => void | Called after drag-to-move | | filterSchema | FilterField[] | Custom filter fields shown in the filter bar | | colorRules | UnknownRecord[] | Rules that map event fields to colours | | groupBy | string \| GroupByInput | Group events into rows by field |

For the full prop list see the Setup guide or the TypeScript types in dist/index.d.ts.

Supabase connector

Pass your Supabase credentials and events are loaded and persisted automatically — no custom fetchEvents needed:

<WorksCalendar
  supabaseUrl={import.meta.env.VITE_SUPABASE_URL}
  supabaseKey={import.meta.env.VITE_SUPABASE_KEY}
  supabaseTable="events"
/>

Requires npm install @supabase/supabase-js.

Custom backend

Supply a fetchEvents function for any other source:

<WorksCalendar
  fetchEvents={async () => {
    const res = await fetch('/api/events');
    return res.json();
  }}
  onEventSave={async (event) => {
    await fetch('/api/events', { method: 'POST', body: JSON.stringify(event) });
  }}
/>

Examples

Run the local example suite:

npm install
npm run examples

Example catalogs:

Documentation

Theming

Base styles — required:

import 'works-calendar/styles';

Optional theme override:

import 'works-calendar/styles/ocean';

Included packaged themes: aviation, soft, minimal, corporate, forest, ocean.

If you use a CSS bundler that doesn't handle package exports, import by file path:

import 'works-calendar/dist/style.css';
import 'works-calendar/dist/themes/ocean.css';

Customizing the chrome

The calendar's left icon rail and right panel are two slots embedders can extend without forking. The stock chrome (saved-views, focus filters, settings, region map, crew on shift) keeps stable positions; your content lands after the built-ins.

import {
  WorksCalendar,
  RightPanelSection,
  type LeftRailAction,
} from 'works-calendar';
import { Bell, Download } from 'lucide-react';

const railExtras: LeftRailAction[] = [
  {
    id: 'export',
    label: 'Export',
    hint: 'Download visible events as CSV',
    icon: <Download size={18} aria-hidden="true" />,
    onClick: () => exportCsv(),
  },
  {
    id: 'notifications',
    label: 'Notifications',
    icon: <Bell size={18} aria-hidden="true" />,
    onClick: () => openNotificationDrawer(),
  },
];

<WorksCalendar
  events={events}
  leftRailExtras={railExtras}
  rightPanelExtras={
    <>
      <RightPanelSection title="Open tickets">
        <MyTicketWidget />
      </RightPanelSection>
      <RightPanelSection title="Compliance">
        <MyComplianceWidget />
      </RightPanelSection>
    </>
  }
/>

leftRailExtras takes LeftRailAction[] (id / label / icon / optional hint / optional active / onClick). Built-in ids (saved-views, focus, settings) are reserved — extras using them are filtered out so a typo can't shadow the chrome.

rightPanelExtras takes any ReactNode. Wrap each section in <RightPanelSection title="…"> so theme tokens + section dividers match the stock content above.

Optional view plugins

Some views are shipped behind optional peer dependencies so the core bundle stays slim. They are auto-detected at runtime — install the peers and the view renders; skip them and a graceful install hint is shown instead.

Map view

Plot events with coordinates on a MapLibre basemap.

npm install maplibre-gl react-map-gl
import { WorksCalendar } from 'works-calendar';

const events = [
  {
    id: 'kphx-1',
    title: 'Phoenix arrival',
    start: new Date(),
    meta: { coords: { lat: 33.43, lon: -112.01 } },
  },
];

<WorksCalendar events={events} initialView="map" />;

Coordinates are read from event.meta.coords ({ lat, lon }, matching the LocationData shape) — event.meta.lat + event.meta.lon/meta.lng is also accepted as a loose convenience form. Marker color resolves through the same colorRules as every other view.

MapView is also exported standalone for custom layouts:

import { MapView } from 'works-calendar';

<MapView
  events={events}
  onEventClick={ev => console.log(ev)}
  mapStyle="https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY"
/>;

The default mapStyle is MapLibre's free demo tile server — fine for local development; production hosts should pass their own style URL (MapTiler, Stadia, Protomaps, self-hosted, …).

Release & project status

License

MIT. See LICENSE.