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

@archpublicwebsite/rangepicker

v1.5.17

Published

Custom date range picker component for PBA Hotel Apps

Readme

@archpublicwebsite/rangepicker

npm version license

A lightweight Vue 3 date range picker with Tailwind CSS support, built for Archipelago hotel booking flows.

Overview

@archpublicwebsite/rangepicker gives you a calendar date picker that handles both date-range and single-date selection, per-day pricing, holiday/weekend styling, and desktop, mobile bottom-sheet, and full-screen display variants.

Import Datepicker (the default export) to get a complete input + calendar out of the box, or compose Rangepicker directly if you need to manage the trigger element and open state yourself.

Features

  • Range or single-date selection in one component (singleMode)
  • Tailwind utility classes prefixed with arch- so they never collide with your app's own Tailwind, Bootstrap, or Vuetify styles
  • Desktop dropdown, mobile bottom sheet, and full-screen variants, with viewport-aware positioning and scroll clamping for tall content
  • Per-day pricing: currency formatting, a loading skeleton, and a low-price legend
  • Configurable weekend days and holiday markers
  • Themeable via HEX color props
  • Full TypeScript definitions
  • Keyboard and screen-reader support
  • Works in Vue 3 and Nuxt 3

Installation

pnpm add @archpublicwebsite/rangepicker vue dayjs @vueuse/core

vue, dayjs, and @vueuse/core are peer dependencies — install them alongside the package.

Quick start

Import the component and its stylesheet:

<script setup lang="ts">
import { ref } from 'vue'
import Datepicker from '@archpublicwebsite/rangepicker'
import '@archpublicwebsite/rangepicker/style.css'

const dates = ref('')
</script>

<template>
  <Datepicker v-model="dates" primary-color="#3b82f6" placeholder="Check in / Check out" />
</template>

What's new

The most recent release adds:

  • weekendDays — pick which days of the week render as "weekend" (red day number). Defaults to ['saturday', 'sunday'].
  • showOtherMonthDays — set to false to blank out leading/trailing adjacent-month cells instead of rendering them.
  • mobileTopPosition default behavior changed — left unset, the mobile popup now anchors below the trigger input, matching desktop, instead of always centering. Pass 'center' to keep the old behavior.
  • variant="full" — a full-screen takeover variant, alongside desktop/mobile.
  • monthChange event — fires with all currently visible months on prev/next navigation.
  • Desktop popups now clamp to the viewport height and scroll, matching mobile — tall calendars and price lists no longer clip.

See CHANGELOG.md for the full release history.

Public API

Components

| Export | Notes | | --- | --- | | Datepicker | Default export. Range or single-date via singleMode. Use this unless you need advanced composition. | | Rangepicker | The underlying calendar/popup Datepicker wraps. Use it directly only when you manage triggerElement and isOpen yourself. | | RangepickerInput, DatepickerInput | Deprecated — use Datepicker instead. Removed in v1.6. |

Composable

  • useRangepicker(triggerRef, options) — returns isOpen, dateRange, and open/close/toggle helpers.
  • useDatepicker(...) — deprecated alias of useRangepicker.

Types

DateRange, DatepickerProps, RangepickerProps, RangepickerEmits, RangepickerInputProps, DatepickerInputProps, DayPrice, PriceFormat, WeekendDay, CalendarDay, CalendarMonth, MonthChangePayload, DisabledDateConfig, DisabledDateInput, HolidayConfig, HolidayInput, HolidayType, HolidayLegendCategory, HolidayLegendLabels.

Usage

Type-safe options

<script setup lang="ts">
import { ref } from 'vue'
import Datepicker, { type DatepickerProps } from '@archpublicwebsite/rangepicker'
import '@archpublicwebsite/rangepicker/style.css'

const dates = ref('')

const pickerOptions: Partial<DatepickerProps> = {
  format: 'DD/MM/YYYY',
  autoApply: true,
  minDays: 2,
  maxDays: 30,
  weekendDays: ['friday', 'saturday'],
}
</script>

<template>
  <Datepicker v-model="dates" v-bind="pickerOptions" primary-color="#3b82f6" />
</template>

Color themes

<Datepicker v-model="dates1" primary-color="#3b82f6" secondary-color="#60a5fa" />
<Datepicker v-model="dates2" primary-color="#8b5cf6" secondary-color="#a78bfa" />

Advanced composition

Use Rangepicker directly when you need your own trigger element and open-state control:

<script setup lang="ts">
import { ref } from 'vue'
import { Rangepicker, useRangepicker, type RangepickerProps } from '@archpublicwebsite/rangepicker'
import '@archpublicwebsite/rangepicker/style.css'

