Language

NepaliDatePicker

A text input with a calendar popover for choosing a Bikram Sambat date.

function NepaliDatePicker(props: NepaliDatePickerProps): JSX.Element

A masked YYYY-MM-DD text input, an optional clear button, and a button that opens a NepaliCalendar in a popover. Typing and picking both produce a BSDate; clearing produces undefined.

import { useState } from "react";
import { NepaliDatePicker, type BSDate } from "bikram-sambat-react";
 
const [date, setDate] = useState<BSDate>();
 
<label htmlFor="dob">Date of birth</label>
<NepaliDatePicker id="dob" value={date} onChange={setDate} clearable />
Live demobikram-sambat-react@0.2.0
value = undefined

Props

Every prop is optional.

Value

PropTypeDefaultDescription
valueBSDate | undefined(uncontrolled)The selected date, controlled. Passing the prop at all, even as undefined, makes the picker controlled; undefined means empty.
defaultValueBSDateThe initial date, when uncontrolled.
onChange(date: BSDate | undefined) => voidCalled when the date changes: picked in the calendar, typed as a complete valid date, or cleared.

Calendar

These are passed on to the calendar in the popover, and apply to typed input too.

PropTypeDefaultDescription
minDateBSDateBS 1979-01-01The first selectable (and typeable) date, inclusive.
maxDateBSDateBS 2100-12-31The last selectable date, inclusive.
isDateDisabled(date: BSDate) => booleanReturn true to disable a day, in the calendar and when typed.
locale"en" | "ne""en"Language of the calendar and every label.
numerals"latin" | "devanagari"from localeDigits in the input text and the calendar.
todayBSDatethe device's dateThe day the calendar highlights as today.
showGregorianDatebooleanfalseShow AD dates in the calendar.
dayShape"circle" | "rounded" | "square""circle"Shape of the calendar's day buttons.

Input

PropTypeDefaultDescription
iconPosition"end" | "start" | "none""end"Where the calendar button goes. See icon position.
maskbooleantrueGuide typing into YYYY-MM-DD: digits only, automatic dashes, paste normalization, a numeric keypad. false gives a plain text input. See typing dates.
clearablebooleanfalseShow a button that clears the date.
placeholderstring"YYYY-MM-DD" / "वर्ष-महिना-गते"The input's placeholder, per locale.
disabledbooleanfalseDisable the whole picker.
readOnlybooleanfalseShow the value without allowing changes: the calendar button is disabled, the clear button hidden.
requiredbooleanMark the input as required, for native form validation.
namestringRender a hidden input with this name for native forms. See forms.
idstringid of the text input, for a <label htmlFor>.
aria-label, aria-labelledby, aria-describedbystringAccessible name and description of the input, when there's no <label>.

Styling

PropTypeDefaultDescription
classNamesPartial<DatePickerClassNames>A class for each part: root, field, input, clear, trigger, popover, icon, plus calendar for the calendar's own parts.
componentsPartial<DatePickerComponents>Replace the calendar's parts, and the TriggerIcon and ClearIcon.
unstyledbooleanfalseLeave out the default nc-* classes, on the picker and its calendar.
classNamestringExtra class for the root element.

See styling & customization.

Typing dates

The input takes YYYY-MM-DD in Latin or Devanagari digits: 2083-06-15 and २०८३-०६-१५ both work, whatever numerals is set to. By default (mask), typing is guided:

  • Digits only. Anything else is dropped. Digits show in the picker's numerals, so with numerals="devanagari" typing 2 shows २.
  • Dashes appear as you type. 2084 becomes 2084-, and 208409 becomes 2084-09-. You can still type a -, /, . or space where a dash goes; it's simply skipped, never doubled.
  • Deleting works naturally. Backspace never re-adds a dash: on 2084- it gives 2084. Backspace just after a dash in the middle (or Delete just before one) removes the digit on the other side of it.
  • Single digits get padded. A separator typed after a one-digit month pads it: 2084-9- becomes 2084-09-. A one-digit day can't be padded while you type (1 may still become 12), so it's padded when the input loses focus. Typing 2084-9-9 and tabbing away commits 2084-09-09, and an invalid result like 2084-13-01 is flagged as usual.
  • Eight digits at most. Once the date is full, more digits are ignored unless you select some to replace.
  • Pasted and autofilled dates are normalized. 2084/9/9, 2084.09.09, 2084 - 9 - 9 and 20840909 all become 2084-09-09, in Latin or Devanagari digits.
  • The caret stays put when you edit the middle of the text.
  • Phones show a numeric keypad (inputMode="numeric"), and IME input is formatted when composition ends.
