InputsReact
A button that shows the chosen week and opens a WeekCalendar in a popup.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<WeekPicker.Root>
<WeekPicker.Trigger>
<WeekPicker.Value />
</WeekPicker.Trigger>
<WeekPicker.Portal>
<WeekPicker.Positioner>
<WeekPicker.Popup>
<WeekPicker.Calendar />
<WeekPicker.Close />
</WeekPicker.Popup>
</WeekPicker.Positioner>
</WeekPicker.Portal>
</WeekPicker.Root>Examples
Form
Inside Field and Form with required, min and max: the weeks outside the bounds are disabled and the first day submits as leave=2026-11-22.
API reference
55 props across 8 parts, in React syntax. className, style, render are listed once, under Shared props.
CalendarWeekPicker.Calendar
The calendar of the popup: a WeekCalendar.Root wired to the picker's value, limits, rule and visible month. Compose it with the WeekCalendar parts. It takes no part in the Field: the picker's root already submits and validates the value. Renders a <div>.
| Data attribute | Description |
|---|---|
data-disabled | Present when the week picker is disabled. |
data-readonly | Present when the week picker does not allow changing the selection. |
data-empty | Present when no week is chosen. |
CloseWeekPicker.Close
A button that closes the popup, named "Close" unless you name it. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
PopupWeekPicker.Popup
The dialog that holds the week calendar, named "Choose week" unless you name it. Focus moves to the calendar's tab stop week when it opens and returns to the trigger when it closes. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
initialFocusDetermines the element to focus when the popover is opened. | boolean | React.RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | — |
finalFocusDetermines the element to focus when the popover is closed. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-starting-style | Present when the popup begins animating in. |
data-ending-style | Present when the popup is animating out. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-instant | Present if animations should be instant. |
PortalWeekPicker.Portal
A portal element that moves the popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
containerA parent element to render the portal element into. | HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | — |
PositionerWeekPicker.Positioner
Positions the popup against the trigger. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
anchorAn element to position the popup against. | Element | VirtualElement | React.RefObject<Element | null> | (() => Element | VirtualElement | null) | null | — |
positionMethodDetermines which CSS position property to use. | "absolute" | "fixed" | 'absolute' |
sideWhich side of the anchor element to align the popup against. | Side | 'bottom' |
sideOffsetDistance between the anchor and the popup in pixels. | number | OffsetFunction | 0 |
alignOffsetAdditional offset along the alignment axis in pixels. | number | OffsetFunction | 0 |
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to. | Boundary | 'clipping-ancestors' |
collisionPaddingAdditional space to maintain from the edge of the collision boundary. | Padding | 5 |
stickyWhether to maintain the popup in the viewport after | boolean | false |
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup. | number | 5 |
disableAnchorTrackingWhether to disable the popup from tracking any layout shift of its positioning anchor. | boolean | false |
collisionAvoidanceDetermines how to handle collisions when positioning the popup. | CollisionAvoidance | — |
alignHow to align the popup relative to the specified side. | Align | 'start' |
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-anchor-hidden | Present when the anchor is hidden. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
RootWeekPicker.Root
Groups all parts of a button that shows the chosen week and opens a week calendar in a popup. It renders no element of its own, only the hidden input. The value is the week's first day as an ISO 8601 string such as '2026-11-08'.
| Prop | Type | Default |
|---|---|---|
minThe earliest first day of a week that can be focused or chosen ('2026-11-09'). | `${number}-${number}-${number}` | — |
maxThe latest first day of a week that can be focused or chosen. | `${number}-${number}-${number}` | — |
isWeekDisabledWhether a week is disabled: navigation skips it and it cannot be chosen. | ((week: Week) => boolean) | — |
isWeekUnavailableWhether a week can be focused but not chosen. | ((week: Week) => boolean) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
readOnlyWhether the user should be unable to change the selection. | boolean | false |
requiredWhether the user must choose a week before submitting a form. | boolean | false |
nameIdentifies the field when a form is submitted. | string | — |
formIdentifies the form that owns the hidden input. | string | — |
localeThe BCP 47 locale of the names, the digits and the week rule. | string | 'en-US' |
weekRuleThe week rule: the locale's, or ISO 8601's (weeks start on Monday, and week 1 holds the | WeekRuleName | 'locale' |
firstDayOfWeekThe first day of the week, 0 (Sunday) to 6 (Saturday). | number | — |
minimalDaysThe days, 1 to 7, the first week of a year has in that year. | number | — |
visibleMonthThe visible month ('2026-11' or a date). | `${number}-${number}-${number}` | `${number}-${number}` | null | — |
onVisibleMonthChangeEvent handler called when the visible month changes. | ((month: `${number}-${number}`, eventDetails: WeekCalendarRootVisibleMonthChangeEventDetails) => void) | — |
fixedWeeksWhether every month shows six weeks. | boolean | false |
todayThe date of today ('2026-11-11'). | `${number}-${number}-${number}` | — |
timeZoneThe time zone that decides what today is, when today is not given. | string | — |
inputRefA ref to access the hidden input element. | React.Ref<HTMLInputElement> | — |
valueThe chosen week's first day, null for none. | `${number}-${number}-${number}` | null | — |
defaultValueThe chosen week when initially rendered. | `${number}-${number}-${number}` | null | — |
onValueChangeEvent handler called when a week is chosen, with its first day. | ((value: `${number}-${number}-${number}` | null, eventDetails: WeekCalendarRootChangeEventDetails) => void) | — |
openWhether the popup is open. | boolean | — |
defaultOpenWhether the popup is initially open. | boolean | false |
onOpenChangeEvent handler called when the popup opens or closes. | ((open: boolean, eventDetails: WeekPickerRootOpenChangeEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the popup opens or closes. | ((open: boolean) => void) | — |
actionsRefA ref to imperative actions: unmount and close. | React.RefObject<PopoverRootActions | null> | — |
modalWhether the rest of the page is inert while the popup is open. | boolean | "trap-focus" | false |
closeOnSelectWhether choosing a week closes the popup. | boolean | true |
defaultVisibleMonthThe month the calendar shows before the popup first opens. | `${number}-${number}-${number}` | `${number}-${number}` | — |
highlightedWeekThe first day of the week that holds focus in the open calendar. | `${number}-${number}-${number}` | null | — |
onHighlightChangeEvent handler called when the focused week changes, null when focus leaves the calendar. | ((start: `${number}-${number}-${number}` | null) => void) | — |
labelsThe texts the picker and its calendar add, each overridable. | Partial<WeekPickerLabels> | — |
children | React.ReactNode | — |
TriggerWeekPicker.Trigger
A button that shows the chosen week and opens the popup. Inside a Field it is named by the Field's label and its own text. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
idID of the trigger. | string | — |
| Data attribute | Description |
|---|---|
data-popup-open | Present when the corresponding popup is open. |
data-pressed | Present when the trigger is pressed. |
data-disabled | Present when the trigger is disabled. |
data-placeholder | Present when no week is chosen and the value shows the placeholder. |
data-valid | Present when the week picker is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the week picker is in an invalid state (when wrapped in Field.Root). |
data-touched | Present when the week picker has been touched (when wrapped in Field.Root). |
data-dirty | Present when the week picker's value has changed (when wrapped in Field.Root). |
data-filled | Present when a week is chosen (when wrapped in Field.Root). |
data-focused | Present when the trigger is focused (when wrapped in Field.Root). |
ValueWeekPicker.Value
The chosen week as text ("Week 46, Nov 8 – 14, 2026"), or the placeholder when none is chosen. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
placeholderThe text shown when no week is chosen. | React.ReactNode | — |
childrenThe content, or a function of the chosen week (null when none). | React.ReactNode | ((week: Week | null) => React.ReactNode) | — |
| Data attribute | Description |
|---|---|
data-placeholder | Present when no week is chosen and the placeholder shows. |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| EnterSpace | On the trigger: opens the popup and moves focus to the chosen week, else the current week | Trigger |
| EnterSpace | On a week: chooses it, closes the popup (with closeOnSelect) and returns focus | Popup |
| Escape | Closes the popup and returns focus to the trigger | Popup |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Trigger | aria-haspopup="dialog", aria-expanded, aria-controls; aria-labelledby the Field label and itself | the button says what it opens, what it is for and which week is chosen |
| Trigger | aria-disabled while readOnly; the Field's validity as data-valid or data-invalid | a read only picker does not open, and says so |
| Value | the week's short text, or the placeholder with data-placeholder | the trigger always has a visible name |
| Popup | role="dialog" named "Choose week" (or your aria-label); aria-modal only with modal | a screen reader announces the dialog on open |
| Calendar | the WeekCalendar root, named by the popup's label, with no hidden input and no part in the Field | the calendar is read by the dialog's name and the form gets the value once |
| Close | a button, "Close" | a touch screen reader can leave a popup it cannot Escape |
Focus order
- The Trigger is one tab stop. Nothing takes focus on mount.
- Opening moves focus into the popup, to a week of the grid; the calendar's buttons and Close come in DOM order around it.
- Choosing a week or closing returns focus to the trigger.