@astryxdesign/core 0.4.6 → 0.4.7-canary.f5cb8af
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/dist/BottomSheet/BottomSheet.d.ts +1 -0
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +37 -11
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +6 -0
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetEdgeTint.js +62 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +1 -0
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts +1 -0
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +10 -4
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
- package/dist/BottomSheet/useSheetGestures.js +23 -5
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +5 -1
- package/dist/astryx.css +4 -0
- package/dist/hooks/useListFocus.d.ts +5 -2
- package/dist/hooks/useListFocus.d.ts.map +1 -1
- package/dist/hooks/useListFocus.js +12 -6
- package/package.json +3 -3
- package/src/BottomSheet/BottomSheet.tsx +19 -0
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +225 -0
- package/src/BottomSheet/BottomSheetEdgeTint.tsx +82 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +66 -0
- package/src/BottomSheet/BottomSheetPanel.tsx +19 -0
- package/src/BottomSheet/BottomSheetSwitcher.tsx +13 -0
- package/src/BottomSheet/useSheetGestures.test.ts +27 -0
- package/src/BottomSheet/useSheetGestures.ts +25 -5
- package/src/Popover/Popover.test.tsx +27 -1
- package/src/TabList/Tab.tsx +5 -1
- package/src/TabList/TabList.test.tsx +21 -4
- package/src/hooks/useListFocus.doc.mjs +2 -2
- package/src/hooks/useListFocus.test.tsx +65 -3
- package/src/hooks/useListFocus.ts +15 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @xds/core
|
|
2
2
|
|
|
3
|
+
# 0.4.7
|
|
4
|
+
|
|
5
|
+
#### Fixes
|
|
6
|
+
|
|
7
|
+
- BottomSheet: a non-modal sheet now colours the iOS 26 Safari toolbar strip with its own surface instead of letting the page show through behind the address bar (#5342).
|
|
8
|
+
- BottomSheet: a tap inside the sheet is a tap, and the sheet leaves on an exit curve (#5326).
|
|
9
|
+
Two defects, both of which read as "the sheet closes with no animation" — reported against the touch DateInput picker, which is a Bottom Sheet.
|
|
10
|
+
|
|
11
|
+
**A tap inside the sheet started a one-pixel drag.** The sheet body carries the pull-to-dismiss handlers, and they promoted to a sheet drag on _any_ downward movement. A finger is never still, so the pixel or two a tap drifts began a drag — and a live drag suppresses the panel's transition, correctly, because a dragged sheet must track the finger rather than lag it. The close that the tap triggered landed inside that window, so the sheet jumped to its closed position with no transition. Tapping the picker's Save button hit this every time; tapping the scrim never did, because the scrim is the dialog itself and arms no gesture. Promotion now needs 8px of travel — the conventional tap slop, well under what a deliberate pull covers in its first frames — in both the pointer and touch paths. The gesture's transition suppression is also scoped to a sheet that is open, so it cannot straddle an exit.
|
|
12
|
+
|
|
13
|
+
**The exit ran on the entrance's curve.** `--ease-standard` is `cubic-bezier(0.24, 1, 0.4, 1)`, a decelerate curve: it spends its speed immediately and coasts. Right for an entrance, wrong for an exit. Measured on device (iPhone, real Safari), a scrim tap put the sheet half off-screen in 59ms of the 410ms transition and 90% off in 163ms, with the dim gone before it — so the close was over before the eye could follow it. The closing state now carries an accelerating curve of its own, `cubic-bezier(0.3, 0, 0.6, 0.6)`: away from rest, gathering speed, quickest as it leaves the screen, and moving within ~50ms so it reads as one departure rather than a hesitation and a snap. Only the curve changes — the exit keeps `--duration-medium`, the entrance's band, which is what keeps it legible under a theme that scales the motion scale down (neutral's medium is 300ms against the base 410ms).
|
|
14
|
+
|
|
15
|
+
The scrim leaves with the sheet: while closing, the dim runs `linear` rather than the decelerate token. A fade covers no distance, so front-loading its progress just ends it early — the reasoning the touch date picker's surface swap already carries. `BottomSheetSwitcher` gets the same treatment when its flow closes; a handoff between two sheets is not a close and is unchanged.
|
|
16
|
+
- `useListFocus` no longer swallows Escape when no `onEscape` was supplied. The hook called `preventDefault()` on every Escape — a habit inherited from the arrow keys, which share the handler and need it to suppress page scroll — so a list with nothing to dismiss still marked the key handled, and a surrounding layer that defers to `defaultPrevented` (a focus trap, a native popover) never got its turn. Escape is now consumed only when an `onEscape` is passed. Arrow, Home and End handling is unchanged (#5346).
|
|
17
|
+
Behaviour change: `AvatarGroup`, `ButtonGroup`, `Outline`, `Pagination`, `SegmentedControl`, `TabList` and `Toolbar` pass no `onEscape`, so an Escape pressed inside one of them now reaches the surrounding layer and can dismiss it — the point of the fix, but a host that counted on the key stopping there will notice. `NavHeadingMenu` does the same when it renders without a menu close handler. Menus and flyouts that do pass `onEscape` are unaffected. `patch`, not `[breaking]`: the swallowing was never a contract — the hook documented Escape only as "custom callback", and no component advertised consuming the key.
|
|
18
|
+
- TabList: the selected tab now carries `aria-current="true"` — ARIA's generic "current item within a set" — instead of `aria-current="page"`. The strip is a `<nav>` and stays one, but it is used to switch views in place at least as often as it is used to navigate, and on those uses `page` asserted a page change that never happened. Assistive tech announced the selected tab as the _current page_ even when nothing had navigated; it now announces it as the _current item_, which is true either way. A tab given an `href` still renders an anchor and still reads as a link — its current marker is just less specific than it was. No role changes and no new props. (#5347)
|
|
19
|
+
|
|
20
|
+
#### Contributors
|
|
21
|
+
|
|
22
|
+
Thanks to everyone who contributed to this release:
|
|
23
|
+
|
|
24
|
+
- @cixzhang
|
|
25
|
+
- @imdreamrunner
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
3
29
|
# 0.4.6
|
|
4
30
|
|
|
5
31
|
#### New Features
|
|
@@ -22,6 +48,7 @@
|
|
|
22
48
|
Nothing else is published, on purpose. There is no export that forces a surface: the touch picker is reachable by being on a touch device, which is the only place it is worth looking at. The media query the switch runs on is an internal constant, not an export — six other core components write `@media (pointer: coarse)` inline rather than sharing one, and nothing has asked to ask the same question. The picker's two sizes (the 44px day cell, the 28px wheel row) are compile-time constants rather than theme variables — the day size is an accessibility floor, and a variable a theme can quietly lower is not a floor. And the sheet's header button is addressed by a `data-` attribute rather than a theme target, because nothing has asked to restyle it. Each of those is additive later and awkward to withdraw once shipped.
|
|
23
49
|
|
|
24
50
|
The wheels also answer a mouse now. A wheel is a scroll container, so a finger pans it for free; a mouse got nothing, because browsers do not drag-scroll an overflow container — pressing and pulling on the one control shaped like a thing you spin did nothing at all. Dragging with a mouse works, and fixes a related bug on the way: BottomSheet begins its own drag from a `pointerdown` on its body and captures the pointer for it, so a click on a wheel row that wobbled more than a pixel or two used to select nothing.
|
|
51
|
+
|
|
25
52
|
- Export `useLocale` and `useCollator` for provider-backed formatting and comparison (#5194)
|
|
26
53
|
- RadioListItem: the `radio-list-item` row theme target now carries `size`, `selected`, and `disabled` state variants (matching `multi-selector-option`), so themes can style selected or disabled rows (#5143).
|
|
27
54
|
- RadioListItem: the `radio-list-item` theme target now rides the painting row element (converging with `list-item`), so a theme can style the row's hover background, padding, and border radius — previously it sat on a layout-only wrapper that painted nothing. The default (unthemed) row appearance is unchanged: it stays a bare surface with no row padding, radius, or hover/selected background (only the radio indicator tints on hover) (#5143).
|
|
@@ -42,6 +69,7 @@
|
|
|
42
69
|
On `Section` the matching `--container-padding-*` custom property moves with the prop, so bleed children (`Table`, `Divider`, a nested `Section`) keep compensating against the padding actually applied.
|
|
43
70
|
|
|
44
71
|
Existing code is unaffected: `padding`, `paddingInline` and `paddingBlock` behave exactly as before, and their generated class output is unchanged.
|
|
72
|
+
|
|
45
73
|
- Selector: add the `selector-option-row` theme target on the dropdown option row, carrying `size` plus `selected`/`disabled` state — so a theme can restyle row padding and density directly (mirroring `multi-selector-option`), instead of reaching the bare `role="option"` element with a structural selector (#5179).
|
|
46
74
|
- MediaTheme: add `mode="auto"` and `mode="off"` (#5299)
|
|
47
75
|
A theme is free to define `--color-background-inverted` as something that is not inverted — and a component that hardcodes `mode="dark"` then paints white text on pale grey at 1.25:1. The surface color is a runtime value and the mode was a compile-time guess, so no amount of care in the component could catch it.
|
|
@@ -51,6 +79,7 @@
|
|
|
51
79
|
When the backdrop is not knowable from CSS — during SSR, on the first client frame, and most often behind a `background-image`, whose pixels need sampling (see `useImageMode`) rather than a computed style — `auto` uses the new `fallback` prop instead of guessing.
|
|
52
80
|
|
|
53
81
|
Toast now uses `mode="auto"`, with its previous rule kept only as that fallback. Every stock Astryx surface renders exactly as before.
|
|
82
|
+
|
|
54
83
|
- Tooltip and HoverCard: tap to open where there is no hover (#5248)
|
|
55
84
|
Hover is the one trigger a touch screen cannot express, and both components were answering that badly. `Tooltip` suppressed itself on any device reporting `(hover: none)`, so its content — often the only label an icon button has — was simply unreachable on a phone. `HoverCard` did nothing at all: the `mouseenter` a tap synthesizes opened the card on every tap of its trigger, over the control the user was aiming at, with no gesture that closed it again.
|
|
56
85
|
|
|
@@ -71,11 +100,13 @@
|
|
|
71
100
|
The indicator also kept its negative overlap margin when it was the first child of a group, hanging 12px outside the group's own box. It now carries the same `:not(:first-child)` guard `Avatar` already had. And a negative `count` rendered the string `+-3` and announced "-3 more"; since the documented shape for the prop is `total - visibleCount`, which goes negative whenever the list is shorter than the slice, it now clamps at zero.
|
|
72
101
|
|
|
73
102
|
Docs: the guidance told readers to "set max to limit visible avatars", and there is no `max` prop. The API is compositional on purpose, so the consumer slices; the guidance now says that. The keyboard behaviour names the APG roving tabindex technique it implements, and `size` now says that the group's value wins over each child avatar's own `size`, including when the group leaves it at the default.
|
|
103
|
+
|
|
74
104
|
- Blockquote: the `cite` attribution renders as a bare `<cite>` instead of being wrapped in a `<footer>`, which was becoming a `contentinfo` document landmark. Also guards the slot with `isRenderable`, so `cite={condition && author}` no longer emits an empty `<cite>`, and wraps long unbroken words instead of overflowing (#5144).
|
|
75
105
|
- Bottom Sheet: float the grab handle so content sits closer to the top (#5222)
|
|
76
106
|
The drag area above the sheet's content was a 48px row in the sheet's flex column, pushing everything below it down by its full height and reading as an empty band above the first line of content.
|
|
77
107
|
|
|
78
108
|
The bar is now 24px and floats over the content: the scrolling area starts at the sheet's top edge and rides up under the pill, so a heading sits 24px closer to the top. The pill is 4px tall centered in the band, so it occupies only 10-14px from the edge — inside the top padding a content wrapper already provides — and a surface gradient behind it keeps it legible over whatever sits or scrolls beneath.
|
|
109
|
+
|
|
79
110
|
- BottomSheet: give the sheet one uniform edge against the scrim. Two things were wrong in dark mode. The sheet drew no edge of its own — surface and scrim sit a few RGB steps apart and the `--shadow-high` drop shadow is black on near-black, so the left and right edges were invisible (measured 1.16:1 boundary contrast in dark against 2.89:1 in light); it now carries a `--border-width` / `--color-border` hairline on its three scrim-facing edges, the same treatment MobileNav gives its scrim-facing edge. And under a theme that packs an inset ring into `--shadow-high` (every bundled theme adds one in dark mode) that ring was painted over by an opaque content wrapper such as `Section`, so it showed only in the gap below where the content ended and the side edges appeared to change width partway down; the scrolling body now paints the surface across the sheet's whole inner box, hiding the ring evenly (#5305).
|
|
80
111
|
- Breadcrumbs: button crumbs keep the link's vertical padding, the variant reaches the item theme targets, and interactive crumbs paint the shared focus ring (#5332)
|
|
81
112
|
- DateInput's touch calendar no longer rests between two months (#5319)
|
|
@@ -86,6 +117,7 @@
|
|
|
86
117
|
The rest position is now corrected rather than trusted: once the gesture is genuinely over — touch released, the scroller quiet, AND its offset confirmed unchanged across a frame — a scroller that is off a pane boundary is moved to the nearest one. A scroller the browser snapped for itself is left alone, so nothing extra happens on Chrome, and sub-pixel drift on a fractional viewport is ignored.
|
|
87
118
|
|
|
88
119
|
That last condition is what keeps the fix from becoming a worse bug than the one it fixes. A quiet period is not proof of rest: iOS runs its own snap animation for a few hundred milliseconds after the finger lifts and fires scroll events irregularly while it does, so a correction that trusts quiet alone can land mid-animation, round an offset still travelling toward next month back to the month it came from, and reverse the swipe.
|
|
120
|
+
|
|
89
121
|
- A disabled element answers the pointer with `default`, never an interactive cursor. Every `cursor` in core and lab carries `':is(:disabled,[aria-disabled="true"])': 'default'`, and the reset gives the same cursor to any disabled element that declares none — `[aria-disabled]` included, which previously got nothing. A lint rule and a Chromium sweep over every story keep it that way. Disabled elements sealed behind `pointer-events: none` are unchanged: the pointer never reaches them, so their cursor comes from an ancestor — which is why the guarantee is `default` rather than a distinct disabled cursor the library could only paint on some of them (#5323).
|
|
90
122
|
- Disabled elements no longer paint a hover state: every self-`:hover` in core and lab, and every `:hover` a theme authors, now carries the zero-specificity guard `:hover:where(:not(:disabled,[aria-disabled="true"]))`, so existing overrides weigh exactly what they weighed before. A lint rule and a Chromium sweep over every story keep it that way (#5247).
|
|
91
123
|
- InputClearButton: the clear (✕) affordance now meets the WCAG 2.5.8 AA 24×24 minimum on touch. The shared button rendered a 20px glyph with a 20px tap target, so every input that clears through it — Typeahead, Tokenizer, FileInput and the rest of the family — was under the floor on a phone. An `::after` overlay now expands the tappable region to 24×24, gated behind `@media (pointer: coarse)`: on a fine pointer the overlay is not generated at all, because a mouse is precise enough, an unconditional overlay could overlap neighboring controls in dense desktop layouts, and an overlay covering the button would take hover away from the `astryx-input-clear-icon` theme target. The visual glyph is unchanged at every breakpoint, and the overlay stops at 24px so it stays clear of the 8px adornment gap and the input's own caret area (#4956).
|
|
@@ -97,6 +129,7 @@
|
|
|
97
129
|
The reset now lives in one place (`overlayPaddingReset`, exported from `@astryxdesign/core/Layout`) instead of being hand-copied per overlay, and moved onto the `useLayer` root, which covers every layer surface at once. Values descendants subtract are zeroed; values they add are cleared to `initial` so readers fall through to their own default rather than losing their padding.
|
|
98
130
|
|
|
99
131
|
Section's padding propagation moved from the public `--astryx-section-padding` token to a private `--_section-padding-propagated`. The two carried different authority under one name — a theme's value versus one ancestor's — and an overlay could not drop the inherited one without blanking the theme's. Propagated values still win over the theme for nested sections, so behavior is unchanged. Themes are unaffected: `--astryx-section-padding` remains the public token and still reaches inside overlays.
|
|
132
|
+
|
|
100
133
|
- Theming: a physical `paddingTop`/`paddingBottom` now reaches the container padding expansion, so `card`, `dialog`, `section` and `number-input` track it the way they already track the logical spellings. A physical block longhand matched none of the padding property names the expansion recognizes, so it landed raw on the element while the component's internals kept reading the default — the NumberInput stepper column came up ~10px short of the field edges, and container bleed compensated by the wrong amount. Mixing spellings was worse than either alone: `padding: '10px'` plus `paddingTop: '14px'` published 10px in the tokens while the element painted 14px on top. `padding-top` and `padding-bottom` ARE the block edges in every horizontal writing mode, so this normalization assumes no direction. `paddingLeft`/`paddingRight` are deliberately unchanged: they are direction-relative — left is inline-start in LTR and inline-end in RTL — and the tokens are consumed by logical properties, so routing them would silently move the padding to the opposite edge under RTL. They keep their physical meaning, exactly as before (#5244).
|
|
101
134
|
- `DateInput`, `DateTimeInput`, `DateRangeInput`, and `Calendar` now format and parse dates using the ambient `InternationalizationProvider` locale instead of the host/browser locale. `plainDateFormat` (backing `formatSharedDate`) previously called `Intl.DateTimeFormat(undefined, ...)`, and `dateParser`'s day/month disambiguation heuristic for ambiguous numeric input (e.g. `3/4/2026`) called `Intl.DateTimeFormat()` with no locale at all, so a tree wrapped in a non-English `locale` still formatted and parsed dates using the host locale (#5120).
|
|
102
135
|
- Use the InternationalizationProvider locale for sorting, formatting, and speech defaults (#5195)
|
|
@@ -107,11 +140,13 @@
|
|
|
107
140
|
Locking background scroll hides the document's scrollbar. Where that scrollbar takes layout space — Windows/Linux desktop, and macOS set to always show scroll bars — hiding it widened the layout viewport by its width (~15px), so the whole page reflowed sideways behind the overlay and back again on close.
|
|
108
141
|
|
|
109
142
|
Both scroll locks now hold that gutter open with `scrollbar-gutter: stable` for the duration of the lock, which keeps `position: fixed` chrome (sticky headers, toast viewports) still as well as in-flow content. Pages with no space-taking scrollbar, and pages that already set `scrollbar-gutter` themselves, are left alone. Engines without `scrollbar-gutter` support fall back to padding the measured difference.
|
|
143
|
+
|
|
110
144
|
- SegmentedControl: keep item labels on a single line, truncating overflow with an ellipsis instead of wrapping to multiple lines (#5035)
|
|
111
145
|
- Selector: `SelectorOptionData` gains `description`, and the closed trigger now shows the selected option instead of just its label. The two-line option row `SelectorOption` draws was unreachable from the `options` prop — the data type carried only `value/label/disabled/icon` — so consumers kept a side map of descriptions keyed by value and re-rendered the row through `renderOption`. `description` now sits on the option data and `DefaultOption` forwards it. On the trigger, the selected option's own `icon` renders in the closed state (`startIcon` still wins when set, so a pinned field icon never doubles up), which retires the app-side `startIcon={value === 'x' ? … : …}` mirroring of state the component already knows. `renderValue` is the seam for drawing the selection yourself — the description included (#5202).
|
|
112
146
|
[feat] Item: new `layout` prop — `'stacked'` (default, unchanged) or `'inline'`, which keeps the description on the label's line with the description ellipsizing first. The inline row centers its two lines rather than sharing a baseline: two font sizes on one baseline make a line box taller than either line, which would push a fixed-height host off its size token. Every row built on `Item` gets the axis, `SelectorOption` included.
|
|
113
147
|
|
|
114
148
|
[fix] Selector: the trigger is sized by padding rather than a fixed height, so it is the `--size-element-*` token for a one-line value (28/32/36) and exactly one text line taller for a two-line one (48/52/56). The token and a text line are both multiples of 4, so every trigger lands on the 4px rhythm and lines up with the Buttons and inputs beside it — and no prop chooses the height, the content does. It previously swapped the fixed height for a minimum whenever `renderValue` was passed, keyed on the prop being present rather than on the content needing the room: a one-line value measured 39px and a two-line one 58px at _every_ size, so the `size` prop stopped affecting the trigger at all. Inside an `InputGroup`, where the group pins the row, the relaxed height did nothing and the content bled 4px through its own border. The group owns the row now and the trigger clamps its own value box to it, so nothing a caller draws can paint over the rows above and below: a `SelectorOption` folds onto one line and ellipsizes, and any other node is cut off at the row's edge. The trigger also stops asserting a height floor of its own there, so a control sized above its group — `<InputGroup size="md">` around a `size="lg"` control — sits in the group's row instead of growing it. The trigger's line box is pinned to that same token rather than a ratio, so the coarse-pointer font bump — and any theme that changes `--font-size-base` — grows the glyphs without moving the control off its size token.
|
|
149
|
+
|
|
115
150
|
- `Slider` with `orientation="vertical"` now gets the same 24px touch hit area the horizontal one got: its track was still only 20px wide on coarse pointers, under the WCAG 2.5.8 AA minimum. The whole track is the tap target for both orientations — the fix that floored the horizontal track's block size did nothing for vertical, whose short axis is the inline one — so the inline size is now floored to 24px on touch, with the same `@media (pointer: coarse)` gate. The rail, fill, marks and thumb all center on the inline 50%, so nothing visible moves and desktop is untouched (#5173).
|
|
116
151
|
- `@astryxdesign/core/theme/syntax` is importable from a server component again: the presets are data, not client references (#5076).
|
|
117
152
|
The subpath's barrel carried `'use client'`, and it is the only entry point for the syntax module. React therefore replaced _every_ export with a client reference for a server importer — including `dracula`, `oneLight`, `allSyntaxPresets`, `syntaxTokenDefaults` and `defineSyntaxTheme`, none of which need a boundary. Reading a preset in a Next.js server module (deriving a code-block ground, emitting theme CSS at build time) got a proxy instead of data, so `preset.tokens` was `undefined` and the failure surfaced far from its cause — the same import under plain Node worked perfectly.
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* SYNC: When modified, update these files to stay in sync:
|
|
14
14
|
* - /packages/core/src/BottomSheet/BottomSheetPanel.tsx
|
|
15
|
+
* - /packages/core/src/BottomSheet/BottomSheetEdgeTint.tsx
|
|
15
16
|
* - /packages/core/src/BottomSheet/BottomSheet.doc.mjs
|
|
16
17
|
* - /packages/core/src/BottomSheet/BottomSheet.test.tsx
|
|
17
18
|
* - /packages/core/src/BottomSheet/BottomSheetSwitcher.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheet.tsx"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheet.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AAe7C,YAAY,EAAC,iBAAiB,EAAE,oBAAoB,EAAC,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAC,iBAAiB,EAAE,oBAAoB,EAAC,MAAM,oBAAoB,CAAC;AAyEhF,UAAU,sBAAuB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAChE,qDAAqD;IACrD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IAEd,0EAA0E;IAC1E,QAAQ,EAAE,SAAS,CAAC;IAEpB,wGAAwG;IACxG,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAAC;IAE7C;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAEjD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,UAAU,0BAA2B,SAAQ,sBAAsB;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,UAAU,wBAAyB,SAAQ,sBAAsB;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAC1B,0BAA0B,GAAG,wBAAwB,CAAC;AA+WxD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BA6BlD;yBA7Be,WAAW"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* SYNC: When modified, update these files to stay in sync:
|
|
18
18
|
* - /packages/core/src/BottomSheet/BottomSheetPanel.tsx
|
|
19
|
+
* - /packages/core/src/BottomSheet/BottomSheetEdgeTint.tsx
|
|
19
20
|
* - /packages/core/src/BottomSheet/BottomSheet.doc.mjs
|
|
20
21
|
* - /packages/core/src/BottomSheet/BottomSheet.test.tsx
|
|
21
22
|
* - /packages/core/src/BottomSheet/BottomSheetSwitcher.tsx
|
|
@@ -29,8 +30,9 @@ import "../theme/tokens.stylex.js";
|
|
|
29
30
|
import { colorVars, durationVars, easeVars } from "../theme/tokens.stylex.js";
|
|
30
31
|
import { useDevWarning, useScrollLock } from "../hooks/index.js";
|
|
31
32
|
import { BottomSheetPanel } from "./BottomSheetPanel.js";
|
|
33
|
+
import { BottomSheetEdgeTint } from "./BottomSheetEdgeTint.js";
|
|
32
34
|
import { BottomSheetSwitcherContext } from "./BottomSheetSwitcherContext.js";
|
|
33
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
35
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
34
36
|
function panelStateForSwitcherPhase(phase, alignmentOffset) {
|
|
35
37
|
switch (phase) {
|
|
36
38
|
case 'active':
|
|
@@ -180,33 +182,57 @@ function StandaloneBottomSheet({
|
|
|
180
182
|
dismissOnLightInteraction();
|
|
181
183
|
}
|
|
182
184
|
}, [dismissOnLightInteraction, hasScrim]);
|
|
183
|
-
return /*#__PURE__*/
|
|
185
|
+
return /*#__PURE__*/_jsxs("dialog", {
|
|
184
186
|
...{
|
|
185
187
|
0: {
|
|
186
188
|
className: "xixxii4 x10a8y8t x1o6l61p xtdtrs8 x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1s85apg x1a2a7pz"
|
|
187
189
|
},
|
|
188
|
-
|
|
190
|
+
8: {
|
|
189
191
|
className: "xixxii4 x10a8y8t x1o6l61p xtdtrs8 x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1a2a7pz x1lliihq"
|
|
190
192
|
},
|
|
191
|
-
|
|
193
|
+
4: {
|
|
192
194
|
className: "xixxii4 x10a8y8t x1o6l61p xtdtrs8 x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1s85apg x1a2a7pz xnixb3f xni466t xxiuuzi xc0dz0a xft5bk6 x15h3t91 x142juwg x1viac0w"
|
|
193
195
|
},
|
|
194
|
-
|
|
196
|
+
12: {
|
|
195
197
|
className: "xixxii4 x10a8y8t x1o6l61p xtdtrs8 x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1a2a7pz x1lliihq xnixb3f xni466t xxiuuzi xc0dz0a xft5bk6 x15h3t91 x142juwg x1viac0w"
|
|
196
198
|
},
|
|
199
|
+
2: {
|
|
200
|
+
className: "xixxii4 x10a8y8t x1o6l61p xtdtrs8 x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1s85apg x1a2a7pz x1cz9k3x"
|
|
201
|
+
},
|
|
202
|
+
10: {
|
|
203
|
+
className: "xixxii4 x10a8y8t x1o6l61p xtdtrs8 x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1a2a7pz x1lliihq x1cz9k3x"
|
|
204
|
+
},
|
|
205
|
+
6: {
|
|
206
|
+
className: "xixxii4 x10a8y8t x1o6l61p xtdtrs8 x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1s85apg x1a2a7pz xnixb3f xni466t xxiuuzi xc0dz0a xft5bk6 x142juwg x1viac0w x1cz9k3x"
|
|
207
|
+
},
|
|
208
|
+
14: {
|
|
209
|
+
className: "xixxii4 x10a8y8t x1o6l61p xtdtrs8 x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1a2a7pz x1lliihq xnixb3f xni466t xxiuuzi xc0dz0a xft5bk6 x142juwg x1viac0w x1cz9k3x"
|
|
210
|
+
},
|
|
197
211
|
1: {
|
|
198
212
|
className: "xixxii4 x10a8y8t x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1s85apg x1a2a7pz x47corl xfo81ep xh8yej3 x5yr21d"
|
|
199
213
|
},
|
|
200
|
-
|
|
214
|
+
9: {
|
|
201
215
|
className: "xixxii4 x10a8y8t x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1a2a7pz x1lliihq x47corl xfo81ep xh8yej3 x5yr21d"
|
|
202
216
|
},
|
|
203
|
-
|
|
217
|
+
5: {
|
|
204
218
|
className: "xixxii4 x10a8y8t x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1s85apg x1a2a7pz xnixb3f xni466t xxiuuzi xc0dz0a xft5bk6 x15h3t91 x142juwg x1viac0w x47corl xfo81ep xh8yej3 x5yr21d"
|
|
205
219
|
},
|
|
206
|
-
|
|
220
|
+
13: {
|
|
207
221
|
className: "xixxii4 x10a8y8t x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1a2a7pz x1lliihq xnixb3f xni466t xxiuuzi xc0dz0a xft5bk6 x15h3t91 x142juwg x1viac0w x47corl xfo81ep xh8yej3 x5yr21d"
|
|
222
|
+
},
|
|
223
|
+
3: {
|
|
224
|
+
className: "xixxii4 x10a8y8t x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1s85apg x1a2a7pz x1cz9k3x x47corl xfo81ep xh8yej3 x5yr21d"
|
|
225
|
+
},
|
|
226
|
+
11: {
|
|
227
|
+
className: "xixxii4 x10a8y8t x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1a2a7pz x1lliihq x1cz9k3x x47corl xfo81ep xh8yej3 x5yr21d"
|
|
228
|
+
},
|
|
229
|
+
7: {
|
|
230
|
+
className: "xixxii4 x10a8y8t x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1s85apg x1a2a7pz xnixb3f xni466t xxiuuzi xc0dz0a xft5bk6 x142juwg x1viac0w x1cz9k3x x47corl xfo81ep xh8yej3 x5yr21d"
|
|
231
|
+
},
|
|
232
|
+
15: {
|
|
233
|
+
className: "xixxii4 x10a8y8t x1x1rfll x7ab17h x1ghz6dp x1717udv xjbqb8w x1rea2x4 x1a2a7pz x1lliihq xnixb3f xni466t xxiuuzi xc0dz0a xft5bk6 x142juwg x1viac0w x1cz9k3x x47corl xfo81ep xh8yej3 x5yr21d"
|
|
208
234
|
}
|
|
209
|
-
}[!!shouldPresent << 2 | !!hasScrim << 1 | !!!hasScrim << 0],
|
|
235
|
+
}[!!shouldPresent << 3 | !!hasScrim << 2 | !!(hasScrim && !isOpen && isPresented) << 1 | !!!hasScrim << 0],
|
|
210
236
|
ref: dialogRef,
|
|
211
237
|
"aria-label": label,
|
|
212
238
|
"aria-hidden": !isOpen && isPresented ? 'true' : undefined,
|
|
@@ -216,7 +242,7 @@ function StandaloneBottomSheet({
|
|
|
216
242
|
onCancel: handleCancel,
|
|
217
243
|
onClick: handleClick,
|
|
218
244
|
onKeyDown: handleKeyDown,
|
|
219
|
-
children: /*#__PURE__*/_jsx("div", {
|
|
245
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
220
246
|
...{
|
|
221
247
|
className: "x10l6tqk x17y0mx6 x1ey2m1c x78zum5 xl56j7k x47corl"
|
|
222
248
|
},
|
|
@@ -235,7 +261,7 @@ function StandaloneBottomSheet({
|
|
|
235
261
|
onMotionComplete: handleMotionComplete,
|
|
236
262
|
children: children
|
|
237
263
|
})
|
|
238
|
-
})
|
|
264
|
+
}), hasScrim ? null : /*#__PURE__*/_jsx(BottomSheetEdgeTint, {})]
|
|
239
265
|
});
|
|
240
266
|
}
|
|
241
267
|
function SwitcherBottomSheetItem({
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colours the iOS Safari toolbar strip below a non-modal sheet. Renders
|
|
3
|
+
* nothing visible; see the file header for why it exists.
|
|
4
|
+
*/
|
|
5
|
+
export declare function BottomSheetEdgeTint(): import("react").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=BottomSheetEdgeTint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BottomSheetEdgeTint.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheetEdgeTint.tsx"],"names":[],"mappings":"AAqEA;;;GAGG;AACH,wBAAgB,mBAAmB,gCAQlC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file BottomSheetEdgeTint.tsx
|
|
7
|
+
* @input Uses StyleX and core color tokens
|
|
8
|
+
* @output Exports BottomSheetEdgeTint, an internal decorative element
|
|
9
|
+
* @position Private helper rendered inside a non-modal BottomSheet dialog
|
|
10
|
+
*
|
|
11
|
+
* iOS 26 Safari dropped `<meta name="theme-color">` and instead derives the
|
|
12
|
+
* colour it paints behind its translucent toolbars by sampling the page: it
|
|
13
|
+
* hit tests a point just inside each viewport edge, walks up to the nearest
|
|
14
|
+
* `fixed`/`sticky` ancestor, and extends that element's declared
|
|
15
|
+
* `background-color` into the browser chrome.
|
|
16
|
+
*
|
|
17
|
+
* A modal sheet is served by that heuristic already — WebKit has a dedicated
|
|
18
|
+
* branch for a dialog's `::backdrop`. A non-modal sheet is not: the nearest
|
|
19
|
+
* fixed ancestor of the panel is the sheet's own full-viewport `<dialog>`,
|
|
20
|
+
* which is transparent and viewport-sized, and WebKit answers a viewport-sized
|
|
21
|
+
* candidate by *keeping the colour it already had* — the host page's. The page
|
|
22
|
+
* then shows through behind the address bar while the sheet covers the screen
|
|
23
|
+
* above it.
|
|
24
|
+
*
|
|
25
|
+
* This element gives the heuristic something unambiguous to sample: fixed,
|
|
26
|
+
* full width, flush with the bottom edge, taller than WebKit's 10px minimum
|
|
27
|
+
* for reading a declared colour, and painted in the sheet's own surface
|
|
28
|
+
* colour. It is masked out so it never renders for the user; Safari reads the
|
|
29
|
+
* computed style, not the painted pixels.
|
|
30
|
+
*
|
|
31
|
+
* Everywhere else it is inert decoration.
|
|
32
|
+
*
|
|
33
|
+
* SYNC: When modified, update these files to stay in sync:
|
|
34
|
+
* - /packages/core/src/BottomSheet/BottomSheet.tsx
|
|
35
|
+
* - /packages/core/src/BottomSheet/BottomSheetSwitcher.tsx
|
|
36
|
+
* - /packages/core/src/BottomSheet/BottomSheetEdgeTint.test.tsx
|
|
37
|
+
*/
|
|
38
|
+
import * as stylex from '@stylexjs/stylex';
|
|
39
|
+
import "../theme/tokens.stylex.js";
|
|
40
|
+
import { colorVars } from "../theme/tokens.stylex.js";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* WebKit ignores the declared `background-color` of a sampled box thinner than
|
|
44
|
+
* 10px and falls back to sampling painted pixels, which a masked element has
|
|
45
|
+
* none of. 12px clears that floor with room to spare and is still far below
|
|
46
|
+
* the ~40px of chrome it colours.
|
|
47
|
+
*/
|
|
48
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
49
|
+
const SAMPLE_HEIGHT_PX = 12;
|
|
50
|
+
/**
|
|
51
|
+
* Colours the iOS Safari toolbar strip below a non-modal sheet. Renders
|
|
52
|
+
* nothing visible; see the file header for why it exists.
|
|
53
|
+
*/
|
|
54
|
+
export function BottomSheetEdgeTint() {
|
|
55
|
+
return /*#__PURE__*/_jsx("div", {
|
|
56
|
+
...{
|
|
57
|
+
className: "xixxii4 x17y0mx6 x1ey2m1c x1kpxq89 x10xzikg xhtitgo x47corl x1cb67k9 xt2iwb0"
|
|
58
|
+
},
|
|
59
|
+
"data-sheet-edge-tint": "",
|
|
60
|
+
"aria-hidden": "true"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomSheetPanel.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheetPanel.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAc5C,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAIvB,QAAA,MAAM,cAAc;;;;CAIV,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,cAAc,CAAC;AAC5D,YAAY,EAAC,oBAAoB,EAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"BottomSheetPanel.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheetPanel.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAc5C,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAIvB,QAAA,MAAM,cAAc;;;;CAIV,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,cAAc,CAAC;AAC5D,YAAY,EAAC,oBAAoB,EAAC,CAAC;AA2LnC,MAAM,MAAM,sBAAsB,GAChC,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEjD,MAAM,MAAM,qBAAqB,GAC7B;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAC,GAChB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAC,GACjC;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC;CACzB,GACD;IAAC,IAAI,EAAE,SAAS,CAAA;CAAC,CAAC;AAEtB,UAAU,qBAAsB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC/D,+CAA+C;IAC/C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,KAAK,EAAE,qBAAqB,CAAC;IAC7B,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAAC;IAC5C,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IACjD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3D,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACzD,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC7D;AA+GD,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,KAAK,EACL,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,KAAK,EACL,QAAQ,EACR,MAAM,EACN,qBAA4B,EAC5B,kBAA0B,EAC1B,SAAS,EACT,cAAc,EACd,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,GAAG,KAAK,EACT,EAAE,qBAAqB,+BAyQvB;yBA3Re,gBAAgB"}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* SYNC: When modified, update these files to stay in sync:
|
|
20
20
|
* - /packages/core/src/BottomSheet/BottomSheet.tsx
|
|
21
|
+
* - /packages/core/src/BottomSheet/BottomSheetEdgeTint.tsx
|
|
21
22
|
* - /packages/core/src/BottomSheet/BottomSheetSwitcher.doc.mjs
|
|
22
23
|
* - /packages/core/src/BottomSheet/BottomSheetSwitcher.test.tsx
|
|
23
24
|
* - /packages/core/src/BottomSheet/index.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomSheetSwitcher.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheetSwitcher.tsx"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"BottomSheetSwitcher.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheetSwitcher.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAQL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAoI5C,MAAM,WAAW,wBAAyB,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC5E,iDAAiD;IACjD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAEnC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;IAE9D;;;;;OAKG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B,iEAAiE;IACjE,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAEtD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,0DAA0D;IAC1D,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,mBAAmB,EACnB,QAAe,EACf,QAAQ,EACR,GAAG,EACH,MAAM,EACN,SAAS,EACT,KAAK,EACL,QAAQ,EACR,OAAO,EACP,SAAS,EACT,GAAG,KAAK,EACT,EAAE,wBAAwB,+BA+Z1B;yBA5ae,mBAAmB"}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* SYNC: When modified, update these files to stay in sync:
|
|
24
24
|
* - /packages/core/src/BottomSheet/BottomSheet.tsx
|
|
25
|
+
* - /packages/core/src/BottomSheet/BottomSheetEdgeTint.tsx
|
|
25
26
|
* - /packages/core/src/BottomSheet/BottomSheetSwitcher.doc.mjs
|
|
26
27
|
* - /packages/core/src/BottomSheet/BottomSheetSwitcher.test.tsx
|
|
27
28
|
* - /packages/core/src/BottomSheet/index.ts
|
|
@@ -34,8 +35,9 @@ import "../theme/tokens.stylex.js";
|
|
|
34
35
|
import { colorVars, durationVars, easeVars } from "../theme/tokens.stylex.js";
|
|
35
36
|
import { hasActiveFocusTrapEscape, isImeKeyEvent, useFocusTrap, useScrollLock } from "../hooks/index.js";
|
|
36
37
|
import { composeEventHandlers, mergeProps, mergeRefs } from "../utils/index.js";
|
|
38
|
+
import { BottomSheetEdgeTint } from "./BottomSheetEdgeTint.js";
|
|
37
39
|
import { BottomSheetSwitcherContext } from "./BottomSheetSwitcherContext.js";
|
|
38
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
40
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
39
41
|
const styles = {
|
|
40
42
|
dialog: {
|
|
41
43
|
kVAEAm: "xixxii4",
|
|
@@ -72,6 +74,10 @@ const styles = {
|
|
|
72
74
|
kwHxkk: "x142juwg",
|
|
73
75
|
k4oj2o: "x1viac0w",
|
|
74
76
|
$$css: true
|
|
77
|
+
},
|
|
78
|
+
scrimClosing: {
|
|
79
|
+
kb4ib: "x1cz9k3x",
|
|
80
|
+
$$css: true
|
|
75
81
|
}
|
|
76
82
|
};
|
|
77
83
|
const IDLE_TRANSITION = {
|
|
@@ -386,12 +392,12 @@ export function BottomSheetSwitcher({
|
|
|
386
392
|
dismissOnLightInteraction();
|
|
387
393
|
}
|
|
388
394
|
}, [dismissOnLightInteraction, hasScrim]);
|
|
389
|
-
const dialogStyleProps = stylex.props(styles.dialog, isFlowVisible && styles.dialogOpen, hasScrim && styles.scrim, !hasScrim && styles.dialogNonModal, xstyle);
|
|
395
|
+
const dialogStyleProps = stylex.props(styles.dialog, isFlowVisible && styles.dialogOpen, hasScrim && styles.scrim, hasScrim && isFlowVisible && activeSheet == null && styles.scrimClosing, !hasScrim && styles.dialogNonModal, xstyle);
|
|
390
396
|
const dialogPresentationProps = mergeProps(dialogStyleProps, className, style);
|
|
391
397
|
const ariaLabel = props['aria-label'] ?? (props['aria-labelledby'] == null ? activeLabel : undefined);
|
|
392
398
|
return /*#__PURE__*/_jsx(BottomSheetSwitcherContext, {
|
|
393
399
|
value: contextValue,
|
|
394
|
-
children: /*#__PURE__*/
|
|
400
|
+
children: /*#__PURE__*/_jsxs("dialog", {
|
|
395
401
|
...props,
|
|
396
402
|
...dialogPresentationProps,
|
|
397
403
|
ref: mergeRefs(ref, dialogRef, containerRef),
|
|
@@ -403,7 +409,7 @@ export function BottomSheetSwitcher({
|
|
|
403
409
|
...(activeSheetPurpose === 'required' ? {
|
|
404
410
|
role: 'alertdialog'
|
|
405
411
|
} : undefined),
|
|
406
|
-
children: children
|
|
412
|
+
children: [children, hasScrim ? null : /*#__PURE__*/_jsx(BottomSheetEdgeTint, {})]
|
|
407
413
|
})
|
|
408
414
|
});
|
|
409
415
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSheetGestures.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/useSheetGestures.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,UAAU,IAAI,eAAe,EAClC,KAAK,YAAY,IAAI,iBAAiB,EACtC,KAAK,SAAS,EACd,KAAK,OAAO,IAAI,YAAY,EAC7B,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useSheetGestures.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/useSheetGestures.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,UAAU,IAAI,eAAe,EAClC,KAAK,YAAY,IAAI,iBAAiB,EACtC,KAAK,SAAS,EACd,KAAK,OAAO,IAAI,YAAY,EAC7B,MAAM,OAAO,CAAC;AA8If,MAAM,WAAW,uBAAuB;IACtC,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gFAAgF;IAChF,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IAC7B,oEAAoE;IACpE,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,oBAAoB,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACzD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,oBAAoB,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACzD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,0EAA0E;IAC1E,YAAY,EAAE,iBAAiB,CAAC;IAChC,gEAAgE;IAChE,WAAW,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,SAAS,EAAE,cAAc,CAAC;IAC1B;;;;;OAKG;IACH,cAAc,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC9C,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,uBAAuB,EAAE,MAAM,CAAC;IAChC,qEAAqE;IACrE,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;;;;;OAOG;IACH,wBAAwB,EAAE,MAAM,IAAI,CAAC;IACrC;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,UAAiB,EACjB,sBAA0B,EAC1B,SAAS,EACT,WAAW,EACX,MAAM,EACN,cAAc,GACf,EAAE,uBAAuB,GAAG,sBAAsB,CAyoClD"}
|
|
@@ -95,6 +95,20 @@ const OVERSCROLL_MAX = 48;
|
|
|
95
95
|
// the scroll, large enough that a swipe merely coming to rest on the last pixel
|
|
96
96
|
// doesn't start a drag on jitter.
|
|
97
97
|
const CONTENT_END_HANDOFF_SLOP = 4;
|
|
98
|
+
// How far a finger resting on the body must travel before the pull becomes a
|
|
99
|
+
// sheet drag rather than a tap.
|
|
100
|
+
//
|
|
101
|
+
// This used to be zero: any downward movement promoted. A finger is never
|
|
102
|
+
// still, so tapping a control inside the sheet drifted a pixel or two and
|
|
103
|
+
// started a drag -- which suppresses the panel's transition, correctly, for as
|
|
104
|
+
// long as the sheet is tracking the finger. The close that the tap triggered
|
|
105
|
+
// then landed inside that window and cut instead of animating. A deliberate
|
|
106
|
+
// pull was unaffected; only a tap, and only a tap inside the sheet, since the
|
|
107
|
+
// scrim is outside the body and arms nothing.
|
|
108
|
+
//
|
|
109
|
+
// 8px is the conventional tap slop, a shade over iOS's own recognizer, and
|
|
110
|
+
// well under what a real pull covers in its first frames.
|
|
111
|
+
const DRAG_PROMOTION_SLOP = 8;
|
|
98
112
|
|
|
99
113
|
// Short haptic tick on detent settle where supported. iOS Safari doesn't
|
|
100
114
|
// expose navigator.vibrate, so this is a no-op there; skipped under
|
|
@@ -754,7 +768,7 @@ export function useSheetGestures({
|
|
|
754
768
|
return;
|
|
755
769
|
}
|
|
756
770
|
const delta = event.clientY - armed.startCoord;
|
|
757
|
-
if (delta >
|
|
771
|
+
if (delta > DRAG_PROMOTION_SLOP && armed.scroller.scrollTop <= 0) {
|
|
758
772
|
// Downward pull at the top: promote to a sheet drag, anchored at the
|
|
759
773
|
// original pointer-down position so the pull distance carries over.
|
|
760
774
|
armedBodyRef.current = null;
|
|
@@ -883,8 +897,8 @@ export function useSheetGestures({
|
|
|
883
897
|
// no longer scroll that way: at the top, a downward pull (delta > 0)
|
|
884
898
|
// collapses; at the bottom, an upward pull (delta < 0) expands. The
|
|
885
899
|
// opposite direction is a real scroll, so disarm and let it through.
|
|
886
|
-
const pullDownAtTop = armed.top && delta >
|
|
887
|
-
const pullUpAtBottom = armed.bottom && delta <
|
|
900
|
+
const pullDownAtTop = armed.top && delta > DRAG_PROMOTION_SLOP && atTop(scroller);
|
|
901
|
+
const pullUpAtBottom = armed.bottom && delta < -DRAG_PROMOTION_SLOP && atBottom(scroller);
|
|
888
902
|
if (pullDownAtTop || pullUpAtBottom) {
|
|
889
903
|
event.preventDefault();
|
|
890
904
|
touchDragRef.current = null;
|
|
@@ -993,11 +1007,15 @@ export function useSheetGestures({
|
|
|
993
1007
|
const contentProps = useMemo(() => ({
|
|
994
1008
|
style: {
|
|
995
1009
|
transform: activeOffset !== 0 ? `translateY(${activeOffset}px)` : undefined,
|
|
996
|
-
|
|
1010
|
+
// Gated on the sheet being up: suppression must not outlive the sheet
|
|
1011
|
+
// it was suppressing for. The host swaps the panel to its closing
|
|
1012
|
+
// state in whatever frame the dismissal lands, and a stale `none`
|
|
1013
|
+
// would apply to that transform too, cutting the exit.
|
|
1014
|
+
transition: isOpen && (isDragging || isScrollAreaReconciling || reducedMotion) ? 'none' : undefined,
|
|
997
1015
|
touchAction: 'none',
|
|
998
1016
|
overscrollBehavior: 'contain'
|
|
999
1017
|
}
|
|
1000
|
-
}), [activeOffset, isDragging, isScrollAreaReconciling, reducedMotion]);
|
|
1018
|
+
}), [activeOffset, isDragging, isOpen, isScrollAreaReconciling, reducedMotion]);
|
|
1001
1019
|
const handleProps = useMemo(() => ({
|
|
1002
1020
|
style: {
|
|
1003
1021
|
touchAction: 'none',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tab.d.ts","sourceRoot":"","sources":["../../src/TabList/Tab.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,EAAc,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAYzD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAK5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAMrD,MAAM,WAAW,QAAS,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC5D;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAmID;;;;;;;;;;;GAWG;AACH,wBAAgB,GAAG,CAAC,EAClB,EAAE,EACF,GAAG,EACH,KAAK,EACL,KAAK,EACL,aAAqB,EACrB,IAAI,EACJ,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,SAAS,EACb,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"Tab.d.ts","sourceRoot":"","sources":["../../src/TabList/Tab.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,EAAc,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAYzD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAK5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAMrD,MAAM,WAAW,QAAS,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC5D;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAmID;;;;;;;;;;;GAWG;AACH,wBAAgB,GAAG,CAAC,EAClB,EAAE,EACF,GAAG,EACH,KAAK,EACL,KAAK,EACL,aAAqB,EACrB,IAAI,EACJ,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,SAAS,EACb,EAAE,QAAQ,qBA+GV;yBA7He,GAAG"}
|
package/dist/TabList/Tab.js
CHANGED
|
@@ -186,7 +186,11 @@ export function Tab({
|
|
|
186
186
|
} : {}),
|
|
187
187
|
[EDGE_COMP_ATTR]: '',
|
|
188
188
|
'data-tab-value': value,
|
|
189
|
-
|
|
189
|
+
// Generic `true` ("the current item within a set"), not `page`: the strip
|
|
190
|
+
// switches views in place at least as often as it navigates, and claiming
|
|
191
|
+
// "current page" when no page changed is a false statement to a screen
|
|
192
|
+
// reader. Stays truthful for the `href` case too, just less specific.
|
|
193
|
+
'aria-current': isSelected ? 'true' : undefined,
|
|
190
194
|
// Roving tabindex: the tab strip is a single Tab stop. The selected tab is
|
|
191
195
|
// the tabbable one; the rest are reachable via arrow keys (handled by
|
|
192
196
|
// TabList's onKeyDown). When no tab is selected, TabList's repair effect
|
package/dist/astryx.css
CHANGED
|
@@ -548,6 +548,7 @@
|
|
|
548
548
|
.xiy8rlo:not(#\#):not(#\#):not(#\#){-webkit-mask-image:linear-gradient(to bottom,transparent,black 24px)}
|
|
549
549
|
.x1bv1mxo:not(#\#):not(#\#):not(#\#){-webkit-mask-image:linear-gradient(to bottom,transparent,black 36px)}
|
|
550
550
|
.xo9xo30:not(#\#):not(#\#):not(#\#){-webkit-mask-image:linear-gradient(to bottom,transparent,black 48px)}
|
|
551
|
+
.xt2iwb0:not(#\#):not(#\#):not(#\#){-webkit-mask-image:linear-gradient(transparent,transparent)}
|
|
551
552
|
.x5lxg6s:not(#\#):not(#\#):not(#\#){-webkit-overflow-scrolling:touch}
|
|
552
553
|
.x1i10hfl:not(#\#):not(#\#):not(#\#){-webkit-tap-highlight-color:transparent}
|
|
553
554
|
.xpdipgo:not(#\#):not(#\#):not(#\#){-webkit-touch-callout:none}
|
|
@@ -983,6 +984,7 @@
|
|
|
983
984
|
.xtdd0ob:not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to left,transparent 0%,rgba(0,0,0,.3) 2px,black var(--spacing-1))}
|
|
984
985
|
.x1b5cd1y:not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to right,transparent 0%,rgba(0,0,0,.3) 2px,black var(--spacing-1),black calc(100% - var(--spacing-1)),rgba(0,0,0,.3) calc(100% - 2px),transparent 100%)}
|
|
985
986
|
.x1ujl4f5:not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(to right,transparent 0%,rgba(0,0,0,.3) 2px,black var(--spacing-1))}
|
|
987
|
+
.x1cb67k9:not(#\#):not(#\#):not(#\#){mask-image:linear-gradient(transparent,transparent)}
|
|
986
988
|
.xw4ya9i:not(#\#):not(#\#):not(#\#){mask-image:none}
|
|
987
989
|
.x19kjcj4:not(#\#):not(#\#):not(#\#){object-fit:contain}
|
|
988
990
|
.xl1xv1r:not(#\#):not(#\#):not(#\#){object-fit:cover}
|
|
@@ -1187,6 +1189,7 @@
|
|
|
1187
1189
|
.x11xpdln:not(#\#):not(#\#):not(#\#){transition-property:transform}
|
|
1188
1190
|
.x74b7sa:not(#\#):not(#\#):not(#\#){transition-property:visibility}
|
|
1189
1191
|
.xxrbq2n:not(#\#):not(#\#):not(#\#){transition-property:width}
|
|
1192
|
+
.xv2sgk5:not(#\#):not(#\#):not(#\#){transition-timing-function:cubic-bezier(.3,0,.6,.6)}
|
|
1190
1193
|
.x9lcvmn:not(#\#):not(#\#):not(#\#){transition-timing-function:ease-out}
|
|
1191
1194
|
.xcj1dhv:not(#\#):not(#\#):not(#\#){transition-timing-function:linear}
|
|
1192
1195
|
.xlr8y92:not(#\#):not(#\#):not(#\#){transition-timing-function:var(--ease-standard)}
|
|
@@ -1761,6 +1764,7 @@
|
|
|
1761
1764
|
.xxcwgru:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{transition-property:background-color}
|
|
1762
1765
|
.xc0dz0a:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::backdrop{transition-property:opacity,display}
|
|
1763
1766
|
.x167zut7:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::backdrop{transition-property:opacity}
|
|
1767
|
+
.x1cz9k3x:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::backdrop{transition-timing-function:linear}
|
|
1764
1768
|
.x15h3t91:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::backdrop{transition-timing-function:var(--ease-standard)}
|
|
1765
1769
|
.x1dlmc9c:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::after{transition-timing-function:var(--ease-standard)}
|
|
1766
1770
|
.xa5v58t:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#)::before{transition-timing-function:var(--ease-standard)}
|
|
@@ -38,7 +38,9 @@ export interface UseListFocusOptions {
|
|
|
38
38
|
*/
|
|
39
39
|
wrap?: boolean;
|
|
40
40
|
/**
|
|
41
|
-
* Callback when Escape key is pressed.
|
|
41
|
+
* Callback when Escape key is pressed. Supplying it also makes the list
|
|
42
|
+
* consume the key (`preventDefault`); without it Escape passes through to
|
|
43
|
+
* the surrounding layer.
|
|
42
44
|
*/
|
|
43
45
|
onEscape?: () => void;
|
|
44
46
|
/**
|
|
@@ -144,7 +146,8 @@ export interface UseListFocusReturn<T extends HTMLElement = HTMLElement> {
|
|
|
144
146
|
* - ArrowUp/ArrowLeft: Move to previous item (wraps to last)
|
|
145
147
|
* - Home: Move to first item
|
|
146
148
|
* - End: Move to last item
|
|
147
|
-
* - Escape:
|
|
149
|
+
* - Escape: runs `onEscape` and consumes the key. With no `onEscape` the key
|
|
150
|
+
* is left alone, so a surrounding layer can still dismiss on it.
|
|
148
151
|
*
|
|
149
152
|
* By default the hook only *moves* focus and leaves `tabindex` management to
|
|
150
153
|
* the caller. Opt into {@link UseListFocusOptions.hasRovingTabIndex} for a hook
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListFocus.d.ts","sourceRoot":"","sources":["../../src/hooks/useListFocus.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useListFocus.d.ts","sourceRoot":"","sources":["../../src/hooks/useListFocus.ts"],"names":[],"mappings":"AAwBA;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,oBAAoB,CAAC;IAEnC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IACrE;;OAEG;IACH,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAEnC;;OAEG;IACH,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;IAEhD;;;;OAIG;IACH,WAAW,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,UAAU,EAAE,MAAM,OAAO,CAAC;IAE1B;;OAEG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;IAEzB;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,KAAK,OAAO,CAAC;IAE/C;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,WAAW,EAAE,CAAC;CAC/B;AA+FD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC9D,OAAO,GAAE,mBAAwB,GAChC,kBAAkB,CAAC,CAAC,CAAC,CA2VvB"}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
*
|
|
15
15
|
* SYNC: When modified, update:
|
|
16
16
|
* - /packages/core/src/hooks/index.ts
|
|
17
|
+
* - /packages/core/src/hooks/useListFocus.doc.mjs
|
|
18
|
+
* - /packages/core/src/hooks/useListFocus.test.tsx
|
|
17
19
|
*/
|
|
18
20
|
import { useCallback, useRef } from 'react';
|
|
19
21
|
import { isRtlElement } from "./isRtlElement.js";
|
|
@@ -127,7 +129,8 @@ function shouldDeferToCaret(target, key) {
|
|
|
127
129
|
* - ArrowUp/ArrowLeft: Move to previous item (wraps to last)
|
|
128
130
|
* - Home: Move to first item
|
|
129
131
|
* - End: Move to last item
|
|
130
|
-
* - Escape:
|
|
132
|
+
* - Escape: runs `onEscape` and consumes the key. With no `onEscape` the key
|
|
133
|
+
* is left alone, so a surrounding layer can still dismiss on it.
|
|
131
134
|
*
|
|
132
135
|
* By default the hook only *moves* focus and leaves `tabindex` management to
|
|
133
136
|
* the caller. Opt into {@link UseListFocusOptions.hasRovingTabIndex} for a hook
|
|
@@ -380,12 +383,15 @@ export function useListFocus(options = {}) {
|
|
|
380
383
|
return;
|
|
381
384
|
}
|
|
382
385
|
|
|
383
|
-
// Escape is handled regardless of orientation
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
+
// Escape is handled regardless of orientation, but only *consumed* when
|
|
387
|
+
// a handler asked for it: a list with no dismissal to perform must leave
|
|
388
|
+
// the key to whatever host layer does have one, and those defer to
|
|
389
|
+
// `defaultPrevented` (see `useFocusTrap`) or to the native popover.
|
|
386
390
|
if (e.key === 'Escape') {
|
|
387
|
-
|
|
388
|
-
|
|
391
|
+
if (onEscape) {
|
|
392
|
+
e.preventDefault();
|
|
393
|
+
onEscape();
|
|
394
|
+
}
|
|
389
395
|
return;
|
|
390
396
|
}
|
|
391
397
|
const horizontal = orientation === 'horizontal' || orientation === 'both';
|