Live demobikram-sambat-react@0.2.0
masked value = undefined
mask={false} value = undefined

Pass mask={false} for a plain text input that accepts anything, as in version 0.1. Either way, the same rules decide when a date is committed:

  • A date is committed only when it's complete, valid and selectable. While you type 2083-06-1, nothing happens; the moment the text becomes 2083-06-15, onChange is called. Partial or invalid text never produces a date.
  • Invalid text isn't flagged while you type. It's marked aria-invalid (and shown with a red border by the default styles) when the input loses focus or you press Enter. 2083-02-32, for example, is invalid because Jestha 2083 has 31 days.
  • Dates outside minDate/maxDate, or rejected by isDateDisabled, count as invalid.
  • Clearing the text calls onChange(undefined).
  • The text follows value. When value changes from outside, or a date is picked in the calendar, the text is rewritten as YYYY-MM-DD in the current numeral system. If a controlled parent rejects a typed date (doesn't update value), the text goes back to the current value.

The popover

  • Opening: click the calendar button, or press Alt+↓ in the input. Focus moves into the calendar, onto the selected date or else today.
  • Picking: click a day or press Enter/Space on it. The popover closes and focus returns to the calendar button.
  • Closing without picking: press Escape (focus returns to the button), click outside, or Tab out of it. Focus is never trapped.
  • Placement: the popover opens below the field, or above it when there isn't room below, and lines up with the field's right edge when it would overflow the viewport on the right.

Escape inside the picker is handled there and doesn't reach your own handlers, so a picker inside a modal dialog closes only itself.

Icon position

iconPosition puts the calendar button after the input (the default), before it, or nowhere:

<NepaliDatePicker iconPosition="end" />
<NepaliDatePicker iconPosition="start" />
<NepaliDatePicker iconPosition="none" />
Live demobikram-sambat-react@0.2.0

With "none", the input itself opens the calendar:

  • clicking the input opens it without moving focus, so you can keep typing;
  • ↓ in the input moves into the calendar (and opens it first if it's closed);
  • after a pick or Escape, focus returns to the input.

The input then has role="combobox" with aria-haspopup="dialog", aria-expanded and aria-controls, the WAI-ARIA combobox date picker pattern.

Clearing

clearable adds a clear button after the input while there's a date. It clears the value (onChange(undefined)) and moves focus to the input. It's hidden when the picker is disabled or readOnly.

Forms

NepaliDatePicker works with plain value/onChange, so it fits any form library. For a native <form>, give it a name: it renders a hidden input holding the date as ASCII YYYY-MM-DD (empty when there's no date), whatever the numeral system shown to the user.

<form action="/save">
  <NepaliDatePicker name="dob" locale="ne" defaultValue={{ year: 2083, month: 6, day: 15 }} />
  <button type="submit">Save</button>
</form>
// The input shows "२०८३-०६-१५"; the form submits dob=2083-06-15.

On the server, parseBsDate from bikram-sambat-ts turns the submitted text back into a BSDate.

ⓘ

Note: the text input itself has no name, so only the hidden YYYY-MM-DD value is submitted. required applies to the visible input, so the browser blocks submitting an empty picker.

Labels

Give the input an accessible name, like any form field: a <label htmlFor> pointing at id, or aria-label / aria-labelledby. The calendar button is labelled "Choose date", or "Choose date, 2083-06-15" once a date is set ("मिति छान्नुहोस्" in Nepali), and the popover is a dialog with the same label.

Disabled and read-only

disabled disables the input and both buttons. readOnly keeps the value visible and selectable as text, but the calendar button is disabled and the clear button is hidden.