Repository navigation
fix(DateInput, DateTimeInput): the sheet calendar's toggle and day tap under a finger #8206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 33 commits
2d9fb81
9954d25
77e8a8a
d0da0a1
2a0703f
8669954
2473006
7def994
e40b2f8
4c21450
9a04272
87374bb
6f62fc8
7ea993f
4040d72
9c453d6
f613d13
289ef00
081c564
f003f8a
cd32c30
5400c34
a15489a
a9368ec
5db1b15
11ab876
73e8382
c4fe3f8
dc2e1fc
90051f2
8173818
d07b6eb
947120e
b5a487a
9f26718
8ddf2ae
184de63
e0fc1d6
6300c56
1eba81b
6404e3d
1e91935
a675cf8
f26df1b
af33cf0
a277007
0295280
6113b9d
6c86a5a
75d06f9
0e1ac4d
f34d87f
65a8bb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| --- | ||
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [fix] Under a finger, the calendar in the DateInput and DateTimeInput bottom sheets now pages its months like the iOS date picker: | ||
|
|
||
| - A swipe moves at most one month, chosen at the lift by iOS's paging rule: a quick flick, or a drag past half a month. It settles on iOS's paging curve. | ||
| - The month title follows the drag and the settle. It changes when the next month is more than half in view. | ||
| - An arrow scrolls one month. A second tap mid-scroll goes on to the month after. | ||
| - In the sheet, a drag is decided once it has moved 10 px either way. It pages the calendar if it has moved 10 px sideways by then, and moves the sheet otherwise. | ||
| - A horizontal trackpad swipe moves the calendar with it and pages at the release, like a drag. | ||
| - With Reduce Motion on, the arrows' step and a swipe's settle cross-fade in 200 ms instead of travelling. | ||
|
|
||
| With a mouse, the calendar still scrolls natively. With any pointer, PageUp and PageDown now move the keyboard focus by a month, and by a year with Shift. | ||
|
|
||
| @vjeux | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| --- | ||
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [feat] Under a finger, the calendar in the DateInput and DateTimeInput bottom sheets now toggles to its month and year wheels like the iOS date picker: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Miss: these touch claims still have no Android emulator run at this head, and the description's iOS table (9–23 % of the fade lost before its first frame) contradicts the 54–82 % the newest commit measures here, with card 36's real-device result unstated. |
||
|
|
||
| - The month/year toggle cross-fades over `--duration-medium-min` (225 ms in the neutral theme, the token nearest iOS's 0.25 s there), where DateInput used a quick linear fade and DateTimeInput an instant swap. The wheels arrive on the theme's entrance curve and leave on its exit curve. As in UIKit, the header's month arrows and Reset and the footer's buttons take no tap during the fade, and a second tap on the title turns it around. | ||
| - The hidden month/year wheels are laid out once the sheet settles, so a toggle after that does not lay them out. | ||
| - The title's chevron points along the reading direction and turns a quarter turn to point down while the wheels show, on the theme's move curve. In DateInput it was a down chevron that flipped up; in DateTimeInput it was a down chevron that stayed still. | ||
| - A finger's tapped day takes its selection at once, with no colour transition. A mouse or pen pick keeps the fade, in the touch sheet too. | ||
|
|
||
| With a mouse or a pen, the toggle stays as it is. | ||
|
|
||
| When the title or Done toggles with keyboard focus in the outgoing panel, focus moves into the incoming one, with any pointer. In DateTimeInput, Done lands on the shown month's day. | ||
|
|
||
| With Reduce Motion on, nothing travels: under a finger the toggle cross-fades in 200 ms, and the chevron dips instead of turning, its turn landing mid-dip. With a mouse it changes at once. | ||
|
|
||
| @vjeux | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| // Copyright (c) Meta Platforms, Inc. and affiliates. | ||
|
|
||
| 'use client'; | ||
|
|
||
| /** | ||
| * @file page.tsx | ||
| * @input None | ||
| * @output A bare page with a DateInput sheet over two centuries, a | ||
| * DateTimeInput sheet, and rows that make the document scroll | ||
| * @position A raw page, with no sandbox chrome, for trying the month and year | ||
| * toggle in both sheets on a phone. The page scrolls with the document, as | ||
| * an app's page does. | ||
| */ | ||
|
|
||
| import {useState} from 'react'; | ||
| import type {ISODateString} from '@astryxdesign/core/Calendar'; | ||
| import {DateInput} from '@astryxdesign/core/DateInput'; | ||
| import {DateTimeInput} from '@astryxdesign/core/DateTimeInput'; | ||
| import type {ISODateTimeString} from '@astryxdesign/core/DateTimeInput'; | ||
| import {VStack} from '@astryxdesign/core/Layout'; | ||
| import {Heading, Text} from '@astryxdesign/core/Text'; | ||
|
|
||
| const ROWS = Array.from({length: 40}, (_, index) => `Row ${index + 1}`); | ||
|
|
||
| export default function MonthYearTogglePage() { | ||
| const [date, setDate] = useState<ISODateString | undefined>( | ||
| '2026-03-15' as ISODateString, | ||
| ); | ||
| const [dateTime, setDateTime] = useState<ISODateTimeString | undefined>( | ||
| undefined, | ||
| ); | ||
| return ( | ||
| <VStack gap={4} padding={4}> | ||
| <VStack gap={1}> | ||
| <Heading level={1}>Month and year toggle</Heading> | ||
| <Text color="secondary"> | ||
| Tap a field, then tap the month title in its sheet. | ||
| </Text> | ||
| </VStack> | ||
| <DateInput | ||
| label="Date of birth" | ||
| presentation="bottom-sheet" | ||
| min={'1900-01-01' as ISODateString} | ||
| max={'2099-12-31' as ISODateString} | ||
| value={date} | ||
| onChange={setDate} | ||
| /> | ||
| <DateTimeInput | ||
| label="Meeting time" | ||
| placeholder="Select a date" | ||
| presentation="bottom-sheet" | ||
| value={dateTime} | ||
| onChange={setDateTime} | ||
| /> | ||
| <VStack gap={2}> | ||
| {ROWS.map(row => ( | ||
| <Text key={row} color="secondary"> | ||
| {row} | ||
| </Text> | ||
| ))} | ||
| </VStack> | ||
| </VStack> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| // Copyright (c) Meta Platforms, Inc. and affiliates. | ||
|
|
||
| /** | ||
| * @file MonthPagerProofs.stories.tsx | ||
| * @input Uses React and the internal MonthPager (packages/core/src/DateInput) | ||
| * @output The month pager alone, whatever the primary pointer: 2026 from | ||
| * March, its shown month in a read-only field | ||
| * @position A checked-in reproduction path for the trackpad proofs in | ||
| * packages/core/src/DateInput/__tests__/MonthPager.interaction.browser.spec.ts. | ||
| * WebKit's driver sends a trackpad's precise deltas only from a desktop | ||
| * context, where the sheets show the mouse calendar instead. | ||
| */ | ||
|
|
||
| import type {Meta, StoryObj} from '@storybook/react'; | ||
| import {useState} from 'react'; | ||
| import {MonthPager} from '../../../packages/core/src/DateInput/MonthPager'; | ||
| import { | ||
| createMonthModel, | ||
| fromMonthIndex, | ||
| toMonthIndex, | ||
| } from '../../../packages/core/src/DateInput/monthGeometry'; | ||
|
|
||
| const FIRST = toMonthIndex(2026, 1); | ||
| const LAST = toMonthIndex(2026, 12); | ||
| const MARCH = toMonthIndex(2026, 3); | ||
| const TODAY = {year: 2026, month: 3, day: 15}; | ||
| const GREGORY = createMonthModel('gregory'); | ||
|
|
||
| /** | ||
| * The readout takes the theme's text colour on the theme's surface, not the | ||
| * browser's white field. | ||
| */ | ||
| const READOUT_STYLE = {color: 'inherit', backgroundColor: 'transparent'}; | ||
|
|
||
| const meta = { | ||
| title: 'Core/MonthPager', | ||
| tags: ['no-visual', '!autodocs'], | ||
| parameters: {layout: 'centered'}, | ||
| } satisfies Meta; | ||
|
|
||
| export default meta; | ||
| type Story = StoryObj<typeof meta>; | ||
|
|
||
| /** The pager alone. Its field names the month it shows. */ | ||
| export const Pager: Story = { | ||
| render: () => { | ||
| const [shown, setShown] = useState(MARCH); | ||
| const {year, month} = fromMonthIndex(shown); | ||
| return ( | ||
| <div style={{display: 'grid', gap: 16, inlineSize: 360}}> | ||
| <input | ||
| aria-label="Shown month" | ||
| readOnly | ||
| style={READOUT_STYLE} | ||
| value={`${year}-${String(month).padStart(2, '0')}`} | ||
| /> | ||
| <MonthPager | ||
| monthModel={GREGORY} | ||
| minMonthIndex={FIRST} | ||
| maxMonthIndex={LAST} | ||
| initialMonthIndex={MARCH} | ||
| onVisibleMonthChange={setShown} | ||
| selectedDate={null} | ||
| today={TODAY} | ||
| isDateDisabled={() => false} | ||
| weekStartsOn={0} | ||
| onSelect={() => {}} | ||
| /> | ||
| </div> | ||
| ); | ||
| }, | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.