Skip to content

feat(date): Temporal polyfill integration#2532

Draft
epr3 wants to merge 28 commits into
v2from
feat-2183-temporal-integration
Draft

feat(date): Temporal polyfill integration#2532
epr3 wants to merge 28 commits into
v2from
feat-2183-temporal-integration

Conversation

@epr3
Copy link
Copy Markdown
Collaborator

@epr3 epr3 commented Mar 21, 2026

🔗 Linked issue

#2183

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Resolves #2183 . Details in the issue.

📸 Screenshots (if appropriate)

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Summary by CodeRabbit

  • Refactor

    • Migrated date/time value handling throughout component library from one date library to another, updating all calendar, date picker, month picker, year picker, and time field components accordingly.
    • Updated internal date comparators, calendar utilities, and formatting logic to align with new date type system.
  • Documentation

    • Updated component documentation and guides to reflect updated date handling approach.
  • Chores

    • Updated package dependencies across the library and documentation site.
    • Updated test fixtures and setup to align with new date infrastructure.

@epr3 epr3 self-assigned this Mar 21, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 21, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 77c5282d-6cf0-4984-9a51-0af31bef268b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request implements comprehensive Temporal API integration across the entire date/time picker ecosystem, replacing all @internationalized/date dependencies with Temporal-based types and implementations. The migration affects components, utilities, tests, and documentation while maintaining existing functionality and control flow.

Changes

