Skip to content

test(TimeInput, DateTimeInput): a stepped minute wheel loops like the full list under a finger - #8398

Draft
vjeux wants to merge 2217 commits into
meta:mainfrom
vjeux:uikit/c22-short-minute-loop
Draft

vjeux wants to merge 2217 commits into
meta:mainfrom
vjeux:uikit/c22-short-minute-loop

Conversation

@vjeux

@vjeux vjeux commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8209. Review only this PR's change: vjeux/astryx@46e8eae...1e4fcad

Maintainer impact

Under a finger, the TimeInput and DateTimeInput sheets' stepped minute wheel loops (#8179, through #8366's short loop), but no test of these sheets pins it at 15 or 30 minutes, where the list is shorter than the column and the wheel draws copies of its steps.

Intended invariant

The stepped minute wheel wraps both ways without carrying into the hour, draws copies to fill its column, and lists each step once for assistive technology, as UIDatePicker's minute wheel loops (spec:AST-043; spec:AST-094 FR1, FR2).

Change

Tests only, in TimeWheelsTouch.test.tsx. The loop and the minute step are described by #8179's changesets, so this PR carries none.

Evidence

  • Failure before the change: with the wheel's loop turned off, all three cases fail; with only its copies removed (loopCopies returning 1), the two TimeInput cases fail.
  • Success after the change: TimeInput at 15 and 30 minutes wraps both ways (10:45 → 10:00 → 10:45, 10:30 → 10:00 → 10:30) and its column shows copies, one option per step in the listbox; DateTimeInput at 11:45 PM wraps onto 11:00 PM the same evening and back.
  • Product/runtime behavior verified unchanged: no product file changes.

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

Stack (merge in this order): … → #8209 → this PR.

@vercel

vercel Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 30, 2026 12:46am UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
❓ Needs maintainer input (for maintainers only) Oct 30, 2026, 1:18 AM UTC

