fix(app): polish workspace navigation surfaces
Use mobile action sheets and long-press file menus while preserving provider context across sheet portals. Keep sidebar and file-pane glyphs on shared visual rails, avoid scrollbar layout shifts, and hide keyboard affordances where shortcuts are unavailable.
This commit is contained in:
+28
-1
@@ -124,8 +124,13 @@ quietly relying on:
|
||||
renders inside its host, not necessarily at window origin. Position anchored
|
||||
content relative to the host: `anchorRect - hostRect`. This is what
|
||||
`measureFloatingPanelPortalHost()` is for.
|
||||
- **React context.** `@gorhom/portal` is not a React portal — a real one keeps
|
||||
context, this one does not. It stores the element and the host renders it, so
|
||||
context resolves at the _host's_ position. Everything provided between
|
||||
`PortalProvider` in `app/_layout.tsx` and your sheet is invisible inside it.
|
||||
This is why app-wide providers wrap `PortalProvider` rather than the reverse.
|
||||
|
||||
The fix for transforms is Gotcha 3.
|
||||
The fix for transforms is Gotcha 3. The fix for context is Gotcha 7.
|
||||
|
||||
## Gotcha 3 — Reanimated transforms vs `measureInWindow`
|
||||
|
||||
@@ -236,6 +241,28 @@ Do not treat `onChange(-1)` as a close by itself. In a stacked
|
||||
another pushed sheet. Close React state from `onDismiss`; use `onChange` only to
|
||||
track phase.
|
||||
|
||||
## Gotcha 7 — A sheet cannot read context from its call site
|
||||
|
||||
React cannot copy contexts reflectively, so the only way across the teleport in
|
||||
Gotcha 2 is to render the providers a second time, with values captured on the
|
||||
near side where they are still readable. `IsolatedBottomSheetModal` takes a
|
||||
`contextBridge` for exactly that:
|
||||
|
||||
```tsx
|
||||
const contextBridge = useCallback<ContextBridge>(
|
||||
(content) => <ThingContext.Provider value={thing}>{content}</ThingContext.Provider>,
|
||||
[thing],
|
||||
);
|
||||
```
|
||||
|
||||
The prop is **required**, and `null` is a real answer. A sheet whose content
|
||||
needs nothing local should have to say so, because the failure mode is silent
|
||||
until someone adds a `useContext` deep inside and it throws on device only —
|
||||
never on web, where the desktop path uses a real portal. `menu-surface.tsx`
|
||||
bridges the menu's two contexts; the rest pass `null`.
|
||||
|
||||
Wrapping providers _around_ the modal does nothing. They land on the wrong side.
|
||||
|
||||
## Recipe for a new anchored panel
|
||||
|
||||
Before you write a new one, ask:
|
||||
|
||||
@@ -23,6 +23,9 @@ sheets the same way a phone does.
|
||||
flipping every menu in the app to sheets at once is not a change anyone can review. Opt a menu in
|
||||
when you have actually looked at it on a phone.
|
||||
|
||||
`ContextMenu` is the exception: it defaults to `compactMode="sheet"` and enables native long press.
|
||||
Disable mobile triggering explicitly on draggable rows, where long press belongs to drag instead.
|
||||
|
||||
## Pages
|
||||
|
||||
A submenu is a page, declared as data on the surface and reached by a `MenuSubTrigger` whose `id`
|
||||
@@ -114,6 +117,10 @@ its own.
|
||||
identity changes — a pushed page taller than the one it replaced is clipped without that.
|
||||
- **Sheets size to content.** `enableDynamicSizing`, not fixed snap points. A pushed page is
|
||||
rarely the height of the page before it.
|
||||
- **The sheet's content is teleported out of the menu's subtree**, so `MenuSheetSurface` rebuilds
|
||||
both menu contexts through the sheet's `contextBridge`. Providing them around the modal puts
|
||||
them on the wrong side of the portal and every item inside throws. Gotcha 7 in
|
||||
[floating-panels.md](floating-panels.md).
|
||||
- **One overlay per menu.** Submenus render inside their parent's layer and paint no second
|
||||
backdrop, so there is exactly one `Modal` on native no matter how deep the menu goes.
|
||||
- Anchoring, flipping, and edge clamping live in `menu-anchor.ts` and are unit-tested. Fix
|
||||
|
||||
Reference in New Issue
Block a user