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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@tugkanpilka/calendar

v1.0.0

Published

A highly modular, infinite scrolling calendar component built with React and TypeScript

Readme

@docbook/calendar

A highly modular, infinite scrolling calendar component built with React and TypeScript. Follows SOLID principles and is designed to be published as an NPM package.

Features

  • Infinite Scrolling: Built-in support for infinite scrolling with IntersectionObserver
  • Render Props: Maximum customization through render props pattern
  • Theme Support: CSS variables for easy theming
  • TypeScript: Fully typed with comprehensive type definitions
  • Performance: Optimized with React.memo and efficient rendering
  • Modular: Clean architecture with separated concerns
  • Zero Dependencies: No internal component dependencies, pure React

Installation

npm install @docbook/calendar
# or
yarn add @docbook/calendar

Peer Dependencies

{
  "react": "^18.0.0 || ^19.0.0",
  "react-dom": "^18.0.0 || ^19.0.0",
  "date-range-utils": "^1.0.16",
  "date-fns": "^4.1.0"
}

Basic Usage

import { Calendar } from '@docbook/calendar';
import type { DateInfo, MonthInfo } from 'date-range-utils';
import { DateRange, StandardDateGenerationStrategy, MonthGroupingStrategy } from 'date-range-utils';

function MyCalendar() {
  const months: MonthInfo<DateInfo>[] = useMemo(() => {
    const dateFactory = (date: Date): DateInfo => ({
      date,
      weekNumber: getWeek(date),
    });

    return new DateRange<DateInfo, MonthInfo<DateInfo>>(
      new Date(2025, 0, 1),
      new Date(2025, 11, 31),
    )
      .create(new StandardDateGenerationStrategy(dateFactory))
      .group(new MonthGroupingStrategy());
  }, []);

  const renderDay = (dateInfo: DateInfo) => {
    return (
      <div>
        {dateInfo.date.getDate()}
      </div>
    );
  };

  return (
    <Calendar
      months={months}
      renderDay={renderDay}
    />
  );
}

Infinite Scrolling

The Calendar component supports infinite scrolling out of the box:

<Calendar
  months={months}
  renderDay={renderDay}
  onLoadMore={async () => {
    // Load more months
    await loadMoreMonths();
  }}
  hasMore={hasMoreMonths}
  isLoading={isLoading}
/>

Custom Rendering

All components support render props for maximum customization:

<Calendar
  months={months}
  renderDay={(dateInfo) => (
    <Day
      dateInfo={dateInfo}
      onDateClick={handleDateClick}
      renderDate={(dayNumber) => (
        <span className="custom-day">{dayNumber}</span>
      )}
      renderIndicator={({ isDotted, dotType }) => (
        isDotted ? <CustomIndicator type={dotType} /> : null
      )}
    />
  )}
/>

Components

Calendar

Main orchestrator component.

Props:

  • months: Array of month information (required)
  • renderDay: Function to render day cells (required)
  • renderWeekNumber: Function to render week numbers (optional)
  • renderMonthHeader: Function to render month headers (optional)
  • showWeekNumbers: Whether to show week numbers (default: true)
  • onLoadMore: Infinite scroll callback (optional)
  • hasMore: Whether there are more items to load (optional)
  • isLoading: Whether currently loading (optional)
  • className: Additional CSS class name (optional)
  • headerClassName: Additional CSS class for header (optional)
  • bodyClassName: Additional CSS class for body (optional)

Day

Day cell component with render props support.

Props:

  • dateInfo: Date information
  • onDateClick: Callback when date is clicked
  • onClick: Additional click handler
  • isSelected: Whether the day is selected
  • isRounded: Whether the day has rounded corners
  • isMarked: Whether the day is marked (e.g., today)
  • isDotted: Whether the day has a dot indicator
  • dotType: Type of dot indicator ('primary' | 'secondary')
  • renderDate: Custom render function for date display
  • renderIndicator: Custom render function for indicator
  • className: Additional CSS class name

Week

Week number component with render props support.

Props:

  • text: Week number text (e.g., "W1")
  • isMultiMonth: Whether the week spans multiple months
  • onClick: Click handler
  • isDotted: Whether the week has a dot indicator
  • isFocused: Whether the week is focused
  • dotType: Type of dot indicator
  • renderText: Custom render function for text display
  • renderIndicator: Custom render function for indicator
  • className: Additional CSS class name

MonthHeader

Month header component with render props support.

Props:

  • text: Month name text
  • onClick: Click handler
  • isFocused: Whether the month is focused
  • isDotted: Whether the month has a dot indicator
  • dotType: Type of dot indicator
  • renderText: Custom render function for text display
  • renderIndicator: Custom render function for indicator
  • className: Additional CSS class name

Utilities

Date Formatting

import { formatCalendarDate } from '@docbook/calendar';

const dayString = formatCalendarDate.day(new Date()); // "2025-01-15"
const weekString = formatCalendarDate.week(new Date(), 3); // "2025-W3"
const monthString = formatCalendarDate.month(2025, "January"); // "2025-M1"

Grid Utilities

import { isWeekNumberCell, calculateGridIndex } from '@docbook/calendar';

const isWeekNumber = isWeekNumberCell(7); // true (index 7 is week number column)
const index = calculateGridIndex(2, 3, 8); // 19 (row 2, col 3, 8 columns)

Scroll Utilities

import { scrollToDate, findDateElement } from '@docbook/calendar';

await scrollToDate('2025-01-15', containerRef.current);
const element = findDateElement('2025-01-15', containerRef.current);

Hooks

useCalendarItems

Generic hook to generate props for calendar components.

import { useCalendarItems } from '@docbook/calendar';

const { getDayProps, getWeekNumberProps, getMonthHeaderProps } = useCalendarItems({
  currentDate: '2025-01-15',
  setCurrentDate: (date) => console.log(date),
  getIndicatorProps: (formattedDate) => ({
    isDotted: true,
    dotType: 'primary',
  }),
});

useInfiniteScroll

Hook for infinite scrolling support.

import { useInfiniteScroll } from '@docbook/calendar';

const { sentinelRef } = useInfiniteScroll({
  hasMore: true,
  isLoading: false,
  onLoadMore: async () => {
    await loadMore();
  },
  scrollRef: containerRef,
});

useScrollToDate

Hook for scrolling to a specific date.

import { useScrollToDate } from '@docbook/calendar';

const { scrollTo, findElement } = useScrollToDate({
  scrollRef: containerRef,
});

await scrollTo('2025-01-15');

Theming

The component uses CSS variables for theming. Override them in your CSS:

:root {
  --calendar-bg-color: #F5F6F8;
  --day-cell-height: 80px;
  --day-cell-marked-bg: #ef4444;
  --indicator-primary-color: #ef4444;
  /* ... more variables */
}

TypeScript

All components and utilities are fully typed. Import types as needed:

import type {
  CalendarProps,
  DayProps,
  WeekProps,
  MonthHeaderProps,
} from '@docbook/calendar';

Styling

The component uses CSS modules. Import the compiled CSS:

import '@docbook/calendar/dist/styles.css';

Or use CSS modules in your build configuration.

Migration from Monorepo

If you're migrating from the monorepo version:

  1. Replace @shared/ui/organisms/Calendar imports with @docbook/calendar
  2. Remove @shared/styles SCSS imports (variables are now included)
  3. Update any internal component dependencies (now using render props)
  4. Ensure peer dependencies are installed

License

MIT

Contributing

Contributions are welcome! Please read our contributing guidelines first.