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

@zhunam/calendar

v2.0.0

Published

A calendar library for Angular: a signal-based in-memory event store with real RRULE recurrence, an optional Google Calendar connector, and an optional month/week/day UI component. The core works completely on its own; `/google` and `/calendar-ui` are

Readme

@zhunam/calendar

A calendar library for Angular: a signal-based in-memory event store with real RRULE recurrence, an optional Google Calendar connector, and an optional month/week/day UI component. The core works completely on its own; /google and /calendar-ui are independent add-ons, use either, both, or neither.

Entry points

| Entry point | Required? | What it gives you | | ------------------------- | -------- | ------------------------------------------------------- | | @zhunam/calendar | Always | CalendarEvent<T>, CalendarStore, CalendarValidationError | | @zhunam/calendar/google | Optional | GoogleCalendarConnector: real Google Calendar read/write | | @zhunam/calendar/calendar-ui | Optional | CalendarBoard: a month/week/day UI component |

Installation

npm install @zhunam/calendar

angular-calendar, rrule, and date-fns ship bundled as internal dependencies of this library. You never install or configure them yourself, regardless of which entry points you use.

Usage

Core

import { CalendarStore, type CalendarEvent } from '@zhunam/calendar';

const store = new CalendarStore();
const event: CalendarEvent = { id: '1', title: 'Standup', start: new Date(), end: new Date() };
store.addEvent(event);

const upcoming = store.eventsInRange(new Date(), new Date(Date.now() + 7 * 86400000));

/google

import { Component } from '@angular/core';
import { GoogleCalendarConnector } from '@zhunam/calendar/google';

@Component({ selector: 'app-agenda' })
export class AgendaPage {
  private readonly connector = new GoogleCalendarConnector();

  async loadEvents() {
    await this.connector.connect(GOOGLE_CLIENT_ID);
    return this.connector.listEvents({ start: new Date(), end: nextWeek });
  }
}

/calendar-ui

<lib-calendar-board
  [events]="events()"
  [(viewMode)]="viewMode"
  [(viewDate)]="viewDate"
  (eventClick)="onEventClick($event)"
  (eventReschedule)="onEventReschedule($event)"
/>