const triggerRef = ref<HTMLElement | null>(null)
const options: Partial<RangepickerProps> = { valueOfMonths: 2, minDays: 1, autoApply: true }

const { isOpen, dateRange, toggle } = useRangepicker(triggerRef, options)
</script>

<template>
  <div>
    <button ref="triggerRef" type="button" @click="toggle">
      {{ dateRange.startDate && dateRange.endDate ? `${dateRange.startDate} - ${dateRange.endDate}` : 'Select dates' }}
    </button>
    <Rangepicker v-model="dateRange" v-model:is-open="isOpen" :trigger-element="triggerRef" />
  </div>
</template>

API reference

Datepicker props

The default export. Recommended entry point for both range and single-date selection.

| Prop | Type | Default | Description | | --- | --- | --- | --- | | modelValue | string | - | Range: "DD MMM YYYY - DD MMM YYYY". Single: "DD MMM YYYY". | | singleMode | boolean | false | Select one date instead of a range. | | variant | 'desktop' \| 'mobile' \| 'full' | 'desktop' | Inline dropdown, mobile bottom sheet, or full-screen takeover. | | primaryColor / secondaryColor | string | - | HEX colors, e.g. #3b82f6. | | minDate / maxDate | string \| Date | - | Selectable date bounds. | | minDays / maxDays | number | - | Nights bounds in range mode. maxDays: 0 = unlimited. | | numberOfMonths | number | 2 | Month panels shown side by side (range mode). | | disabledDates | (string \| Date \| { date, label? })[] | [] | Blocked dates, optionally with a sold-out label. | | holidays | HolidayInput[] | [] | Bare dates or { date, name, type? } objects — see "Holidays & the legend" below. | | holidayLegendLabels | { holiday?, promoday?, other? } | - | Fallback legend label per category, used only for entries with no name. | | weekendDays | WeekendDay[] | ['saturday','sunday'] | Days styled as weekends. | | prices | DayPrice[] | - | Per-date nightly prices rendered in each cell. | | pricesLoading | boolean | false | Show a loading skeleton in price slots. | | priceFormat | 'short' \| 'currency' \| 'raw' | 'short' | Price label format. | | priceCurrency | string | '₱' | Currency symbol for priceFormat: 'currency'. | | showPriceLegend | boolean | false | Show a legend for the lowest-price color. | | priceCurrencyLabel | string | - | Currency label in the price legend, e.g. 'IDR'. | | showPriceTooltip | boolean | true | Include price in the hover tooltip. | | showOtherMonthDays | boolean | true | Render adjacent-month days; false blanks those cells. | | format | string | 'DD MMM YYYY' | dayjs display/emit format. | | placeholder | string | mode-dependent | 'Select dates' (range) or 'Select a date' (single). | | showToday | boolean | true | Highlight today's date. | | showTooltip | boolean | true | Night-count tooltip on hover (range mode). | | showClearButton | boolean | true | Show the ✕ clear button. | | autoApply | boolean | true | Apply on selection complete, no Apply/Cancel buttons. | | close | boolean | - | Set true to close the picker programmatically. | | keepPositionOnScroll | boolean | true | Recalculate popup position on page scroll (desktop). | | mobileTopPosition | number \| string | undefined | Unset anchors below the trigger; 'center' centers; a number/px string pins a fixed offset. | | borderRadius | string | '0.75rem' | Calendar container border radius. | | dayBorderRadius | string | - | Day cell border radius (falls back to --rangepicker-day-radius). | | colorScheme | 'light' \| 'dark' | 'light' | Color scheme. | | fontFamily | string | - | Custom font family. | | id / name | string | - | Native id/name on the input element. | | readonly | boolean | true | Prevent manual text entry; select via calendar only. | | class | string | - | CSS class on the inner input element. |

Rangepicker props

Most props match Datepicker above. Naming differs in a few places (this component predates Datepicker): valueOfMonths/valueOfColumns instead of numberOfMonths, plus isOpen, triggerElement, position, delimiter, and colorStyles for manual composition. See types.ts for the full RangepickerProps interface.

Holidays & the legend

Each holidays entry is either a bare date (defaults to type: 'holiday', no name) or a { date, name, type? } object:

