feat(overlays): add managed overlay package
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Lifecycle and route interception
|
||||
description: Understand results, queues, Host lifetime, and router adapters.
|
||||
order: 20
|
||||
toc:
|
||||
- id: result-contracts
|
||||
title: Result contracts
|
||||
- id: queue
|
||||
title: Queues and Hosts
|
||||
- id: dismissal
|
||||
title: Dismissal
|
||||
- id: navigation
|
||||
title: Route interception
|
||||
---
|
||||
|
||||
## Result contracts {#result-contracts}
|
||||
|
||||
`openConfirm` uses `false` for cancellation, `openPrompt` uses `null`, and custom Modals, Sheets, and Drawers use `undefined` when no result is supplied. `close(value)` is checked against the generic passed to `openModal<Result>()`.
|
||||
|
||||
```tsx
|
||||
import { openModal } from "@workspace/overlays/modal"
|
||||
|
||||
const productId = await openModal<string>({
|
||||
title: "Choose a product",
|
||||
content: ({ close }) => (
|
||||
<ProductPicker onSelect={(product) => close(product.id)} />
|
||||
),
|
||||
})
|
||||
```
|
||||
|
||||
## Queues and Hosts {#queue}
|
||||
|
||||
Within one Scope, each feature has a lazily created channel and Base UI handle. Calls of the same kind enter a FIFO queue and the next request appears after the previous closing animation completes. Different features can remain visible together, so a Modal may open a Confirm.
|
||||
|
||||
Unmounting a Host settles its pending requests with their cancellation values. React Strict Mode's simulated unmount does not incorrectly cancel requests.
|
||||
|
||||
## Dismissal {#dismissal}
|
||||
|
||||
Custom content can call `dismiss()`. The close button, Escape key, and backdrop press enter the same cancellation path. Setting `dismissible: false` blocks external dismissal, while the content may still explicitly call `close(result)` or `dismiss()`.
|
||||
|
||||
## Route interception {#navigation}
|
||||
|
||||
`@workspace/overlays/navigation` depends only on Scope, not on a concrete overlay feature or router:
|
||||
|
||||
```tsx
|
||||
import { OverlayNavigationGuard } from "@workspace/overlays/navigation"
|
||||
|
||||
const adapter: OverlayNavigationAdapter = {
|
||||
subscribe(listener) {
|
||||
return router.block((transition) => {
|
||||
listener({
|
||||
cancel: transition.reset,
|
||||
proceed: transition.retry,
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
<OverlayNavigationGuard adapter={adapter} />
|
||||
```
|
||||
|
||||
The default Guard examines the current and named local Scopes and finds the last-opened layer. It dismisses an allowed layer and cancels navigation. A layer with `dismissible: false` remains open and keeps blocking. It calls `proceed()` only when no layer is open.
|
||||
|
||||
The adapter's `proceed()` must continue the exact blocked attempt instead of starting a new navigation that triggers the blocker again. Pass a custom `interceptor` to replace the policy.
|
||||
Reference in New Issue
Block a user