Cohort / File(s) Summary
Documentation
docs/content/docs/components/*.md, docs/content/docs/guides/dates.md, docs/content/docs/guides/migration.md, docs/content/docs/utilities/use-date-formatter.md
Updated all component and guide documentation to reference temporal-polyfill instead of @internationalized/date, changing code examples, installation instructions, and preface language to reflect Temporal-based date handling.
Demo Components
docs/components/demo/Calendar/*, docs/components/demo/CalendarSelect/*, docs/components/demo/CalendarSwipe/*, docs/components/demo/CalendarYearIncrement/*, docs/components/demo/MonthPicker/*, docs/components/demo/MonthRangePicker/*, docs/components/demo/RangeCalendar/tailwind/index.vue, docs/components/demo/YearPicker/*, docs/components/demo/YearRangePicker/*, docs/components/examples/DatePickerSelection/index.vue, docs/components/examples/DatePickerViewSwitching/index.vue
Updated demo components to construct dates using Temporal.PlainDate.from(...) instead of CalendarDate, and updated helper functions and type signatures to accept TemporalDate parameters.
Core Calendar Components
packages/core/src/Calendar/Calendar.test.ts, packages/core/src/Calendar/CalendarCell.vue, packages/core/src/Calendar/CalendarCellTrigger.vue, packages/core/src/Calendar/CalendarNext.vue, packages/core/src/Calendar/CalendarPrev.vue, packages/core/src/Calendar/CalendarRoot.vue, packages/core/src/Calendar/useCalendar.ts, packages/core/src/Calendar/story/*
Migrated DateValue types to TemporalDate across all props, emits, context, and internal logic; updated placeholder initialization and value assignments to remove .copy() usage; replaced date comparison and mutation patterns with Temporal equivalents.
Date Field Components
packages/core/src/DateField/DateField.test.ts, packages/core/src/DateField/DateFieldRoot.vue, packages/core/src/DateField/story/*
Updated to use TemporalDate for all date values, replaced .copy() with direct assignments, and adjusted placeholder sync logic using Temporal comparisons.
Date Picker Components
packages/core/src/DatePicker/DatePicker.test.ts, packages/core/src/DatePicker/DatePickerCalendar.vue, packages/core/src/DatePicker/DatePickerField.vue, packages/core/src/DatePicker/DatePickerRoot.vue, packages/core/src/DatePicker/story/*
Migrated date types to TemporalDate, updated model synchronization to use Temporal.PlainDate.compare and plain-date equality checks, removed .copy() cloning behavior.
Date Range Field Components
packages/core/src/DateRangeField/DateRangeField.test.ts, packages/core/src/DateRangeField/DateRangeFieldRoot.vue, packages/core/src/DateRangeField/story/*
Updated range field to use TemporalDate for start/end values, replaced equality comparisons with toPlainDate(...).equals(...) patterns.
Date Range Picker Components
packages/core/src/DateRangePicker/DateRangePicker.test.ts, packages/core/src/DateRangePicker/DateRangePickerCalendar.vue, packages/core/src/DateRangePicker/DateRangePickerField.vue, packages/core/src/DateRangePicker/DateRangePickerRoot.vue, packages/core/src/DateRangePicker/story/*
Migrated range picker to TemporalDate, updated day-equality checks from isEqualDay to isSameDay, removed .copy() calls in range synchronization.
Month Picker Components
packages/core/src/MonthPicker/MonthPicker.test.ts, packages/core/src/MonthPicker/MonthPickerCell.vue, packages/core/src/MonthPicker/MonthPickerCellTrigger.vue, packages/core/src/MonthPicker/MonthPickerNext.vue, packages/core/src/MonthPicker/MonthPickerPrev.vue, packages/core/src/MonthPicker/MonthPickerRoot.vue, packages/core/src/MonthPicker/useMonthPicker.ts, packages/core/src/MonthPicker/story/*
Updated all month picker components to use TemporalDate, refactored boundary checks to use compareYearMonth, updated calendar derivation to use Temporal.Now.plainDateISO(), removed date cloning behavior.
Month Range Picker Components
packages/core/src/MonthRangePicker/MonthRangePicker.test.ts, packages/core/src/MonthRangePicker/MonthRangePickerCell.vue, packages/core/src/MonthRangePicker/MonthRangePickerCellTrigger.vue, packages/core/src/MonthRangePicker/MonthRangePickerNext.vue, packages/core/src/MonthRangePicker/MonthRangePickerPrev.vue, packages/core/src/MonthRangePicker/MonthRangePickerRoot.vue, packages/core/src/MonthRangePicker/useRangeMonthPicker.ts
Migrated month range picker to TemporalDate, updated range state management with direct value assignments instead of cloning.
Range Calendar Components
packages/core/src/RangeCalendar/RangeCalendar.test.ts, packages/core/src/RangeCalendar/RangeCalendarCell.vue, packages/core/src/RangeCalendar/RangeCalendarCellTrigger.vue, packages/core/src/RangeCalendar/RangeCalendarNext.vue, packages/core/src/RangeCalendar/RangeCalendarPrev.vue, packages/core/src/RangeCalendar/RangeCalendarRoot.vue, packages/core/src/RangeCalendar/useRangeCalendar.ts, packages/core/src/RangeCalendar/story/*
Migrated range calendar to use TemporalDate throughout, updated day-equality logic from isEqualDay to isSameDay, removed .copy() in range assignment flows.
Time Field Components
packages/core/src/TimeField/TimeField.test.ts, packages/core/src/TimeField/TimeFieldRoot.vue, packages/core/src/TimeField/story/*
Updated to use Temporal.PlainDateTime and Temporal.PlainTime for time values, refactored segment increment/decrement logic to use Temporal arithmetic, replaced cycle with add/subtract operations.
Time Range Field Components
packages/core/src/TimeRangeField/TimeRangeField.test.ts, packages/core/src/TimeRangeField/TimeRangeFieldRoot.vue, packages/core/src/TimeRangeField/story/*
Migrated time range field to Temporal.PlainDateTime, replaced time mutation with Temporal immutable .with() calls, updated comparison logic.
Year Picker Components
packages/core/src/YearPicker/YearPicker.test.ts, packages/core/src/YearPicker/YearPickerCell.vue, packages/core/src/YearPicker/YearPickerCellTrigger.vue, packages/core/src/YearPicker/YearPickerNext.vue, packages/core/src/YearPicker/YearPickerPrev.vue, packages/core/src/YearPicker/YearPickerRoot.vue, packages/core/src/YearPicker/useYearPicker.ts, packages/core/src/YearPicker/story/*
Updated to use TemporalDate, replaced calendar identifier access pattern from .calendar.identifier to .calendarId, updated year boundary checks with Temporal comparisons.
Year Range Picker Components
packages/core/src/YearRangePicker/YearRangePicker.test.ts, packages/core/src/YearRangePicker/YearRangePickerCell.vue, packages/core/src/YearRangePicker/YearRangePickerCellTrigger.vue, packages/core/src/YearRangePicker/YearRangePickerNext.vue, packages/core/src/YearRangePicker/YearRangePickerPrev.vue, packages/core/src/YearRangePicker/YearRangePickerRoot.vue, packages/core/src/YearRangePicker/useRangeYearPicker.ts
Migrated year range picker to TemporalDate with updated range state management and direct value assignments.
Temporal Utilities (New)
packages/core/src/temporal/index.ts, packages/core/src/temporal/types.ts, packages/core/src/temporal/calendar.ts, packages/core/src/temporal/comparators.ts
Added new Temporal abstraction layer providing TemporalDate union type, calendar conversion utilities via toCalendar(), and comprehensive comparators (isSameDay, isBefore, isAfter, boundary functions like startOfMonth/endOfMonth, week utilities, and validation helpers).
Date Utilities
packages/core/src/date/calendar.ts, packages/core/src/date/calendar.test.ts, packages/core/src/date/comparators.ts, packages/core/src/date/types.ts, packages/core/src/shared/date/comparators.ts, packages/core/src/shared/date/types.ts, packages/core/src/shared/date/index.ts, packages/core/src/shared/date/parser.ts, packages/core/src/shared/date/useDateField.ts, packages/core/src/shared/useDateFormatter.ts
Migrated date utility modules from DateValue to TemporalDate, reimplemented week/calendar grid generation using Temporal operations, updated parser to work with Temporal types, replaced DateFormatter with Intl.DateTimeFormat.
Public API
packages/core/src/index.ts, packages/core/src/shared/index.ts
Updated re-exported type surface to export TemporalDate instead of DateValue.
Package Configuration
docs/package.json, packages/core/package.json
Replaced @internationalized/date dependency with temporal-polyfill (^0.3.0).
Setup and Testing
packages/core/vitest.setup.ts, playground/nuxt/package.json
Added temporal-polyfill/global to test environment, introduced cleanup() after each test, removed @internationalized/date from playground dependencies.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

v2

Suggested reviewers

  • zernonia

Poem

🐰 With Temporal's grace, we hop through time,
From internationalized dates, we climb,
Each PlainDate and withCalendar call,
Makes our date pickers truly enthrall! ✨
No more .copy(), just .with() delight—
Temporal API shines so bright! 🌙

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-2183-temporal-integration

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Mar 21, 2026

Open in StackBlitz

npm i https://pkg.pr.new/reka-ui@2532

commit: 78773b9

@epr3 epr3 mentioned this pull request Mar 21, 2026
15 tasks
@epr3
Copy link
Copy Markdown
Collaborator Author

epr3 commented Mar 21, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (11)
packages/core/src/YearRangePicker/YearRangePicker.test.ts (2)

382-383: ⚠️ Potential issue | 🟡 Minor

Same DateValue type reference issue as noted above.

The isYearDisabled callback also uses DateValue type. This should be updated to TemporalDate for consistency with the migration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/YearRangePicker/YearRangePicker.test.ts` around lines 382 -
383, The test's isYearDisabled callback in YearRangePicker.test.ts incorrectly
types its parameter as DateValue; update the parameter type to TemporalDate to
match the migration (i.e., change the isYearDisabled: (date: DateValue) => ...
signature to use TemporalDate) so the test uses the correct TemporalDate type
for year checks.

273-276: ⚠️ Potential issue | 🔴 Critical

Replace DateValue with TemporalDate in callback parameter types.

The isYearUnavailable and isYearDisabled callbacks use the undefined type DateValue, which is no longer exported. Replace with TemporalDate to align with the migration.

Affected lines
  • Line 273: isYearUnavailable: (date: DateValue) =>
  • Line 382: isYearDisabled: (date: DateValue) =>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/YearRangePicker/YearRangePicker.test.ts` around lines 273 -
276, The callbacks isYearUnavailable and isYearDisabled use the removed
DateValue type; update their parameter types to TemporalDate (e.g., change
isYearUnavailable: (date: DateValue) => ... to isYearUnavailable: (date:
TemporalDate) => ... and likewise for isYearDisabled) so the tests compile
against the migrated type export; ensure any imports or type references in
YearRangePicker.test.ts that referenced DateValue are updated or removed
accordingly.
packages/core/src/Calendar/CalendarPrev.vue (1)

23-28: ⚠️ Potential issue | 🟠 Major

Remove unused step default and reorder variable declarations.

The step: 'month' default is dead code — step is neither declared in CalendarPrevProps nor used anywhere in the component. Remove it from line 23.

Additionally, rootContext (line 28) is used in the disabled computed (line 26) but declared after it. Reorder to declare rootContext before disabled for clarity:

Suggested reordering
const props = withDefaults(defineProps<CalendarPrevProps>(), { as: 'button' })
defineSlots<CalendarPrevSlot>()

const rootContext = injectCalendarRootContext()

const disabled = computed(() => rootContext.disabled.value || rootContext.isPrevButtonDisabled(props.prevPage))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/Calendar/CalendarPrev.vue` around lines 23 - 28, Remove the
dead default step value from the withDefaults call (remove step: 'month' so the
props default only includes as: 'button') and move the rootContext declaration
before the disabled computed; specifically, call injectCalendarRootContext() to
assign rootContext prior to using it in the computed(() => ...) for disabled so
that rootContext.disabled.value and
rootContext.isPrevButtonDisabled(props.prevPage) reference an already-declared
variable.
packages/core/src/Calendar/CalendarNext.vue (1)

23-28: ⚠️ Potential issue | 🟡 Minor

Remove the step: 'month' default from withDefaultsstep is not defined in CalendarNextProps and is never used in the component.

The code order (accessing rootContext in disabled before declaring rootContext) is not a runtime error because the computed function is deferred until accessed, so rootContext will already exist by execution time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/Calendar/CalendarNext.vue` around lines 23 - 28, Remove the
unnecessary default `step: 'month'` from the withDefaults call because `step` is
not part of CalendarNextProps and is unused; update the line with
withDefaults(defineProps<CalendarNextProps>(), { ... }) to only include valid
defaults (e.g., keep `as: 'button'`) and ensure props is used unchanged
elsewhere; no change needed to the computed `disabled` or the `rootContext`
initialization other than ensuring you reference injectCalendarRootContext() by
name when locating the code to edit.
docs/components/examples/DatePickerSelection/index.vue (1)

91-110: ⚠️ Potential issue | 🟠 Major

Last quarter resolves to the current quarter on quarter-end months.

Math.floor(currentMonth / 3) makes March/June/September/December fall into the next bucket, so this example returns Jan-Mar in March, Apr-Jun in June, etc. Derive the start of the current quarter first, then subtract three months.

🐛 Proposed fix
-      const currentMonth = now.month
-      const currentQuarter = Math.floor(currentMonth / 3)
-
-      // If we're in the first quarter, we need to go back to last year
-      let startOfLastQuarter
-      if (currentQuarter === 0) {
-        startOfLastQuarter = now
-          .subtract({ years: 1 })
-          .set({ month: 10, day: 1 }) // October 1st of previous year
-      }
-      else {
-      // Otherwise, just go back 3 months from the start of current quarter
-        startOfLastQuarter = now
-          .set({ month: currentQuarter * 3 - 2, day: 1 })
-      }
+      const startOfCurrentQuarter = now.set({
+        month: now.month - ((now.month - 1) % 3),
+        day: 1,
+      })
+      const startOfLastQuarter = startOfCurrentQuarter.subtract({ months: 3 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/components/examples/DatePickerSelection/index.vue` around lines 91 -
110, The current logic computes currentQuarter incorrectly (making quarter-end
months fall into the next quarter); instead derive the start of the current
quarter from now (use currentMonth to compute startOfCurrentQuarter), set its
month/day to the first day of that quarter, then compute startOfLastQuarter by
subtracting 3 months from startOfCurrentQuarter so year rollovers are handled
automatically; update the block that sets startOfLastQuarter (and keep
endOfLastQuarter as startOfLastQuarter.add({ months: 3 }).subtract({ days: 1 }))
replacing the Math.floor(currentMonth / 3) logic in the action for the "Last
quarter" example (referencing getToday, currentMonth, startOfCurrentQuarter,
startOfLastQuarter, endOfLastQuarter).
packages/core/src/RangeCalendar/RangeCalendar.test.ts (1)

208-225: ⚠️ Potential issue | 🟠 Major

Restore the preserved-end assertion in this controlled-range test.

Line 225 only proves that some days stayed selected. It no longer verifies the behavior named by the test—keeping the externally controlled end after editing the start—so that regression can now slip through unnoticed. The new any payload here also drops the stricter guard the file added elsewhere.

Suggested fix
-        'onUpdate:modelValue': (data: any) => {
+        'onUpdate:modelValue': (data) => {
+          if (!isDateRange(data))
+            return
           rerender({
             calendarProps: {
               modelValue: {
                 start: data.start ?? controlledRange.start,
                 end: data.end ?? preservedEnd,
               },
             },
           })
         },
@@
-    expect(getSelectedDays(calendar).length).toBeGreaterThan(0)
+    expect(getByTestId('date-1-24')).toHaveAttribute('data-selection-start')
+    expect(getByTestId('date-1-28')).toHaveAttribute('data-selection-end')
+    expect(getByTestId('date-1-25')).toHaveAttribute('data-selected')
+    expect(getByTestId('date-1-27')).toHaveAttribute('data-selected')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/RangeCalendar/RangeCalendar.test.ts` around lines 208 -
225, The test's assertion that the externally controlled end remains preserved
was removed; restore an explicit assertion that preservedEnd is still present
after clicking the start. In the onUpdate:modelValue handler (the anonymous
listener in this test), tighten the payload typing instead of using any (ensure
the handler expects an object with optional start and end) and after the
user.click(twentyFourthDay) add an assertion verifying the calendar's model
still uses preservedEnd (e.g., compare the resolved end value to preservedEnd
via getSelectedDays or the calendarProps/modelValue), keeping the existing check
that the clicked date is marked as data-selection-start.
packages/core/src/RangeCalendar/RangeCalendarCellTrigger.vue (1)

208-238: ⚠️ Potential issue | 🟠 Major

Add an escape hatch when shiftFocus() keeps encountering disabled days.

This path still recurses until it finds an enabled candidate. If isDateDisabled blocks every day in that direction and there is no minValue / maxValue boundary to stop it, a single Arrow key press can recurse and keep paging indefinitely. The month/year pickers in this PR added a recursion cap; the day-grid path needs the same safeguard.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/RangeCalendar/RangeCalendarCellTrigger.vue` around lines
208 - 238, The shiftFocus function can recurse indefinitely when every candidate
day is disabled; add a recursion cap by extending shiftFocus to accept an
attempts/count parameter (default 0) and increment it on each recursive call; if
attempts exceeds a reasonable limit (e.g. 50) return early to bail out. Update
the two recursive calls—when paging via nextTick and when recursing past a
disabled candidate (the calls to shiftFocus(day, add) and
shiftFocus(candidateDayValue, add))—to pass attempts + 1, and leave other logic
(min/max checks, next/prev page via rootContext.nextPage()/prevPage(), and
checks like rootContext.isNextButtonDisabled()/isPrevButtonDisabled())
unchanged.
packages/core/src/TimeField/TimeField.test.ts (1)

601-614: ⚠️ Potential issue | 🟡 Minor

Test expectation contradicts the test name: cycling from 11 PM to 12 AM should result in AM, not PM.

The test name correctly states the expected behavior ("transitions from PM to AM"), but line 613 expects the dayPeriod to be PM. When the hour cycles from 23 (11 PM) upward, it wraps to 0 (12 AM), which according to the dayPeriod calculation (hours 0–11 = AM, hours 12–23 = PM) should always be AM.

Corrected expectation
       expect(hour).toHaveTextContent('12')
-      expect(getByTestId('dayPeriod')).toHaveTextContent('PM')
+      expect(getByTestId('dayPeriod')).toHaveTextContent('AM')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/TimeField/TimeField.test.ts` around lines 601 - 614, The
test "transitions from PM to AM when cycling from 11 PM to 12 AM" has an
incorrect expectation: after simulating an arrow-up from 23:30 the day period
should be AM, not PM. Update the assertion in the TimeField.test block (the test
using setup with modelValue Temporal.PlainTime.from({ hour: 23, minute: 30 }))
that checks getByTestId('dayPeriod') to expect 'AM' instead of 'PM' so the test
name and assertion align.
packages/core/src/DateField/DateFieldRoot.vue (1)

313-314: ⚠️ Potential issue | 🟠 Major

HTML input[type="date"] expects string values for max and min.

The :max="maxValue" and :min="minValue" bindings pass TemporalDate objects, but the HTML <input type="date"> element expects ISO date strings (e.g., "2024-07-10"). This could cause the browser to ignore these constraints or produce unexpected behavior.

🐛 Proposed fix
     <VisuallyHidden
       :id="id"
       as="input"
       type="date"
       feature="focusable"
       tabindex="-1"
       :value="modelValue ? modelValue.toString() : ''"
       :name="name"
       :disabled="disabled"
       :required="required"
-      :max="maxValue"
-      :min="minValue"
+      :max="maxValue?.toString()"
+      :min="minValue?.toString()"
       `@focus`="Array.from(segmentElements)?.[0]?.focus()"
     />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/DateField/DateFieldRoot.vue` around lines 313 - 314, The
input[type="date"] bindings :max="maxValue" and :min="minValue" are passing
TemporalDate objects; convert them to ISO date strings before binding so the
browser recognizes the constraints. Update DateFieldRoot.vue to provide
string-safe attributes (e.g., create computed props like maxAttr/minAttr or
adjust existing maxValue/minValue getters) that return maxValue?.toString() and
minValue?.toString() (or pass through if already a string) and use those
computed values in the input's :max and :min bindings to guard against
null/undefined and non-string types.
packages/core/src/TimeRangeField/TimeRangeFieldRoot.vue (1)

217-226: ⚠️ Potential issue | 🟡 Minor

areAllDaysBetweenValid is inappropriate for validating same-day time ranges.

This function is designed to validate intermediate days between two dates. When both start and end times fall on the same day (as they will after convertValue normalizes them to today), the function skips validation entirely and returns true. The function explicitly begins checking from the day after the start date, so for same-day ranges, no validation occurs.

For time range validation within a single day, a different approach is needed that checks time-specific unavailability rather than day-level availability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/TimeRangeField/TimeRangeFieldRoot.vue` around lines 217 -
226, The current call to areAllDaysBetweenValid only checks intermediate days
and will skip validation for same-day ranges; update the conditional around
propsIsTimeUnavailable (using convertedModelValue.start and
convertedModelValue.end) to detect when start and end are the same calendar day
and, in that case, perform a time-granular validation (e.g., a new helper or
inline loop) that checks the specific time interval against
propsIsTimeUnavailable rather than calling areAllDaysBetweenValid; keep using
areAllDaysBetweenValid for multi-day ranges. Ensure the same-day branch inspects
times between convertedModelValue.start and convertedModelValue.end and returns
true when any unavailable time is found (matching the existing control flow).
packages/core/src/Calendar/CalendarRoot.vue (1)

277-293: ⚠️ Potential issue | 🟠 Major

Normalize a seeded single value before handling multiple selection.

In multiple mode, this branch only handles undefined and arrays. A valid uncontrolled state is still a single TemporalDate here (multiple + defaultValue), so the second click becomes a no-op because neither branch runs.

Suggested fix
-  else if (Array.isArray(modelValue.value)) {
-    const index = modelValue.value.findIndex(date => isSameDay(date, value))
+  else {
+    const modelValueArray = Array.isArray(modelValue.value)
+      ? modelValue.value
+      : [modelValue.value]
+    const index = modelValueArray.findIndex(date => isSameDay(date, value))
     if (index === -1) {
-      modelValue.value = [...modelValue.value, value]
+      modelValue.value = [...modelValueArray, value]
     }
     else if (!preventDeselect.value) {
-      const next = modelValue.value.filter(date => !isSameDay(date, value))
+      const next = modelValueArray.filter(date => !isSameDay(date, value))
       if (!next.length) {
         placeholder.value = value
         modelValue.value = undefined
         return
       }
       modelValue.value = next
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/Calendar/CalendarRoot.vue` around lines 277 - 293, The
multiple-selection branch doesn't handle the case where modelValue.value is a
single TemporalDate (seeded uncontrolled default), so normalize that single
value into an array before the array-specific logic runs: in the block handling
multiple selections, detect when modelValue.value exists but is not an array and
replace it with [modelValue.value] (preserving placeholder/other state), then
proceed with the existing Array.isArray(modelValue.value) logic (including
isSameDay checks, preventDeselect handling, and clearing placeholder when
removing the last item) so subsequent clicks behave correctly.
🟡 Minor comments (5)
packages/core/src/date/calendar.ts-299-300 (1)

299-300: ⚠️ Potential issue | 🟡 Minor

Remove unused variables explicitFirstDay and firstDayMap (lines 299-300).

Both variables are defined but never used. Additionally, there's a type mismatch: the parameter firstDayOfWeek is typed as DayOfWeek (an object), but test cases pass string values like 'mon' and 'sun'. The type casting and map definition suggest incomplete code that should be cleaned up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/date/calendar.ts` around lines 299 - 300, Remove the unused
variables explicitFirstDay and firstDayMap and fix the typing for the
firstDayOfWeek parameter: delete the two declarations (explicitFirstDay and
firstDayMap) and change the type of firstDayOfWeek where it's declared to accept
the string values used in tests (e.g., use firstDayOfWeek: DayOfWeek | string or
a string literal union like 'sun'|'mon'|...); ensure any conversion logic that
relied on firstDayMap is implemented where needed or callers are adjusted to
pass the correct type. References: explicitFirstDay, firstDayMap,
firstDayOfWeek, DayOfWeek.
docs/content/docs/components/year-picker.md-35-35 (1)

35-35: ⚠️ Potential issue | 🟡 Minor

Add a direct link for “Temporal proposal docs”.

Line 35 references docs without a URL, so readers can’t navigate directly. This phrasing is repeated in related pages, so applying the same fix there will prevent drift.

🛠 Suggested doc tweak
-We recommend reading the Temporal proposal docs to understand the model and immutability semantics used by Reka UI date-related components.
+We recommend reading the [Temporal proposal docs](https://tc39.es/proposal-temporal/docs/) to understand the model and immutability semantics used by Reka UI date-related components.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/docs/components/year-picker.md` at line 35, Add a direct
hyperlink for the phrase "Temporal proposal docs" in the sentence "We recommend
reading the Temporal proposal docs to understand the model and immutability
semantics used by Reka UI date-related components." Replace the plain text with
an inline link to the official Temporal proposal (e.g.,
https://github.com/tc39/proposal-temporal) and ensure the link opens in a new
tab if your markdown renderer supports it; also apply the same quick edit to
other pages that repeat this sentence to keep documentation consistent.
docs/content/docs/guides/dates.md-34-35 (1)

34-35: ⚠️ Potential issue | 🟡 Minor

Documentation inconsistency: TimeValue type mapping appears incorrect.

The documentation states TimeValue → PlainTime, but in packages/core/src/shared/date/comparators.ts, TimeValue is defined as TemporalDateTime. This discrepancy could confuse users. Please verify and align the documentation with the actual type definition.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/docs/guides/dates.md` around lines 34 - 35, The docs mapping is
inconsistent with the code: change the documentation entry that currently reads
"TimeValue → PlainTime" to "TimeValue → TemporalDateTime" to match the type
alias in comparators.ts (TimeValue and TemporalDateTime); also scan the dates
guide for any other references to TimeValue or PlainTime and update them to
TemporalDateTime so the docs and the actual type definition stay aligned.
packages/core/src/DateField/DateFieldRoot.vue-130-133 (1)

130-133: ⚠️ Potential issue | 🟡 Minor

Type cast drops undefined from union.

The cast as Ref<TemporalDate> loses the undefined possibility from modelValue. Based on usage in the component (e.g., if (!modelValue.value) checks), this should be as Ref<TemporalDate | undefined>.

🐛 Proposed fix
 const modelValue = useVModel(props, 'modelValue', emits, {
   defaultValue: defaultValue.value,
   passive: (props.modelValue === undefined) as false,
-}) as Ref<TemporalDate>
+}) as Ref<TemporalDate | undefined>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/DateField/DateFieldRoot.vue` around lines 130 - 133, The
current cast on the modelValue returned from useVModel (const modelValue =
useVModel(...) as Ref<TemporalDate>) incorrectly drops undefined from the type;
change the assertion to preserve optionality (use Ref<TemporalDate | undefined>)
so downstream checks like if (!modelValue.value) remain correct; update the cast
on modelValue (and any related type annotations) to Ref<TemporalDate |
undefined> while keeping the existing defaultValue and passive arguments to
useVModel.
packages/core/src/YearPicker/useYearPicker.ts-71-79 (1)

71-79: ⚠️ Potential issue | 🟡 Minor

Fix BCE era detection in year heading formatter.

The Temporal era property returns 'gregory-inverse' for BCE dates in the Gregorian calendar, not 'bce'. The current check props.placeholder.value.era?.toLowerCase() === 'bc' will never match, causing BCE year headings to skip the era label. Change the comparison to check for 'gregory-inverse'.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/YearPicker/useYearPicker.ts` around lines 71 - 79, The
headingFormatOptions computed currently checks props.placeholder.value.era for
'bc' which never matches; update the conditional in useYearPicker's
headingFormatOptions to test for 'gregory-inverse' (e.g.,
props.placeholder.value.era === 'gregory-inverse') so BCE Gregorian dates set
options.era = 'short' for the correct Temporal era value; keep the rest of the
computed logic unchanged.
🧹 Nitpick comments (18)
packages/core/src/date/calendar.ts (1)

17-32: Consider adding explicit type annotations and avoiding any.

The normalizeTemporalDate function uses any cast which reduces type safety. While the duck-typing approach is pragmatic for handling various input shapes, consider using a more explicit union type or type guard.

-function normalizeTemporalDate(dateObj: TemporalDate): TemporalDate {
-  const value = dateObj as any
-
-  if (value && typeof value.with === 'function' && typeof value.add === 'function')
-    return value as TemporalDate
+function normalizeTemporalDate(dateObj: TemporalDate | { year: number; month: number; day?: number }): TemporalDate {
+  if ('with' in dateObj && typeof dateObj.with === 'function' && 'add' in dateObj && typeof dateObj.add === 'function')
+    return dateObj as TemporalDate
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/date/calendar.ts` around lines 17 - 32, The function
normalizeTemporalDate currently uses an any cast which weakens type safety;
change its signature to accept a safer input type (e.g., unknown or a new union
type like TemporalLike = Temporal.PlainDate | { year:number; month:number;
day?:number } | { with: Function; add: Function } | string) and add one or two
small type‑guard helpers (e.g., isTemporalObjectWithMethods(value): value is
Temporal.PlainDate and isYearMonthDay(value): value is {year:number;
month:number; day?:number}) to replace the any usage; update the function to use
those guards (and strong parameter/return annotations) instead of casting to any
so the checks are type‑checked and the return type remains Temporal.PlainDate.
docs/content/docs/components/time-field.md (1)

34-34: Add a hyperlink to the Temporal documentation.

The recommendation to read the Temporal proposal docs would be more useful with an actual link. Consider adding a hyperlink similar to how the polyfill link is provided on line 32.

📝 Suggested improvement
-We recommend reading the Temporal proposal docs to understand the model and immutability semantics used by Reka UI date-related components.
+We recommend reading the [Temporal proposal docs](https://tc39.es/proposal-temporal/docs/) to understand the model and immutability semantics used by Reka UI date-related components.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/docs/components/time-field.md` at line 34, Update the sentence
"We recommend reading the Temporal proposal docs to understand the model and
immutability semantics used by Reka UI date-related components." to include a
hyperlink to the Temporal proposal (e.g., the official TC39 Temporal docs or
proposal page), following the same markdown style used for the polyfill link on
the nearby line; ensure the anchor text mentions "Temporal proposal docs" and
the link opens the official Temporal documentation URL.
packages/core/src/Calendar/story/CalendarChromatic.story.vue (1)

90-91: Redundant type annotations in prop callbacks.

The type annotations (date: TemporalDate) in the inline arrow functions are unnecessary since the paging function already has proper typing and TypeScript can infer the parameter types from the prop definitions.

♻️ Suggested simplification
-        :next-page="(date: TemporalDate) => paging(date, 1)"
-        :prev-page="(date: TemporalDate) => paging(date, -1)"
+        :next-page="(date) => paging(date, 1)"
+        :prev-page="(date) => paging(date, -1)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/Calendar/story/CalendarChromatic.story.vue` around lines 90
- 91, The inline arrow callbacks for the props :next-page and :prev-page include
redundant parameter type annotations "(date: TemporalDate)"; remove the explicit
"(date: TemporalDate)" so TypeScript can infer the type from the paging function
and the prop definitions — change the callbacks to use date => paging(date, 1)
and date => paging(date, -1) (referencing the paging function and the
next-page/prev-page props) to simplify the code and eliminate unnecessary type
verbosity.
packages/core/src/temporal/index.ts (1)

1-15: Consider re-exporting types for a unified import path.

The module provides a convenient re-export of Temporal, but consumers still need to import types like TemporalDate from @/temporal/types separately. For better developer ergonomics, consider also re-exporting the types from this module.

📦 Suggested improvement
 import { Temporal } from 'temporal-polyfill'
 
 export { Temporal }
+export * from './types'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/temporal/index.ts` around lines 1 - 15, This module only
re-exports the Temporal runtime but not the related type aliases, forcing
consumers to import types separately; update the file that exports Temporal (the
current module exporting Temporal) to also re-export the type definitions (use
export type) for the commonly used symbols such as TemporalDate,
TemporalPlainDate, TemporalZonedDateTime (or the set of types defined in your
local types module) from the existing types module so consumers can import both
runtime and types from this single module while preserving tree-shaking.
docs/content/docs/components/range-calendar.md (1)

34-34: Add a direct link to the Temporal docs in the recommendation sentence.

This improves discoverability for readers following the migration guidance.

Suggested docs tweak
-We recommend reading the Temporal proposal docs to understand the model and immutability semantics used by Reka UI date-related components.
+We recommend reading the [Temporal proposal docs](https://tc39.es/proposal-temporal/docs/) to understand the model and immutability semantics used by Reka UI date-related components.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/docs/components/range-calendar.md` at line 34, Update the
sentence "We recommend reading the Temporal proposal docs to understand the
model and immutability semantics used by Reka UI date-related components." to
include a direct, inline link to the Temporal proposal (e.g., the official
Temporal proposal or MDN Temporal docs) so readers can click through
immediately; edit the text in docs/content/docs/components/range-calendar.md
replacing the plain mention of "Temporal proposal docs" with a hyperlink to the
chosen Temporal documentation URL.
docs/content/docs/components/year-range-picker.md (1)

32-34: Same suggestion: add a direct link to Temporal documentation.

For consistency with other component docs and improved developer experience, consider adding a hyperlink to the Temporal proposal documentation here as well.

📚 Suggested improvement
-We recommend reading the Temporal proposal docs to understand the model and immutability semantics used by Reka UI date-related components.
+We recommend reading the [Temporal proposal docs](https://tc39.es/proposal-temporal/docs/) to understand the model and immutability semantics used by Reka UI date-related components.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/docs/components/year-range-picker.md` around lines 32 - 34,
Update the paragraph that currently reads "We recommend reading the Temporal
proposal docs to understand the model and immutability semantics used by Reka UI
date-related components" to include a direct hyperlink to the Temporal proposal
documentation (for example: https://tc39.es/proposal-temporal/ or the docs page
https://tc39.es/proposal-temporal/docs/), so users can click through
immediately; find that sentence in year-range-picker.md and replace it with the
same sentence but with the Temporal proposal docs text wrapped as a link.
docs/content/docs/components/calendar.md (1)

32-34: Consider adding a direct link to the Temporal documentation.

The recommendation to read "Temporal proposal docs" would be more actionable with a hyperlink. This would improve developer experience by providing a clear path to the referenced documentation.

📚 Suggested improvement
-We recommend reading the Temporal proposal docs to understand the model and immutability semantics used by Reka UI date-related components.
+We recommend reading the [Temporal proposal docs](https://tc39.es/proposal-temporal/docs/) to understand the model and immutability semantics used by Reka UI date-related components.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/docs/components/calendar.md` around lines 32 - 34, Replace the
plain text "Temporal proposal docs" in the paragraph that mentions Temporal with
a direct hyperlink to the official Temporal specification (for example
https://tc39.es/proposal-temporal/) so readers can click through immediately;
update the sentence that currently reads "We recommend reading the Temporal
proposal docs to understand the model and immutability semantics used by Reka UI
date-related components." to include the hyperlink on "Temporal proposal docs".
packages/core/src/DateRangePicker/DateRangePicker.test.ts (1)

71-115: Add an edit-path assertion for datetime/zoned ranges.

These cases only verify initial rendering. A single start-hour/end-minute edit would lock in the Temporal datetime path and would have caught same-day short-circuit regressions in the picker wrapper.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/DateRangePicker/DateRangePicker.test.ts` around lines 71 -
115, The tests for CalendarDateTime and ZonedDateTime only check initial
rendering; add an edit-path assertion by simulating a user edit on a time
segment (e.g., use userEvent.type or fireEvent.change on
getByTestId('start-hour') or 'end-minute' in both tests that call setup with
modelValue calendarDateTime and zonedDateTime) and then assert the component
remained on the datetime edit path (for example: the time segment elements like
'start-hour'/'start-minute'/'start-second' are still present and reflect the
edited value), ensuring the picker did not short-circuit to a date-only path
after the edit.
packages/core/src/DatePicker/DatePicker.test.ts (1)

167-178: Consider using subtract for decrement operations.

While add({ [unit]: -1 }) is valid Temporal API, using subtract({ [unit]: 1 }) is more idiomatic and matches the approach used in TimeField.test.ts line 185.

♻️ Optional: Use subtract for decrements
     function cycle(segment: DateFields | TimeFields) {
       const unitMap = {
         year: 'years',
         month: 'months',
         day: 'days',
         hour: 'hours',
         minute: 'minutes',
         second: 'seconds',
       }
       const unit = unitMap[segment]
-      return String(zonedDateTime.add({ [unit]: -1 })[segment])
+      return String(zonedDateTime.subtract({ [unit]: 1 })[segment])
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/DatePicker/DatePicker.test.ts` around lines 167 - 178, The
test helper function cycle currently decrements the zonedDateTime by calling
zonedDateTime.add({ [unit]: -1 }); change this to the idiomatic
Temporal.subtract call: use zonedDateTime.subtract({ [unit]: 1 }) inside the
cycle function (keep the existing unitMap and return expression) so the
decrement matches the approach used in TimeField.test.ts and improves
readability.
packages/core/src/temporal/types.ts (1)

12-16: Consider renaming TemporalDateTime for clarity.

The name TemporalDateTime suggests a date+time combination, but the union includes Temporal.PlainTime which only has time components (no date). Consider a name like TemporalTimeValue or TemporalWithTime to better reflect that this represents "any Temporal type with time properties."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/temporal/types.ts` around lines 12 - 16, Rename the
confusing type TemporalDateTime to a clearer name (e.g., TemporalTimeValue or
TemporalWithTime) because the union includes Temporal.PlainTime (time-only) as
well as Temporal.PlainDateTime and Temporal.ZonedDateTime; update the type
declaration (formerly TemporalDateTime) and replace all references/usages across
the codebase to the new name so imports/exports remain consistent, and adjust
any JSDoc/comments mentioning TemporalDateTime to reflect the new identifier.
packages/core/src/shared/date/comparators.ts (1)

17-18: Unused locale parameter in GetDefaultDateProps.

The locale parameter is defined in the type but is no longer used in the function body after the migration (it was previously used with DateFormatter/createCalendar). Consider removing it if it's no longer needed to avoid confusion.

♻️ Proposed fix
 type GetDefaultDateProps = {
   defaultValue?: TemporalDate | TemporalDate[] | undefined
   defaultPlaceholder?: TemporalDate | undefined
   granularity?: Granularity
-  locale?: string
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/shared/date/comparators.ts` around lines 17 - 18,
GetDefaultDateProps currently declares a locale parameter that is never used;
remove the unused locale from the GetDefaultDateProps function signature and its
associated type definition (and any returned object properties) to avoid
confusion, or alternatively reintroduce its use if intended; locate the
GetDefaultDateProps function and its type in comparators.ts and either delete
the locale parameter and any references to it, or wire it back into the date
formatting/calendar creation logic (e.g., DateFormatter/createCalendar) so the
parameter is actually consumed.
packages/core/src/shared/useDateFormatter.ts (1)

40-55: Duplicate toDate helper exists in parser.ts.

This toDate function has nearly identical logic to the one in packages/core/src/shared/date/parser.ts. Consider extracting this to a shared utility (e.g., in @/temporal/comparators or a new @/temporal/utils) to avoid code duplication and ensure consistent behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/shared/useDateFormatter.ts` around lines 40 - 55, Extract
the duplicated toDate logic into a shared utility function (e.g., create a new
exported function toDate in a temporal utils module) and have both
packages/core/src/shared/useDateFormatter.ts and
packages/core/src/shared/date/parser.ts import and call that single toDate;
preserve the current behavior/signature (accepts TemporalDate and optional
timeZone defaulting to Temporal.Now.timeZoneId(), handles isZonedDateTime,
Temporal.PlainDateTime, and other Temporal types with plainTime midnight),
export the utility from the new module (or from an existing `@/temporal/`* module)
and replace the local toDate implementations with imports of that shared
function so behavior remains identical and duplication is removed.
packages/core/src/shared/date/parser.ts (1)

200-209: getTimeFieldValue returns null for dayPeriod but callers may not expect this.

The function handles hour, minute, and second but returns null for any other TimeSegmentPart including dayPeriod. While Line 23-24 handles dayPeriod separately before calling this function, consider adding a comment or making the contract explicit to prevent future misuse.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/shared/date/parser.ts` around lines 200 - 209,
getTimeFieldValue currently only returns numeric time parts and returns null for
other TimeSegmentPart values (including 'dayPeriod'), which can be surprising to
callers; update the function contract so it's explicit: either narrow the input
type to exclude 'dayPeriod' (e.g., accept only 'hour' | 'minute' | 'second') or
add a concise doc comment above getTimeFieldValue stating it only handles
numeric fields and will return null for non-numeric parts like 'dayPeriod' (and
prefer to keep existing behavior), and ensure any callers relying on numeric
values validate dayPeriod before calling (e.g., in the code that handles
dayPeriod separately). Include the symbol getTimeFieldValue and the union member
'dayPeriod' in your change so readers can find and understand the intended
usage.
packages/core/src/Calendar/CalendarCellTrigger.vue (1)

135-136: Long line reduces readability; consider extracting boundary checks.

This compound condition on a single line is hard to parse. Extracting the min/max boundary checks into separate variables or a helper would improve maintainability.

♻️ Suggested refactor
 function shiftFocus(day: TemporalDate, add: number) {
   const candidateDayValue = day.add({ days: add })
+  const candidatePlain = toPlainDate(candidateDayValue)
+  const isBelowMin = rootContext.minValue.value && Temporal.PlainDate.compare(candidatePlain, toPlainDate(rootContext.minValue.value)) < 0
+  const isAboveMax = rootContext.maxValue.value && Temporal.PlainDate.compare(candidatePlain, toPlainDate(rootContext.maxValue.value)) > 0

-  if ((rootContext.minValue.value && Temporal.PlainDate.compare(toPlainDate(candidateDayValue), toPlainDate(rootContext.minValue.value)) < 0) || (rootContext.maxValue.value && Temporal.PlainDate.compare(toPlainDate(candidateDayValue), toPlainDate(rootContext.maxValue.value)) > 0))
+  if (isBelowMin || isAboveMax)
     return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/Calendar/CalendarCellTrigger.vue` around lines 135 - 136,
The long compound boundary check should be split for readability: compute two
descriptive booleans (e.g., isBeforeMin and isAfterMax) using
toPlainDate(candidateDayValue) compared with
toPlainDate(rootContext.minValue.value) and
toPlainDate(rootContext.maxValue.value) via Temporal.PlainDate.compare, or move
that logic into a small helper like isOutOfBounds(candidateDayValue,
rootContext). Replace the single-line if with a simple if (isBeforeMin ||
isAfterMax) return, referencing candidateDayValue, rootContext.minValue,
rootContext.maxValue, toPlainDate and Temporal.PlainDate.compare to locate the
code in CalendarCellTrigger.vue.
packages/core/src/DateRangePicker/DateRangePickerRoot.vue (1)

205-206: Consider simplifying the object spread.

The destructuring { start: date.start, end: date.end } could be simplified to just date since DateRange already has this shape.

♻️ Minor simplification
   onDateChange(date: DateRange) {
-    modelValue.value = { start: date.start, end: date.end }
+    modelValue.value = date
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/DateRangePicker/DateRangePickerRoot.vue` around lines 205 -
206, The assignment in onDateChange currently reconstructs the DateRange by
writing { start: date.start, end: date.end }; simplify this by assigning the
DateRange directly to modelValue.value (i.e., set modelValue.value to date) in
the onDateChange handler to avoid redundant destructuring and preserve the
DateRange shape.
packages/core/src/DatePicker/DatePickerRoot.vue (1)

180-189: Simplify redundant null coalescing.

At line 182, date ?? undefined is redundant since date is already typed as TemporalDate | undefined.

♻️ Minor cleanup
   onDateChange(date: TemporalDate | undefined) {
     if (!date || !modelValue.value) {
-      modelValue.value = date ?? undefined
+      modelValue.value = date
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/DatePicker/DatePickerRoot.vue` around lines 180 - 189, In
onDateChange, simplify the redundant null coalescing by assigning date directly
instead of using "date ?? undefined": replace the assignment to modelValue.value
in the first branch so it sets modelValue.value = date; keep the existing logic
and references to modelValue, preventDeselect, and toPlainDate unchanged.
packages/core/src/shared/date/useDateField.ts (1)

398-413: Verify fallback behavior in dateTimeValueIncrementation.

The fallback at lines 411-412 uses day operations but this code path is reached when part is 'minute' or 'second' and the dateRef doesn't have an hour property. This appears to be unreachable dead code since:

  1. If part === 'minute', the minuteSecondIncrementation function should be called instead
  2. If part === 'second', same applies

However, if somehow reached, it would return incorrect values. Consider removing this fallback or throwing an error for unexpected parts.

🔧 Suggested improvement
-    const next = dateRef.with({ day: prevValue ?? dateRef.day }).add({ days: sign })
-    return next.day
+    throw new Error(`Unexpected part: ${part}`)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/shared/date/useDateField.ts` around lines 398 - 413, The
fallback branch in dateTimeValueIncrementation currently uses day arithmetic
(returning next.day) for cases where part is 'minute' or 'second' but dateRef
lacks an hour, which is unreachable and can produce incorrect values; update
dateTimeValueIncrementation to either remove this day-based fallback or replace
it with an explicit error for unexpected parts, and ensure
minuteSecondIncrementation is always routed for 'minute'/'second' cases; locate
the logic inside dateTimeValueIncrementation that checks part and dateRef.hour
and either delete the final day-based fallback block or throw a clear exception
mentioning the unexpected part, so incorrect values are not returned silently.
packages/core/src/Calendar/useCalendar.ts (1)

39-54: toDate helper correctly handles Temporal conversion.

The implementation properly handles all three Temporal date types with appropriate timezone handling. This is similar to the helper in useDateField.ts - consider extracting to a shared utility to avoid duplication.

♻️ Consider extracting to shared utility

Both useDateField.ts and useCalendar.ts define similar toDate helpers. Consider extracting to @/temporal/comparators or a dedicated conversion utility to maintain DRY principles.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/Calendar/useCalendar.ts` around lines 39 - 54, The toDate
implementation in useCalendar.ts duplicates logic found in useDateField.ts;
extract the conversion into a shared utility (e.g., create a new function
exported from a module like "@/temporal/toDate" or "@/temporal/comparators") and
replace the local toDate in both useCalendar.ts and useDateField.ts with an
import of that utility; ensure the exported function preserves the same
signature (toDate(dateValue: TemporalDate, timeZone?: string)), update imports
in both files to use the new module, and remove the duplicated helper
definitions so only the shared implementation is maintained.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 805359e5-2323-4227-9462-02bb79acf3c1

📥 Commits

Reviewing files that changed from the base of the PR and between 49d1d32 and aebd771.

⛔ Files ignored due to path filters (2)
  • playground/nuxt/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (144)
  • docs/components/demo/Calendar/tailwind/index.vue
  • docs/components/demo/CalendarSelect/css/index.vue
  • docs/components/demo/CalendarSelect/tailwind/index.vue
  • docs/components/demo/CalendarSwipe/css/index.vue
  • docs/components/demo/CalendarSwipe/tailwind/index.vue
  • docs/components/demo/CalendarYearIncrement/css/index.vue
  • docs/components/demo/CalendarYearIncrement/tailwind/index.vue
  • docs/components/demo/MonthPicker/css/index.vue
  • docs/components/demo/MonthPicker/tailwind/index.vue
  • docs/components/demo/MonthRangePicker/css/index.vue
  • docs/components/demo/MonthRangePicker/tailwind/index.vue
  • docs/components/demo/RangeCalendar/tailwind/index.vue
  • docs/components/demo/YearPicker/css/index.vue
  • docs/components/demo/YearPicker/tailwind/index.vue
  • docs/components/demo/YearRangePicker/css/index.vue
  • docs/components/demo/YearRangePicker/tailwind/index.vue
  • docs/components/examples/DatePickerSelection/index.vue
  • docs/components/examples/DatePickerViewSwitching/index.vue
  • docs/content/docs/components/calendar.md
  • docs/content/docs/components/date-field.md
  • docs/content/docs/components/date-picker.md
  • docs/content/docs/components/date-range-field.md
  • docs/content/docs/components/date-range-picker.md
  • docs/content/docs/components/month-picker.md
  • docs/content/docs/components/month-range-picker.md
  • docs/content/docs/components/range-calendar.md
  • docs/content/docs/components/time-field.md
  • docs/content/docs/components/time-range-field.md
  • docs/content/docs/components/year-picker.md
  • docs/content/docs/components/year-range-picker.md
  • docs/content/docs/guides/dates.md
  • docs/content/docs/guides/migration.md
  • docs/content/docs/utilities/use-date-formatter.md
  • docs/package.json
  • packages/core/package.json
  • packages/core/src/Calendar/Calendar.test.ts
  • packages/core/src/Calendar/CalendarCell.vue
  • packages/core/src/Calendar/CalendarCellTrigger.vue
  • packages/core/src/Calendar/CalendarNext.vue
  • packages/core/src/Calendar/CalendarPrev.vue
  • packages/core/src/Calendar/CalendarRoot.vue
  • packages/core/src/Calendar/story/CalendarChromatic.story.vue
  • packages/core/src/Calendar/story/CalendarFixedWeeksForLocales.story.vue
  • packages/core/src/Calendar/story/CalendarLocales.story.vue
  • packages/core/src/Calendar/story/CalendarMultiple.story.vue
  • packages/core/src/Calendar/story/CalendarPopover.story.vue
  • packages/core/src/Calendar/story/CalendarSelect.story.vue
  • packages/core/src/Calendar/story/CalendarValidation.story.vue
  • packages/core/src/Calendar/story/CalendarYearIncrement.story.vue
  • packages/core/src/Calendar/story/_Calendar.vue
  • packages/core/src/Calendar/story/_CalendarMultiple.vue
  • packages/core/src/Calendar/useCalendar.ts
  • packages/core/src/DateField/DateField.test.ts
  • packages/core/src/DateField/DateFieldRoot.vue
  • packages/core/src/DateField/story/DateFieldChromatic.story.vue
  • packages/core/src/DateField/story/_DateField.vue
  • packages/core/src/DatePicker/DatePicker.test.ts
  • packages/core/src/DatePicker/DatePickerCalendar.vue
  • packages/core/src/DatePicker/DatePickerField.vue
  • packages/core/src/DatePicker/DatePickerRoot.vue
  • packages/core/src/DatePicker/story/DatePickerChromatic.story.vue
  • packages/core/src/DatePicker/story/DatePickerValidation.story.vue
  • packages/core/src/DatePicker/story/_DatePicker.vue
  • packages/core/src/DateRangeField/DateRangeField.test.ts
  • packages/core/src/DateRangeField/DateRangeFieldRoot.vue
  • packages/core/src/DateRangeField/story/DateRangeFieldChromatic.story.vue
  • packages/core/src/DateRangeField/story/DateRangeFieldGranular.story.vue
  • packages/core/src/DateRangeField/story/DateRangeFieldValidation.story.vue
  • packages/core/src/DateRangeField/story/_DateRangeField.vue
  • packages/core/src/DateRangePicker/DateRangePicker.test.ts
  • packages/core/src/DateRangePicker/DateRangePickerCalendar.vue
  • packages/core/src/DateRangePicker/DateRangePickerField.vue
  • packages/core/src/DateRangePicker/DateRangePickerRoot.vue
  • packages/core/src/DateRangePicker/story/DateRangePickerChromatic.story.vue
  • packages/core/src/DateRangePicker/story/DateRangePickerValidation.story.vue
  • packages/core/src/DateRangePicker/story/_DateRangePicker.vue
  • packages/core/src/MonthPicker/MonthPicker.test.ts
  • packages/core/src/MonthPicker/MonthPickerCell.vue
  • packages/core/src/MonthPicker/MonthPickerCellTrigger.vue
  • packages/core/src/MonthPicker/MonthPickerNext.vue
  • packages/core/src/MonthPicker/MonthPickerPrev.vue
  • packages/core/src/MonthPicker/MonthPickerRoot.vue
  • packages/core/src/MonthPicker/story/_MonthPicker.vue
  • packages/core/src/MonthPicker/useMonthPicker.ts
  • packages/core/src/MonthRangePicker/MonthRangePicker.test.ts
  • packages/core/src/MonthRangePicker/MonthRangePickerCell.vue
  • packages/core/src/MonthRangePicker/MonthRangePickerCellTrigger.vue
  • packages/core/src/MonthRangePicker/MonthRangePickerNext.vue
  • packages/core/src/MonthRangePicker/MonthRangePickerPrev.vue
  • packages/core/src/MonthRangePicker/MonthRangePickerRoot.vue
  • packages/core/src/MonthRangePicker/useRangeMonthPicker.ts
  • packages/core/src/RangeCalendar/RangeCalendar.test.ts
  • packages/core/src/RangeCalendar/RangeCalendarCell.vue
  • packages/core/src/RangeCalendar/RangeCalendarCellTrigger.vue
  • packages/core/src/RangeCalendar/RangeCalendarNext.vue
  • packages/core/src/RangeCalendar/RangeCalendarPrev.vue
  • packages/core/src/RangeCalendar/RangeCalendarRoot.vue
  • packages/core/src/RangeCalendar/story/RangeCalendarChromatic.story.vue
  • packages/core/src/RangeCalendar/story/RangeCalendarMultiple.story.vue
  • packages/core/src/RangeCalendar/story/RangeCalendarPopover.story.vue
  • packages/core/src/RangeCalendar/story/RangeCalendarValidation.story.vue
  • packages/core/src/RangeCalendar/story/_RangeCalendar.vue
  • packages/core/src/RangeCalendar/useRangeCalendar.ts
  • packages/core/src/TimeField/TimeField.test.ts
  • packages/core/src/TimeField/TimeFieldRoot.vue
  • packages/core/src/TimeField/story/TimeFieldChromatic.story.vue
  • packages/core/src/TimeRangeField/TimeRangeField.test.ts
  • packages/core/src/TimeRangeField/TimeRangeFieldRoot.vue
  • packages/core/src/TimeRangeField/story/TimeRangeFieldChromatic.story.vue
  • packages/core/src/TimeRangeField/story/TimeRangeFieldGranular.story.vue
  • packages/core/src/TimeRangeField/story/TimeRangeFieldValidation.story.vue
  • packages/core/src/YearPicker/YearPicker.test.ts
  • packages/core/src/YearPicker/YearPickerCell.vue
  • packages/core/src/YearPicker/YearPickerCellTrigger.vue
  • packages/core/src/YearPicker/YearPickerNext.vue
  • packages/core/src/YearPicker/YearPickerPrev.vue
  • packages/core/src/YearPicker/YearPickerRoot.vue
  • packages/core/src/YearPicker/story/_YearPicker.vue
  • packages/core/src/YearPicker/useYearPicker.ts
  • packages/core/src/YearRangePicker/YearRangePicker.test.ts
  • packages/core/src/YearRangePicker/YearRangePickerCell.vue
  • packages/core/src/YearRangePicker/YearRangePickerCellTrigger.vue
  • packages/core/src/YearRangePicker/YearRangePickerNext.vue
  • packages/core/src/YearRangePicker/YearRangePickerPrev.vue
  • packages/core/src/YearRangePicker/YearRangePickerRoot.vue
  • packages/core/src/YearRangePicker/useRangeYearPicker.ts
  • packages/core/src/date/calendar.test.ts
  • packages/core/src/date/calendar.ts
  • packages/core/src/date/comparators.ts
  • packages/core/src/date/types.ts
  • packages/core/src/index.ts
  • packages/core/src/shared/date/comparators.ts
  • packages/core/src/shared/date/index.ts
  • packages/core/src/shared/date/parser.ts
  • packages/core/src/shared/date/types.ts
  • packages/core/src/shared/date/useDateField.ts
  • packages/core/src/shared/index.ts
  • packages/core/src/shared/useDateFormatter.ts
  • packages/core/src/temporal/calendar.ts
  • packages/core/src/temporal/comparators.ts
  • packages/core/src/temporal/index.ts
  • packages/core/src/temporal/types.ts
  • packages/core/vitest.setup.ts
  • playground/nuxt/package.json
💤 Files with no reviewable changes (1)
  • playground/nuxt/package.json

Comment thread packages/core/src/Calendar/CalendarRoot.vue
Comment thread packages/core/src/Calendar/useCalendar.ts
Comment thread packages/core/src/DateField/story/_DateField.vue
Comment thread packages/core/src/DateRangeField/story/DateRangeFieldChromatic.story.vue Outdated
Comment thread packages/core/src/temporal/comparators.ts
Comment thread packages/core/src/temporal/types.ts
Comment thread packages/core/src/YearPicker/YearPicker.test.ts
Comment thread packages/core/src/YearPicker/YearPickerCellTrigger.vue Outdated
Comment thread packages/core/src/YearPicker/YearPickerRoot.vue Outdated
@innocenzi
Copy link
Copy Markdown

@zernonia my apologies for the ping, feel free to ignore if busy, but would really appreciate iterating on this issue, as @internationalization/date is very frustrating to work with, and migrating to Temporal is the right thing to do for long term maintenance 🙏

@epr3 epr3 force-pushed the feat-2183-temporal-integration branch 2 times, most recently from 9d8e72f to 3556749 Compare May 4, 2026 20:58
@epr3
Copy link
Copy Markdown
Collaborator Author

epr3 commented May 4, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

epr3 added 11 commits May 31, 2026 23:21
Temporal date objects don't expose .era as a property for era
detection. Switch to Intl.DateTimeFormat.formatToParts() with
era: 'short' and inspect the 'era' part to decide when to
enable era formatting. Also normalize iso8601 calendar to gregory
in MonthPicker and YearPicker headingFormatOptions for consistency.
Move validation inside strict-bound loop so endDate is never
checked and start===end returns true. Add regression tests.
Migrate all DateValue type annotations to TemporalDate to align with
temporal integration and fix compilation errors.
…check

ISO today breaks non-ISO calendar year comparison; convert via toCalendar using placeholder's calendarId before isSameYear check
Normalized modelValue to array in onYearChange multiple branch to fix
ignored clicks when multiple=true and modelValue is a single TemporalDate.
Array may contain undefined when modelValue is single date wrapped. Filter fixes type-check and build.
Add operational guidance files for each workspace to standardize development workflows, verification commands, and cross-package sync rules.
Updates eslint 10.0.3→10.3.0, vite 8.0.0→8.0.10, esbuild 0.27→0.28,
antfu-eslint-config 7.7.2→8.2.0, vue-tsc 2.2.12→3.2.6, size-limit 12.x
added. Lockfile sync after dependency upgrades.
DateValue was legacy alias. TemporalDate is canonical type now.
@epr3 epr3 force-pushed the feat-2183-temporal-integration branch from 78773b9 to 28c44d9 Compare May 31, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Temporal API integration

2 participants