angular-calendar ships its own base stylesheet; CalendarBoard only styles its own toolbar. Import it once in your app (e.g. in angular.json's styles array, or a global stylesheet):

@import 'angular-calendar/css/angular-calendar.css';

API

Core (@zhunam/calendar)

CalendarEvent<T>

| Field | Type | Description | | ------------- | ---------- | -------------------------------------------------------------- | | id | string | Unique identifier. You assign it; nothing here generates one. | | title | string | Display title. | | start | Date | Start of the event, inclusive. | | end | Date | End of the event, exclusive ([start, end)). | | allDay | boolean? | Whether this is an all-day event. Default false. | | recurrence | string? | RRULE string (RFC 5545), e.g. 'FREQ=WEEKLY;COUNT=5'. The RRULE: prefix is optional. | | data | T? | Your own domain data attached to the event. Never read or written by this library. |

CalendarStore

| Member | Type | Description | | --------------------------------------- | ------------------------------------- | ------------------------------------------------------------------ | | events | Signal<CalendarEvent[]> | Every event in the store, in insertion order. | | addEvent(event) | void | Throws CalendarValidationError if invalid or the id already exists. | | updateEvent(id, changes) | void | Validates the merged result, not changes alone. No-op if id isn't found. | | deleteEvent(id) | void | No-op if id isn't found. | | eventsInRange(start, end) | Signal<CalendarEvent[]> | Expands recurring events into their real occurrences via rrule. | | findConflicts(event) | Signal<CalendarEvent[]> | Other events (including occurrences of recurring ones) overlapping event. |

@zhunam/calendar/google

GoogleCalendarConnector

| Member | Type | Description | | ------------------------------ | -------------------------------------- | ----------------------------------------------------------------------- | | isConnected | Signal<boolean> | Whether a valid access token is currently held. | | connect(clientId) | Promise<void> | Shows Google's consent screen. clientId is a public OAuth Client ID, not a secret. | | disconnect() | void | Clears local state and best-effort revokes the token with Google. | | listEvents(range) | Promise<CalendarEvent[]> | Reads events from the user's primary calendar, paginated automatically. | | createEvent(event) | Promise<CalendarEvent<T>> | Creates an event. event.data is never sent to Google. | | updateEvent(id, changes) | Promise<CalendarEvent<T>> | Partial update via a real PATCH, only the changed fields are sent. | | deleteEvent(id) | Promise<void> | Deleting an already-deleted event resolves as success, not an error. |

Errors

| Error | Thrown when | | --------------------------------- | -------------------------------------------------------------------------------- | | CalendarValidationError | An event has an invalid start/end, end before start, or an unparseable recurrence (core CalendarStore, or createEvent()/updateEvent() here). | | GoogleApiError | Google responds with a non-2xx status (a 410 on deleteEvent() doesn't count, see above). status carries the real HTTP code. | | GoogleCalendarNotConnectedError | Any method other than connect() is called while isConnected() is false, before any request is attempted. |

@zhunam/calendar/calendar-ui

CalendarBoard (<lib-calendar-board>)

| Name | Type | Description | | ---------------------- | -------------------------------------------- | ------------------------------------------------------------------------ | | events | input.required<CalendarEvent<T>[]> | Events to display. A recurring event renders as a single non-draggable, non-resizable card. | | viewMode | model<'month' \| 'week' \| 'day'> | The currently shown view. Default 'month'. | | viewDate | model<Date> | The date the current view is anchored on. | | eventClick | output<CalendarEvent<T>> | Emitted with the original event when its card is clicked. | | eventReschedule | output<{ id, start, end }> | Emitted after a drag or resize. Never emitted for a recurring event. | | visibleRangeChange | output<{ start, end }> | The visible [start, end) range, emitted whenever viewDate/viewMode changes. |

CalendarBoardModule: NgModule wrapper for consumers still on a classic NgModule architecture. The standalone component is still the recommended way to consume it.

Theming

CalendarBoard only styles its own toolbar (nav buttons, view switch, title) via CSS custom properties, part of the shared --zhunam-* namespace used across every @zhunam/* library. Everything else (day cells, event pills, hour grid) comes from angular-calendar's own stylesheet, imported separately, and isn't controlled by these properties:

| Custom property | Default | Description | | --------------------------------------- | -------- | -------------------------------------------------- | | --zhunam-primary | #3b82f6 | Active view-switch button border and text color. | | --zhunam-text | #374151 | Base text color, inherited by the toolbar's buttons and title. | | --zhunam-border | #d1d5db | Nav and view-switch button borders. | | --zhunam-font-family | system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif | Font for the whole component. | | --zhunam-calendar-event-dot-color | #1e90ff | Color of the "has events" dot on month-view days. Deliberately independent from --zhunam-primary: it matches angular-calendar's own default event color, not this toolbar's accent. |

Compatibility

@angular/core ^20.0.0 || ^21.0.0 || ^22.0.0, the only peer dependency across all three entry points. angular-calendar, rrule, and date-fns are bundled dependencies of this library, not peer dependencies: you never pick a version for them yourself.

Behavior limits

  • CalendarStore.eventsInRange()/findConflicts(): a single recurring event contributes at most 1000 occurrences to one call, even against a high-frequency rule with no COUNT/UNTIL.
  • GoogleCalendarConnector.listEvents(): at most 1000 events per call (4 pages of 250).

Both limits exist to keep a single call bounded against pathological input (an unbounded recurrence, an extremely loaded calendar); neither throws when hit, both console.warn() instead, so an incomplete result is discoverable while debugging rather than silently short.

Why this one

A reactive core that's actually useful standalone, no backend, no external provider required to model events, recurrence, and conflicts. Google Calendar sync is opt-in and keeps its access token genuinely unreadable from outside the connector (a real ECMAScript #private field, not just a TypeScript annotation). The optional UI wraps angular-calendar without ever leaking its types into your code, swap the rendering engine later without touching your app.

License

MIT


Built by Ariana Mora · LinkedIn · GitHub