const holidays = [
  { date: '17-08', name: 'Indonesian Independence Day' },                    // recurring — no year, shown every year
  { date: '27-05-2026', name: 'Eid al-Adha', type: 'holiday' },              // specific — 2026 only
  { date: '2026-09-18', name: 'Long-weekend promo rate', type: 'promoday' }, // blue
  { date: '2026-09-10', name: 'Loyalty member birthday', type: 'birthday' }, // gray ("Other")
]
  • date with no year ('DD-MM') recurs every year the calendar renders; a full date (ISO, Date, or 'DD-MM-YYYY') shows only in that one year — use this for lunar-calendar holidays whose Gregorian date shifts (Eid, Nyepi, Waisak, Isra Mi'raj).
  • type has three visual buckets: 'holiday' (default, red day number + red legend date), 'promoday' (blue), or anything else — 'birthday', a custom string, or omitted — which is gray and labeled "Other".
  • Names are not shown in a tooltip. Passing holidays is the only thing needed to also get a legend below the calendar — it appears automatically, as one wrapping line of text per month currently on screen (e.g. "17 Indonesian Independence Day · 25 Prophet Muhammad's Birthday"), with each entry's leading date number colored by category instead of a dot. It re-filters live as the user navigates prev/next.
  • There's no date-range field — a multi-day event is entered as several same-named dated entries, one per day. The legend automatically merges same-name entries within a month into a compact range (e.g. "20, 23–24 Cuti Bersama Idulfitri") instead of repeating the name.
  • holidayLegendLabels overrides the fallback label used only for entries with no name; named entries always show their own name.

AI-agent implementation contract

Use this deterministic mapping when generating holiday data:

| Input style | Use when | Example | Visibility behavior | | --- | --- | --- | --- | | DD-MM | Recurring annual holiday | 17-08 | Appears every year (2026, 2027, ...). | | DD-MM-YYYY | One-off/specific year holiday | 17-08-2026 | Appears only in that exact year. | | YYYY-MM-DD (ISO) | One-off/specific year holiday | 2026-08-17 | Appears only in that exact year. |

Implementation rules for agents:

  1. Convert recurring holidays to DD-MM to avoid duplicating entries each year.
  2. Keep moving-date holidays (Eid/Nyepi/Waisak/Isra Mi'raj) as explicit year dates.
  3. Prefer stable short names so merged legend rows remain readable.
  4. Never generate both recurring and explicit duplicates for the same holiday/date unless an explicit yearly override is intentional.

Validation checklist:

  1. Navigate to next year and confirm DD-MM entries still appear.
  2. Confirm DD-MM-YYYY and ISO entries disappear outside their year.
  3. Verify legend updates as visible months change (monthChange event).

Events

| Event | Payload | Description | | --- | --- | --- | | update:modelValue | { startDate, endDate } (Rangepicker) / string (Datepicker) | Date value changed. | | update:isOpen | boolean | Picker opened/closed (Rangepicker only). | | dateSelected | Dayjs | A date was clicked. | | rangeSelected | { start: Dayjs, end: Dayjs } | A range was completed. | | monthChange | MonthChangePayload[] | Prev/next navigation, with all visible months. |

Key types

interface DateRange {
  startDate: string
  endDate: string
}

interface RangepickerEmits {
  'update:modelValue': [value: { startDate: string, endDate: string }]
  'update:isOpen': [value: boolean]
  'dateSelected': [date: Dayjs]
  'rangeSelected': [start: Dayjs, end: Dayjs]
  'monthChange': [months: MonthChangePayload[]]
}

type WeekendDay = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday'

Styling

The component ships Tailwind utilities prefixed with arch-, so it's safe alongside Vuetify, Bootstrap, or your app's own Tailwind config. Three ways to theme it, from easiest to most custom:

<!-- 1. Color props -->
<Datepicker primary-color="#3b82f6" secondary-color="#60a5fa" />
/* 2. CSS variables (global), RGB channel values */
:root {
  --color-primary: 59 130 246;
  --color-secondary: 148 163 184;
}
/* 3. Override a specific class */
.rangepicker-day-selected {
  background-color: purple !important;
}

If styles don't apply, confirm you imported @archpublicwebsite/rangepicker/style.css.

Quick implementation checklist

  1. Import stylesheet once: @archpublicwebsite/rangepicker/style.css.
  2. Use holidays with the date-format contract above.
  3. Set weekendDays explicitly for local market calendars when needed.
  4. Test both one-month and two-month modes (numberOfMonths: 1 | 2).
  5. Test desktop and mobile variants for footer/legend wrapping.

Browser support

Chrome/Edge, Firefox, and Safari (latest 2 versions), Mobile Safari (iOS 14+), and Chrome for Android.

Development

pnpm install
pnpm dev          # demo app with live examples
pnpm build        # build the package
pnpm type-check

Contributing

See the root README for contribution guidelines.

License

MIT © Archipelago Hotels