@archpublicwebsite/rangepicker

Custom date range picker component for PBA Hotel Apps
12,040
Install
npm install @archpublicwebsite/rangepicker
Latest Version:1.7.3
Vue:3
License:MIT
Last Updated:Sep 13, 2026
Links: npm
Maintainer: archpublicwebsite

@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 the named Datepicker export to get a complete input + calendar out of the box (the default export remains available for compatibility), or compose Rangepicker directly if you need to manage the trigger element and open state yourself.

Datepicker also accepts the deprecated Litepicker-shaped options object so existing 1.6.x consumers can upgrade without an immediate call-site rewrite. This compatibility surface is scheduled for removal in 1.7.0; new code should pass canonical props directly.

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 Named and 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. Retained throughout 1.x; earliest planned removal is v2.0.

Composable

  • useDatePicker(triggerRef, options) — primary composable. Returns isOpen, dateRange, and open/close/toggle helpers.
  • useDatepicker, useRangepicker, and useRangePicker — deprecated compatibility spellings. Use useDatePicker for new implementations; the earliest planned removal of an alias is v2.0.

Types

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

LegacyDatepickerOptions and the DatepickerProps.options prop are deprecated and scheduled for removal in 1.7.0.

numberOfColumns is retained on the deprecated RangepickerInput compatibility shim and inside the deprecated options object until 1.7.0. Use numberOfMonths with Datepicker for new code.

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>

Legacy options object

The deprecated Litepicker-shaped options object remains accepted for existing 1.6.x consumers. It is scheduled for removal in 1.7.0:

<Datepicker
  v-model="dates"
  :options="{
    autoApply: true,
    minDate: new Date(),
    format: 'DD MMM YYYY',
    numberOfColumns: 2,
    numberOfMonths: 2,
    singleMode: false,
  }"
  primary-color="#3b82f6"
/>

Supported values are normalized into the canonical Datepicker props, and an explicitly passed canonical prop wins over its legacy options value. startDate sets the initial visible month; numberOfColumns remains available inside the legacy object for old independent-column layouts until 1.7.0. Legacy-only fields such as allowRepick, dropdowns, tooltipText, tooltipNumber, and inlineMode are accepted for source compatibility but have no separate canonical behavior. Migrate to direct props when updating a call site.

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, useDatePicker } from '@archpublicwebsite/rangepicker'
import '@archpublicwebsite/rangepicker/style.css'

const triggerRef = ref<HTMLElement | null>(null)
const { isOpen, dateRange, toggle } = useDatePicker(triggerRef)
</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".
options LegacyDatepickerOptions - Deprecated; scheduled for removal in 1.7.0.
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.
initialDate string | Date current month Initial visible month when no date is selected.
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 'IDR' 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 and re-filters live as the user navigates prev/next. The legend renders as month sections with one row per entry: a category-colored date marker followed by the label.
  • 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.
focusin - Input focus event (Datepicker only). Useful for analytics or lazy data fetch.
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

Related Packages

@tiaohsun/vue-datepicker

A Vue 3 datepicker with multi-calendar support and a self-contained theme (no Ta...

1,933 0
vue-jalali-datepicker

A lightweight, zero-dependency Persian (Jalali / Shamsi) date and range picker f...

539 0
vue-accessible-datepicker

⚠️**WARNING: vue-accessible-datepicker is at pre-alpha stage of development so t...

5,314 6
vuejs-datepicker

A simple Vue.js datepicker component. Supports disabling of dates, inline mode,...

3,505,193 2,584

Version History

Version Released Vue License
1.7.3 ^3.5.22 MIT
1.7.2 ^3.5.22 MIT
1.7.1 ^3.5.22 MIT
1.7.0 ^3.5.22 MIT
1.6.0 ^3.5.22 MIT