@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Oct 25, 2026
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 25, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: no current spec on main says the touch minute wheel should loop short stepped lists like UIDatePicker (spec:AST-043 only selects the sheet's time wheels, and spec:AST-094 is still in #8378), and the stack also rewrites the draft DateInput PR2 requirement for the month pager. An engineering owner records that decision by commenting /approve-spec <full head SHA> here, or by landing a current spec that covers the loop, and the author may do so as an engineering owner. Independently of that decision, please:

  • rebase once the predecessor pull requests land (or retarget onto #8366's branch), so this pull request's diff is its own 77-line commit rather than 52,142 authored lines;
  • drop the VelocityTracker re-export from packages/core/src/gestures/index.ts, which Lab's tsconfig maps, because architecture:interaction-modality INV10 says Lab cannot import the tracker;
  • describe the change in .changeset/short-minute-lists-loop.md against main, where the sheet never had a stepped minute column.

[Automated review]

Comment on lines +94 to +95
export {VelocityTracker} from './VelocityTracker';
export type {VelocityTrackerOptions} from './VelocityTracker';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

architecture:interaction-modality INV10 says the tracker "is not exported" and "Lab cannot import the tracker", and this is the barrel Lab's tsconfig maps as @astryxdesign/core/gestures. The four recognizers import ./VelocityTracker directly, so these two re-exports can go.

Comment thread .changeset/short-minute-lists-loop.md Outdated
'@astryxdesign/core': patch
---

[fix] Under a finger, the minute wheel in the TimeInput and DateTimeInput bottom sheets loops at every step, as iOS's date picker does. Stepped by 6, 10, 12, 15, 20 or 30 minutes it stopped at its first and last step; now copies of its steps fill the whole wheel, it wraps both ways, a tap lands on the copy tapped, and the hour does not carry (11:45 PM turns to 11:00 PM).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On main the sheet's minute column lists all 60 minutes and ignores increment; the stepped column arrives in this same stack with the time-wheels change. If both ship in one release, this reads as a fix for a bug no release had. Consider folding it into time-wheels-follow-uidatepicker.md, which this commit already edits.

Comment on lines +189 to +194
- **PR2 — Scroll and gesture work.** On the native month scroller, scroll-driven
measurement and selection updates remain animation-frame throttled or settle-driven.
On the month pager under a finger, a drag moves the month grid without a render, and the
visible month is reported once per half-month crossed, during the drag and the settle
that ends it.
Listeners, frames, timers, and observers are cleaned up on deactivation or unmount.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This turns PR2 into a new month-pager requirement that no current record states. The record is draft (approved_by: null), so an engineering owner needs to approve or reject this wording before it governs anything.

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR

🧪 Sandbox Preview

View Sandbox for this PR

Modified Components

DateInput (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 266B 266B 0B
Lines of Code 3532 6837 -
Complexity Very High Very High (617) -
DateTimeInput (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 286B 286B 0B
Lines of Code 3609 3792 -
Complexity Very High Very High (570) -
TimeInput (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Entry File (ESM) 411B 411B 0B
Lines of Code 1245 1728 -
Complexity Very High Very High (204) -

Component Size

Component Import before Import after Delta CSS before CSS after Delta
core/AlertDialog 56.9KB 57.2KB +294B 12.4KB 12.4KB 0B
core/AppShell 57.9KB 58.1KB +242B 11.6KB 11.6KB 0B
core/AvatarGroupOverflow 39.9KB 40.2KB +303B 6.4KB 6.4KB 0B
core/Banner 47.8KB 48.1KB +289B 9.9KB 9.9KB 0B
core/BaseTypeahead 54.3KB 54.6KB +251B 9.9KB 9.9KB 0B
core/BottomSheet 34.0KB 33.9KB -9B 7.9KB 7.9KB -22B
core/BottomSheetSwitcher 14.8KB 14.8KB 0B 3.3KB 3.3KB 0B
core/BreadcrumbItem 66.4KB 66.6KB +297B 11.0KB 11.0KB 0B
core/Button 43.3KB 43.5KB +273B 9.2KB 9.2KB 0B
core/Calendar 54.9KB 55.2KB +290B 10.0KB 10.0KB 0B
core/Carousel 50.2KB 50.5KB +316B 10.2KB 10.2KB 0B
core/ChatComposer 60.6KB 60.9KB +270B 10.8KB 10.8KB 0B
core/ChatComposerInput 55.7KB 56.0KB +264B 10.1KB 10.1KB 0B
core/ChatComposerTokenElement 55.7KB 56.0KB +264B 10.1KB 10.1KB 0B
core/ChatDictationButton 46.5KB 46.8KB +264B 9.5KB 9.5KB 0B
core/ChatLayout 48.8KB 49.1KB +273B 9.9KB 9.9KB 0B
core/ChatLayoutScrollButton 46.4KB 46.6KB +255B 9.6KB 9.6KB 0B
core/ChatSendButton 45.8KB 46.1KB +265B 9.2KB 9.2KB 0B
core/CheckboxInput 49.9KB 50.2KB +273B 9.3KB 9.3KB 0B
core/CheckboxListItem 54.0KB 54.3KB +244B 10.5KB 10.5KB 0B
core/ClickableCard 12.8KB 13.1KB +289B 6.9KB 6.9KB 0B
core/CodeBlock 60.5KB 60.8KB +278B 10.7KB 10.7KB 0B
core/Collapsible 11.7KB 12.0KB +291B 4.2KB 4.2KB 0B
core/CommandPalette 50.9KB 51.2KB +281B 11.8KB 11.8KB 0B
core/CommandPaletteItem 8.4KB 8.7KB +280B 3.5KB 3.5KB 0B
core/ComplexSelector 55.8KB 56.1KB +318B 10.6KB 10.6KB 0B
core/ContextMenu 77.7KB 78.0KB +275B 14.6KB 14.6KB -13B
core/ContextMenuItem 16.6KB 16.8KB +280B 5.2KB 5.2KB 0B
core/DateInput 99.9KB 146.8KB +46.9KB ⚠️ 14.8KB 15.1KB +355B
core/DateRangeInput 69.4KB 69.6KB +266B 11.2KB 11.2KB 0B
core/DateTimeInput 110.1KB 159.3KB +49.2KB ⚠️ 15.2KB 15.7KB +444B
core/DialogHeader 48.8KB 49.1KB +280B 9.8KB 9.8KB 0B
core/DrawerHeader 48.9KB 49.2KB +249B 11.2KB 11.2KB 0B
core/DropdownMenu 72.7KB 73.0KB +277B 14.7KB 14.7KB -12B
core/DropdownMenuCheckboxItem 16.1KB 16.4KB +284B 5.6KB 5.6KB 0B
core/DropdownMenuItem 15.4KB 15.7KB +287B 5.2KB 5.2KB 0B
core/DropdownMenuRadioItem 16.2KB 16.5KB +275B 5.6KB 5.6KB 0B
core/DropdownMenuSubMenu 58.1KB 58.3KB +270B 9.5KB 9.5KB 0B
core/FileInput 54.1KB 54.4KB +262B 10.3KB 10.3KB 0B
core/IconButton 43.3KB 43.6KB +273B 9.2KB 9.2KB 0B
core/Item 11.4KB 11.6KB +273B 4.7KB 4.7KB 0B
core/LayerProvider 63.3KB 63.6KB +288B 10.1KB 10.1KB 0B
core/Lightbox 51.2KB 51.4KB +249B 9.8KB 9.8KB 0B
core/Link 43.5KB 43.8KB +288B 7.6KB 7.6KB 0B
core/ListItem 53.5KB 53.7KB +268B 10.5KB 10.5KB 0B
core/Markdown 144.5KB 144.7KB +247B 17.4KB 17.3KB -14B
core/MobileNav 51.0KB 51.3KB +264B 10.1KB 10.1KB 0B
core/MobileNavToggle 46.2KB 46.5KB +259B 9.4KB 9.4KB 0B
core/MoreMenu 72.9KB 73.2KB +278B 14.7KB 14.7KB -12B
core/MultiSelector 76.2KB 76.4KB +270B 14.6KB 14.6KB -11B
core/NumberInput 55.7KB 56.0KB +278B 10.7KB 10.7KB 0B
core/Outline 28.6KB 28.9KB +290B 4.0KB 4.0KB 0B
core/Pagination 87.7KB 88.0KB +263B 15.3KB 15.3KB -17B
core/Popover 49.8KB 50.1KB +238B 12.1KB 12.1KB -9B
core/PowerSearch 155.1KB 203.6KB +48.5KB ⚠️ 18.6KB 19.0KB +384B
core/RadioListItem 52.1KB 52.4KB +286B 9.7KB 9.7KB 0B
core/SegmentedControlItem 8.6KB 8.9KB +285B 3.9KB 3.9KB 0B
core/SelectableCard 12.8KB 13.1KB +294B 7.1KB 7.1KB 0B
core/Selector 79.1KB 79.4KB +267B 15.0KB 15.0KB -14B
core/SelectorOption 14.5KB 14.8KB +251B 5.1KB 5.1KB 0B
core/SideNav 58.0KB 58.3KB +272B 10.6KB 10.6KB 0B
core/SideNavCollapseButton 46.6KB 46.9KB +242B 9.5KB 9.5KB 0B
core/SideNavHeading 54.7KB 55.0KB +248B 10.1KB 10.1KB 0B
core/SideNavItem 54.5KB 54.8KB +266B 10.1KB 10.1KB 0B
core/Step 32.4KB 32.7KB +279B 5.3KB 5.3KB 0B
core/Stepper 47.4KB 47.7KB +265B 9.6KB 9.6KB 0B
core/Switch 48.4KB 48.7KB +285B 9.3KB 9.3KB 0B
core/Tab 9.7KB 10.0KB +294B 4.1KB 4.1KB 0B
core/TabMenu 51.6KB 51.9KB +276B 10.0KB 10.0KB 0B
core/Table 84.0KB 84.2KB +253B 15.1KB 15.1KB -12B
core/TableCell 78.8KB 79.1KB +267B 14.8KB 14.8KB -13B
core/TableHeaderCell 78.6KB 78.8KB +272B 14.8KB 14.8KB -13B
core/TableSelectionToolbar 52.4KB 52.6KB +278B 11.2KB 11.2KB 0B
core/TextInput 53.5KB 53.8KB +281B 10.5KB 10.5KB 0B
core/Thumbnail 50.7KB 50.9KB +274B 11.0KB 11.0KB 0B
core/TimeInput 86.8KB 116.5KB +29.7KB ⚠️ 13.8KB 13.8KB +19B
core/Timestamp 27.5KB 27.5KB -1B 9.5KB 9.5KB 0B
core/Toast 59.8KB 60.1KB +258B 9.7KB 9.7KB 0B
core/ToggleButton 44.1KB 44.4KB +243B 9.3KB 9.3KB 0B
core/Token 29.6KB 29.9KB +289B 4.5KB 4.5KB 0B
core/Tokenizer 64.8KB 65.1KB +306B 11.7KB 11.7KB 0B
core/TopNav 51.9KB 52.1KB +277B 10.4KB 10.4KB 0B
core/TopNavHeading 54.2KB 54.4KB +231B 9.9KB 9.9KB 0B
core/TopNavItem 9.0KB 9.3KB +283B 3.8KB 3.8KB 0B
core/TopNavMegaMenu 54.2KB 54.4KB +268B 10.8KB 10.8KB 0B
core/TopNavMegaMenuItem 9.1KB 9.4KB +281B 4.1KB 4.1KB 0B
core/TopNavMenu 53.6KB 53.8KB +258B 10.1KB 10.1KB 0B
core/TreeList 32.5KB 32.8KB +274B 4.7KB 4.7KB 0B
core/Typeahead 61.5KB 61.7KB +268B 11.3KB 11.3KB 0B

Sizes are gzip. Import counts the JavaScript the import loads eagerly; CSS counts the package stylesheet rules referenced by the modules it can load from its own package, lazily loaded ones included.

⚠️ Touch-only size budget

These components grew past the touch-only budget (2 KB gzip JavaScript or 1 KB gzip CSS per component). If the growth runs only on a coarse pointer, load its JavaScript lazily so the import does not carry it, or justify the cost in the pull request description; CSS stays in the package stylesheet, so justify it.

Component JavaScript CSS
core/DateInput +46.9KB ⚠️ +355B
core/DateTimeInput +49.2KB ⚠️ +444B
core/PowerSearch +48.5KB ⚠️ +384B
core/TimeInput +29.7KB ⚠️ +19B

Bundle Size Summary

Gzip figures measure each package's entry file, not the code an import pulls in.

Package Entry (ESM) Entry gzip before Entry gzip after Delta
@astryxdesign/core 4.9KB 1.2KB 1.2KB 0B
Package stylesheet Gzip before Gzip after Delta
@astryxdesign/core 34.6KB 34.9KB +337B

Accessibility Audit

Status: No accessibility violations detected.

Visual Regression

55 of 502 shot(s) changed.

A change here is a question, not a failure: check whether the after is the
picture you intended. Record that review on the PR. Baseline maintenance is an
explicit dispatch of CI; this report never rewrites the baseline or adds a release gate.

component story theme mode pixels
Selector Indicator Space Bottom Sheet Narrow End RTL neutral light 753,933
Selector Indicator Space Bottom Sheet Wide Start neutral light 740,072
RichTextEditor Markdown parity: side by side probe dark 558,840
RichTextEditor Markdown parity: side by side probe light 540,756
Chat Full AI Chat probe light 230,823
Chat Full AI Chat probe dark 229,805
AppShell With Mobile Nav probe light 217,010
AppShell With Mobile Nav probe dark 216,960
Collapsible Dividers probe dark 66,445
Collapsible Dividers probe light 66,445
AppShell Playground neutral dark 65,198
AppShell Playground neutral light 63,788
Chat Default neutral dark 36,304
Chat Default neutral light 35,767
Chat Empty State probe light 23,531
Chat Empty State probe dark 22,622
ChatAutoScroll Scroll Behavior Comparison neutral dark 15,378
ChatAutoScroll Scroll Behavior Comparison neutral light 14,107
Chat With Attachments probe light 13,406
Chat With Attachments probe dark 11,232

and 35 more.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: no current spec on main says the touch minute wheel should loop a short stepped list with copies around the cylinder and no hour carry, and the stack also adds a new month-pager requirement to the draft DateInput PR2. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, or by landing a current spec that states it, and the author may do so as an engineering owner. Independently of that decision, please:

  • rebase once the predecessor pull requests land (or retarget onto #8366's branch), so this pull request's diff is its own 77-line change instead of 54,672 authored lines;
  • remove the VelocityTracker re-export from packages/core/src/gestures/index.ts and keep the tracker off Lab's internal import paths in scripts/core-internal-modules.cjs, as architecture:interaction-modality INV10 requires;
  • treat the stepped sheet minute column as a breaking change for a planned minor under spec:AST-017 FR31 (or keep the sheet listing every minute), because DateTimeInput documents timeIncrement as ignored by the touch sheet;
  • describe .changeset/short-minute-lists-loop.md against main, where the sheet's minute column never stopped at its steps.

[Automated review]

Comment on lines +92 to +93
export {VelocityTracker} from './VelocityTracker';
export type {VelocityTrackerOptions} from './VelocityTracker';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

architecture:interaction-modality INV10 says the tracker "is not exported" and "Lab cannot import the tracker", and its verification lists "an export of the tracker" as a failure. This barrel is what Lab's tsconfig maps as @astryxdesign/core/gestures; the recognizers import ./VelocityTracker directly, so these two lines can go.

Comment thread scripts/core-internal-modules.cjs Outdated

/** The internal module paths Lab imports: motion, gestures, the touch resolver. */
const CORE_INTERNAL_SPECIFIER =
/^@astryxdesign\/core\/((?:motion|gestures)(?:\/[\w.-]+)*|utils\/touchPresentation)$/;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This admits every gestures/* subpath, so @astryxdesign/core/gestures/VelocityTracker resolves for Lab too. INV10 says Lab cannot import the tracker: exclude it here, or refuse it in lab-core-imports.test.mjs.


- The AM/PM column follows the hour. Turning the hour from 11 to 12 now gives 12 PM, not 12 AM, and the AM/PM column turns onto PM.
- The hour, minute and second columns loop. Minutes no longer carry into the hour.
- The minute column steps by TimeInput's `increment` or DateTimeInput's `timeIncrement` when it divides 60. A time between two steps shows rounded to the nearest step inside the day and the bounds until a wheel moves, and the seconds stay as they are.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On main, DateTimeInput documents timeIncrement as "Ignored by the Astryx touch sheet". Making it set the sheet's minute column takes the minutes between steps away from consumers who set it for arrow keys. spec:AST-017 FR25 and FR31 treat that as a [breaking] change for a declared minor, not a patch [fix].

Comment thread .changeset/short-minute-lists-loop.md Outdated
'@astryxdesign/core': patch
---

[fix] Under a finger, the minute wheel in the TimeInput and DateTimeInput bottom sheets loops at every step, as iOS's date picker does. Stepped by 6, 10, 12, 15, 20 or 30 minutes it stopped at its first and last step; now copies of its steps fill the whole wheel, it wraps both ways, a tap lands on the copy tapped, and the hour does not carry (11:45 PM turns to 11:00 PM).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On main the sheet's minute column lists all 60 minutes and ignores increment, so no release had a stepped column that stopped at its ends. Consider folding this into time-wheels-follow-uidatepicker.md and describing it against main.

Comment on lines +189 to +193
- **PR2 — Scroll and gesture work.** On the native month scroller, scroll-driven
measurement and selection updates remain animation-frame throttled or settle-driven.
On the month pager under a finger, a drag moves the month grid without a render, and the
visible month is reported once per half-month crossed, during the drag and the settle
that ends it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This turns PR2 into a month-pager requirement that no current record states. The record is draft (approved_by: null), so an engineering owner needs to approve or reject this wording before it governs anything.

@cixzhang

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf.

For the picker wheels in this stack: Cindy decided in #8131 that a wheel coming to rest on a disabled row continues to the nearest allowed row (a tie goes toward the current value) and selects it, as on iOS, and that opening a bounded sheet never changes an untouched value. Please keep this PR consistent with that rule: #8131 (comment)

@vjeux

vjeux commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Consistent at 2085770: this PR adds no rest logic of its own. Its minute column is #8366's wheel, which leaves a disabled copy for the nearest enabled one (a tie toward the committed one), and what a rest commits is #8179's TimeWheels, which now goes on past a bound to the nearest step inside it (b67d6f5). Opening the sheet never changes an untouched value: tests in TimeWheelsTouch.test.tsx for both sheets.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: no current spec on main says the touch sheet's minute wheel should step by increment/timeIncrement and loop a short stepped list (copies around the cylinder, no hour carry), and the stack also adds a new month-pager requirement to the draft component:DateInput PR2. spec:AST-094 is now current, but it leaves each component's presentation to that component's own record. An engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, or by landing a current spec that states it; the author may do so as an engineering owner. Independently of that decision, please:

  • rebase once #8209 and #8366 land (or retarget onto #8366's branch), so the diff is this pull request's own 77-line change instead of 55,039 authored lines;
  • ship the stepped sheet minute column as [breaking] for a declared minor under spec:AST-017 FR25 and FR31 (or keep the sheet listing every minute), because DateTimeInput documents timeIncrement as ignored by the touch sheet;
  • replace .changeset/short-minute-lists-loop.md and .changeset/wheel-short-loop.md with one entry described against main, where the sheet's minute column never stopped at its steps;
  • skip the earlier request to drop the VelocityTracker re-export: architecture:interaction-modality INV10 on main no longer bars Lab from importing the tracker, and Core's internal gestures barrel is not a package export.

[Automated review]


- The AM/PM column follows the hour. Turning the hour from 11 to 12 now gives 12 PM, not 12 AM, and the AM/PM column turns onto PM.
- The hour, minute and second columns loop. Minutes no longer carry into the hour.
- The minute column steps by TimeInput's `increment` or DateTimeInput's `timeIncrement` when it divides 60. A time between two steps shows rounded to the nearest step until a wheel moves (inside the day, and inside the bounds when the time is), and the seconds stay as they are.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On main, DateTimeInput documents timeIncrement as "Ignored by the Astryx touch sheet". Stepping the sheet's minute column by it takes the off-step minutes away from touch users of callers who set it for arrow keys. spec:AST-017 FR25 and FR31 make that a [breaking] change for a declared minor, not a patch [fix], and main declares 0.7.5.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: no current spec on main says the touch sheet's minute wheel should step by increment/timeIncrement and loop a short stepped list (copies around the cylinder, a tap landing on the tapped copy, no hour carry), and the stack also adds two new requirements to the draft component:DateInput record, PR2's half-month title report for the month pager and AR8's Shift+PageUp/Shift+PageDown year step. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, or by landing a current spec that states them, and the author may do so as an engineering owner. Independently of that decision, please:

  • rebase once #8209 and #8366 land (or retarget onto #8366's branch), so the diff is this pull request's own 77-line change instead of 54,624 authored lines, 29 of whose added files main now already holds;
  • keep DateTimeInput's sheet listing every minute until a declared minor, or ship the step there as [breaking] under spec:AST-017 FR25 and FR31, because main documents timeIncrement as ignored by the touch sheet;
  • replace .changeset/short-minute-lists-loop.md and .changeset/wheel-short-loop.md with one entry described against main, where the sheet's minute column never stopped at its steps.

[Automated review]


- The AM/PM column follows the hour. Turning the hour from 11 to 12 now gives 12 PM, not 12 AM, and the AM/PM column turns onto PM.
- The hour, minute and second columns loop. Minutes no longer carry into the hour.
- The minute column steps by TimeInput's `increment` or DateTimeInput's `timeIncrement` when it divides 60. A time between two steps shows rounded to the nearest step until a wheel moves (inside the day, and inside the bounds when the time is), and the seconds stay as they are.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On main, DateTimeInput documents timeIncrement as “Ignored by the Astryx touch sheet, which uses wheels”. Stepping the sheet's minute wheel by it removes off-step minutes for touch users of callers who set it for arrow keys, with no prop that keeps them; spec:AST-017 FR1, FR25 and FR31 make that a [breaking] change for a declared minor, not a patch [fix], and main declares 0.7.5.

Comment thread .changeset/short-minute-lists-loop.md Outdated
'@astryxdesign/core': patch
---

[fix] Under a finger, the minute wheel in the TimeInput and DateTimeInput bottom sheets loops at every step, as iOS's date picker does. Stepped by 6, 10, 12, 15, 20 or 30 minutes it stopped at its first and last step; now copies of its steps fill the whole wheel, it wraps both ways, a tap lands on the copy tapped, and the hour does not carry (11:45 PM turns to 11:00 PM).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On main the sheet's minute column lists all 60 minutes and never stopped at its steps, and in this same diff time-wheels-follow-uidatepicker.md says every column loops while wheel-short-loop.md says no column loops yet. Consider folding the short-list loop into time-wheels-follow-uidatepicker.md and describing it against main.

Comment on lines +196 to +198
On the month pager under a finger, a drag moves the month grid without a render, and the
visible month is reported once per half-month crossed, during the drag and the settle
that ends it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The render-free drag is already spec:AST-094 FR9; reporting the visible month once per half-month crossed is a new requirement no current record states. The record is draft, so an engineering owner approves or rejects this wording.

Comment on lines +232 to +233
picks the focused day. Named exception: Shift+PageUp and Shift+PageDown move
by a year, as in the APG date picker dialog.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shift+PageUp and Shift+PageDown moving the focus by a year is new at this head and stated by no current record (the Calendar record, also draft, lists only PageUp and PageDown). An engineering owner approves or rejects it with the rest of the open spec question.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: no current spec on main says the touch sheet's minute wheel should step by increment/timeIncrement and loop a short stepped list (copies around the cylinder, a tap landing on the tapped copy, no hour carry), and the stack also adds two new requirements to the draft component:DateInput record, PR2's half-month title report for the month pager and AR8's Shift+PageUp/Shift+PageDown year step. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, or by landing a current spec that states them, and the author may do so as an engineering owner. Independently of that decision, please:

  • rebase once #8209 and #8366 land (or retarget onto #8366's branch), so the diff is this pull request's own 77-line change instead of 49,729 authored lines that currently conflict with main;
  • keep DateTimeInput's sheet listing every minute until a declared minor, or ship the step there as [breaking] under spec:AST-017 FR25 and FR31, because main documents timeIncrement as ignored by the touch sheet;
  • fold .changeset/short-minute-lists-loop.md and .changeset/wheel-short-loop.md into one entry described against main, where the sheet's minute column never stopped at its steps.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This needs an engineering owner's spec decision before it can be approved: no current spec on main says the touch sheet's minute wheel should step by increment/timeIncrement and loop a short stepped list (copies around the wheel, a tap landing on the tapped copy, no hour carry), and the stack also adds two new requirements to the draft component:DateInput record, PR2's half-month title report for the month pager and AR8's Shift+PageUp/Shift+PageDown year step. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, or by landing a current spec that states them, and the author may do so as an engineering owner. Independently of that decision, please:

  • rebase once #8209 and #8366 land (or retarget onto #8366's branch), so the diff is this pull request's own 77-line change instead of 49,768 authored lines that now conflict with main in 13 files;
  • keep DateTimeInput's sheet listing every minute until a declared minor, or ship the step there as [breaking] under spec:AST-017 FR25 and FR31, because main documents timeIncrement as ignored by the touch sheet;
  • fold .changeset/short-minute-lists-loop.md and .changeset/wheel-short-loop.md into one entry described against main, where the sheet's minute column never stopped at its steps.

[Automated review]


- The AM/PM column follows the hour. Turning the hour from 11 to 12 now gives 12 PM, not 12 AM, and the AM/PM column turns onto PM.
- The hour, minute and second columns loop. Minutes no longer carry into the hour.
- The minute column steps by TimeInput's `increment` or DateTimeInput's `timeIncrement` when it divides 60. A time between two steps shows rounded to the nearest step until a wheel moves (inside the day, and inside the bounds when the time is), and the seconds stay as they are.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On main, DateTimeInput documents timeIncrement as "Ignored by the Astryx touch sheet, which uses wheels". Stepping the sheet's minute wheel by it removes off-step minutes for touch users of callers who set it for arrow keys, with no prop that keeps them; spec:AST-017 FR1, FR25 and FR31 make that a [breaking] change for a declared minor, not a patch [fix], and main declares 0.7.5.

Comment thread .changeset/short-minute-lists-loop.md Outdated
'@astryxdesign/core': patch
---

[fix] Under a finger, the minute wheel in the TimeInput and DateTimeInput bottom sheets loops at every step, as iOS's date picker does. Stepped by 6, 10, 12, 15, 20 or 30 minutes it stopped at its first and last step; now copies of its steps fill the whole wheel, it wraps both ways, a tap lands on the copy tapped, and the hour does not carry (11:45 PM turns to 11:00 PM).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On main the sheet's minute column lists all 60 minutes and never stopped at its steps, and wheel-short-loop.md in this same diff says no column loops yet (and lists only 10, 15, 20 and 30). Consider folding the short-list loop into time-wheels-follow-uidatepicker.md and describing it against main.

Comment on lines +196 to +198
On the month pager under a finger, a drag moves the month grid without a render, and the
visible month is reported once per half-month crossed, during the drag and the settle
that ends it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The render-free drag is already spec:AST-094 FR9; reporting the visible month once per half-month crossed is a new requirement no current record states. The record is draft, so an engineering owner approves or rejects this wording.

Comment on lines +232 to +233
picks the focused day. Named exception: Shift+PageUp and Shift+PageDown move
by a year, as in the APG date picker dialog.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shift+PageUp and Shift+PageDown moving the focus by a year is new wording stated by no current record (the draft Calendar record lists only PageUp and PageDown). An engineering owner approves or rejects it with the rest of the open spec question.

vjeux added 19 commits October 29, 2026 13:45
…o their own changesets

In the same release meta#8179's time columns loop, so "no shipped column loops
yet" would contradict it; the columns that loop say so themselves.
…heirs) into uikit/c21-wheel-rest-and-spin
… while the toggle fades

The header's arrows and Reset, and DateInput's footer actions, sit outside the shield that covers the panels during the month/year cross-fade, and their `inert` followed the panels' (deferred to the fade's end). During the opening fade a tap on Done fell through the still-inert Done to Save beneath it and closed the DateInput sheet, and DateTimeInput's fading arrows and Reset still paged the month or cleared the pick. These few controls now leave with the toggle and arrive with the fade's end; only the panels' own `inert` waits for the fade, since flipping it restyles a panel's whole subtree.
… the glyph is

Under Reduce Motion on a touch screen, a second title tap within the 200 ms dip restarted it from opacity 1, flashing the chevron back to opaque. The new dip now starts from the running dip's presented opacity, read before it is cancelled (a style read only in that case).
…llows the toggle during render

`useFadeInert` mirrored the toggle into state from a layout effect when the swap is not deferred, costing a second commit per toggle (architecture:react-component-runtime INV1, INV3). `settled` now follows `isWheelOpen` during render in that case; the effect keeps only the branch where the committed panel shows no exit can run (no Web Animations), with a reason naming that read.
…fect synchronizes with

The effect waits on the browser's clock and idle time (a timer for the sheet's present motion, then an idle callback), and its cleanup cools the wheels for the next opening (architecture:react-component-runtime INV3).
…ged, and where it was measured

The panels' `inert` swaps when the fade ends (not a frame later), the commit's restyle is described without claiming it changes no inherited style, the old chevron and the 0.01 s fade are DateInput's, and the iPhone and Android claims are narrowed to the iOS simulator measurement.
…s own spin

meta#8527's `spinTo` no longer takes a duration or a curve, so a column the time wheels move by themselves turns on the wheel's own spin: an animated `setContentOffset`, 0.3 s on Core Animation's ease-in-ease-out (`picker.tapSpin`, the role `timing.curves.move`), resolved under the column when the turn starts (spec:AST-094 FR4). TimeWheels no longer plays the literal `datePicker.wheels.columnTurn`, and the browser proof samples the AM/PM column's turn against that spin.
…ay round its seam

Under min 13:00 and max 23:00 the hours 0 to 12 are disabled; one ArrowDown from 23 crosses the seam onto 0, and the nearest enabled hour around the loop is 23 itself, so nothing moves or commits. It rested on 13 before meta#8527's seam-aware rest (f4386c5).
…he wheel's own spin

meta#8527's `spinTo` no longer takes a duration or a curve: the month wheel turns onto the nearest reachable month on the wheel's own spin (`picker.tapSpin` on the role `timing.curves.move`, UIKit's animated `setContentOffset`), resolved when the turn starts (spec:AST-094 FR4), not on the literal `datePicker.wheels.columnTurn`.
…he nearest enabled month around the seam

With min 2025-11-01, max 2026-02-28 and January 25, 2026 chosen, one ArrowUp from January crosses the seam onto December 2026, past max; around the loop the nearest enabled month is January itself, so nothing moves or commits. It went to February 25 before meta#8527's seam-aware rest and key step (f4386c5).

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approval waits on an engineering owner's spec decision: no current record states that, under a finger, increment and timeIncrement step the TimeInput and DateTimeInput sheet minute wheel and that a short list loops with drawn copies, while component:TimeInput defines Increment as the typed field's arrow-key step. An engineering owner can settle it by landing records that state this behaviour (the component:TimeInput update, an INV7 exception for the copies, and the draft component:DateInput PR2, AR8 and FR13 and component:DateTimeInput FR8 wording), or by commenting /approve-spec <full head SHA> on this pull request once it carries that wording; the author may do so as an engineering owner. Whatever is decided, these still need changes:

  • .changeset/time-wheels-minute-step.md ships a released, documented change to timeIncrement as a patch [feat], which spec:AST-017 FR25 and FR31 admit only as a [breaking] changeset in a declared minor.
  • Against main the diff is the whole open stack (about 50,000 authored lines around a 68-line change), so landing #8209 and #8366 first would leave this pull request's own change.
  • The flagged touch-only size growth needs figures against main and a justification (INV6), and the wheels' new mouse behaviour (commit on release, travel on from a disabled row) needs fine-pointer before-and-after evidence (spec:AST-017 FR59).

[Automated review]

| FR10 | Standalone mode MUST compose Field; admitted InputGroup mode MUST remove competing Field chrome while preserving name, description, value, keyboard, focus, and editing semantics. | `family:input-fields/FR3`; current source and InputGroup tests | Settled shared rule and verified adoption |
| FR11 | Clear MUST emit `undefined` through the ordinary change pipeline. The pointer and bottom-sheet surfaces restore field focus using their shipped timing; the native surface MUST NOT reclaim focus because doing so reopens the OS picker. Implementation gap: when the field becomes disabled or busy while its clear control holds focus, the control unmounts and focus falls to the document body; `spec:AST-085/FR4` requires focus to stay or move to a destination this record names. | Current source and focused clear tests; `spec:AST-085/FR4` | Verified shipped behavior |
| FR12 | No surface renders native `disabled`. A disabled field MUST stay focusable with `aria-disabled`, block typing, picking, clearing, and opening, describe its disabled reason when one is given, and take disabled paint. While busy (`isLoading` or a pending Action) the pointer and bottom-sheet surfaces expose `aria-disabled` and `aria-busy`, stay focusable, and block new commits; the native surface does the same for `isLoading`, and during a pending Action stays editable so the OS picker remains attached but rejects additional commits until the Action settles. A busy field that is not disabled keeps its ordinary paint and Spinner, shows no hover ring while commits are blocked, and keeps its keyboard focus ring. | `spec:AST-085/FR1–FR6`; `family:input-fields/FR4–FR6`; `family:buttons/FR4`; current source and focused tests | Settled shared rule and verified adoption |
| FR13 | A bottom-sheet picker wheel that comes to rest on a disabled row (outside `min`/`max`) MUST continue to the nearest enabled row, a tie going toward the committed row, and select it there. A wheel resting on its committed row MUST NOT select or commit, even when that row is outside the bounds, so opening a bounded sheet never changes an untouched value: a committed value outside the bounds stays, and an empty field stays empty. A shown wheel whose value changes from outside, as when a bound clamps the time after another wheel turned, MUST turn onto the new row rather than jump to it. | DEC-1; UIDatePicker on the iOS 26.2 simulator; wheel unit and real-browser tests | Settled by DEC-1 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This row's last sentence (a shown wheel whose value changes from outside MUST turn onto the new row) and its second sentence applied after a drag (a rest back on a committed value outside the bounds commits nothing) go beyond cixzhang's #8131 decision that DEC-1 records, so "Settled by DEC-1" overstates them; they wait on an engineering owner's approval.

| FR5 | The two pointer segments share one row while each retains its intrinsic basis; when the container is narrower, flex wrapping moves them into separate full-width rows. | Public docs, current source, focused layout tests | Verify rendered geometry; class-level behavior covered |
| FR6 | Date-time bounds and date constraints MUST prevent invalid commits. Native controls MAY receive platform hints, but JavaScript validation remains the final commit boundary; calendar-only options MAY be unavailable on native surfaces. | Public docs, current source, focused presentation tests | Verified observation; owner approval pending |
| FR7 | A preset-time listbox MUST remain optional, preserve typed entry, expose one selected option independently from keyboard highlight, and limit offered values to the selected date's effective minimum and maximum. | Public docs, current source, focused interaction tests | Verified observation; owner approval pending |
| FR8 | Its bottom-sheet date and time wheels rest as `component:DateInput/FR13` requires: a wheel stopping on a disabled row continues to the nearest enabled row and selects it, opening a bounded sheet never changes an untouched value, and a shown wheel turns onto a value a bound clamps. | `component:DateInput/DEC-1`; wheel unit tests | Settled by `component:DateInput/DEC-1` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The added clause "a shown wheel turns onto a value a bound clamps" is not part of the #8131 decision, so "Settled by component:DateInput/DEC-1" overstates it; it waits on the same owner decision as DateInput FR13's last sentence.

'@astryxdesign/core': patch
---

[feat] Under a finger, the minute wheel in the TimeInput and DateTimeInput bottom sheets steps by TimeInput's `increment` or DateTimeInput's `timeIncrement` when it divides 60, and loops at every step, as iOS's date picker does. A time between two steps shows rounded to the nearest step until a wheel moves (inside the day, and inside the bounds when the time is), and the seconds stay as they are. With a mouse, the wheels still list every minute.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Released 0.7.4 documents timeIncrement as "Ignored by the Astryx touch sheet, which uses wheels", and a caller who set it for the desktop arrow keys must drop it to give touch users every minute back, so spec:AST-017 FR1, FR25 and FR31 make this a [breaking] change for a declared minor (main declares 0.7.5), not a patch [feat].

vjeux added 5 commits October 29, 2026 17:34
…names what it synchronizes

The un-keyed layout effect that lands a far jump synchronizes the pager's position with the DOM: it can move only once the render that mounts the target's pane has committed it, and moves before paint (architecture:react-component-runtime INV3).

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approval waits on an engineering owner's spec decision: under a finger this change makes increment and timeIncrement step and loop the TimeInput and DateTimeInput sheet minute wheel with drawn copies, while the current component:TimeInput spec defines Increment as the typed field's arrow-key step, and it adds new wording to the draft component:DateInput and component:DateTimeInput records (PR2, AR8, FR13's rest-back and turn clauses, FR8). An engineering owner records that decision by landing spec updates that state it (the TimeInput Increment row, FR7 and ORD3, plus a recorded INV7 exception for the copies) or by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Independent of that decision, these still need changes:

  • .changeset/time-wheels-minute-step.md changes released, documented timeIncrement behaviour in a patch [feat], so it needs a [breaking] changeset in a declared minor, or the sheet should keep every minute until then.
  • The diff against main carries the whole open stack (about 50,000 authored lines), and landing #8209 and #8366 first would leave only this change to review.
  • The touch-only size growth needs figures against main with a justification, and the mouse-drag and mouse-rest wheel changes need fine-pointer before-and-after evidence.

[Automated review]

@vjeux vjeux changed the title fix(TimeInput, DateTimeInput): a stepped minute wheel loops like the full list under a finger test(TimeInput, DateTimeInput): a stepped minute wheel loops like the full list under a finger Oct 30, 2026

This branch was successfully deployed

1 active deployment
Preview — 1e4fcad1 Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. needs:design-review Affects visuals — Design should review needs:spec-owner-review Current knowledge records await owner approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants