Skip to content

feat!: close part of the stack, not only all of it - #42

Merged
arekkubaczkowski merged 1 commit into
mainfrom
claude/partial-close-yywpjl
Aug 5, 2026
Merged

feat!: close part of the stack, not only all of it#42
arekkubaczkowski merged 1 commit into
mainfrom
claude/partial-close-yywpjl

Conversation

@arekkubaczkowski

Copy link
Copy Markdown
Owner

closeAll() could only empty a group. A sheet deep in a stack had no way to say close what is above me, or close three — even though the store already knew enough to do both: stackOrderByGroup is ordered, and a sheet knows its own id. This was an API gap, not a missing capability.

API

const { closeAll, closeTo, closeDepth } = useBottomSheetManager();
const { closeAbove } = useBottomSheetContext();

closeAbove()        // from inside a sheet — everything above me, no id needed
closeTo(id)         // down to a sheet, leaving it open
closeDepth(3)       // three from the top

All three route through closeAllAnimated with a resolved range, so stagger, onBeforeClose interception and stoppedAt reporting behave exactly as before.

Two decisions worth reviewing

Both bounds together take the narrower one, so neither can widen the other.

An until naming a sheet outside the group closes nothing. For a bounded call, silently emptying the stack is the worst available fallback — it is the opposite of what was asked. A __DEV__ warning says which id was not found.

Breaking

  • CloseAllResult.closedAll.completed. Bounded by until/depth it means closed the requested range, which the old name contradicted.
  • CloseAllOptionsCascadeOptions, now exported from the store.

Verification

11 new tests (93 total) cover the boundaries: depth 0 and negative, depth past the stack, an until that is the top sheet, an until that is absent, both bounds fighting in either direction, and an interceptor stopping a bounded cascade.

Checked on device via the new Partial Close demo — closeDepth(1) closed exactly one, closeTo(level 1) closed two and left level 1 open, closeDepth(0) and an unknown until both closed nothing. The unknown-until case was confirmed through the debugger rather than the screen: it renders the same result as closeDepth(0), so the warning is the only thing that distinguishes them.

Not included

Docs still reference closedAll and CloseAllOptions (docs/docs/api/hooks.md, types.md).

closeAll() could only empty a group, so a sheet deep in a stack had no way to
say "close what is above me" or "close three". Both were expressible from the
store already — stackOrderByGroup is ordered and a sheet knows its own id — so
this is an API gap, not a missing capability.

  closeTo(id)        close down to a sheet, leaving it open
  closeDepth(n)      close n sheets from the top
  closeAbove()       from useBottomSheetContext: everything above me

All three route through closeAllAnimated with a resolved range, so stagger,
onBeforeClose interception and stoppedAt reporting behave exactly as before.
Passing both bounds takes whichever closes fewer sheets, so neither can widen
the other. An `until` naming a sheet outside the group closes nothing — for a
bounded call, silently emptying the stack is the worst possible fallback.

BREAKING: CloseAllResult.closedAll is now .completed. Bounded by until/depth it
means "closed the requested range", which the old name contradicted.
CloseAllOptions is replaced by CascadeOptions, exported from the store.

11 tests cover the boundaries: depth 0 and negative, depth past the stack, an
until that is the top sheet, an until that is absent, both bounds fighting in
either direction, and an interceptor stopping a bounded cascade.
@arekkubaczkowski
arekkubaczkowski merged commit bc76299 into main Aug 5, 2026
3 checks passed
arekkubaczkowski added a commit that referenced this pull request Aug 5, 2026
…43)

* feat(example): cover the lifecycle APIs the example never exercised

Five public APIs shipped with no way to try them in the example app, which
made them the only parts of 2.0 that could not be checked on a device:
status flags, open rejection, forceClose/destroyAll, backdrop: false, and
group isolation.

- Status flags & open rejection panel: the four `useBottomSheetStatus` flags
  read live from outside the sheet, next to the rejection that misreading
  them causes. `isOpen` excludes `'opening'`, so the two guard buttons
  diverge when pressed mid-animation. Re-opening lives *inside* the sheet —
  the backdrop swallows every tap outside it, so the panel is unreachable
  while a sheet is up.
- Force close & teardown: close() vs forceClose() vs destroyAll() against a
  refusing interceptor, plus a child pushed with `backdrop: false`.
- Group isolation: a second manager mounted at the app root with its own
  host, so closeAll() in either group leaves the other standing. Opening the
  persistent scanner from group B reproduces `group-mismatch`.

Docs: `closedAll` → `completed` and `CloseAllOptions` → `CascadeOptions`
after #42, and hooks.md documents closeTo/closeDepth/closeAbove, which it
had not picked up at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(example): exercise swmansion keyboardBehavior="inset"

The one adapter prop the example could not reach: `inset` needs the optional
peer `react-native-keyboard-controller`, which was not installed, so the
adapter took its missing-peer fallback and the feature shipped unverified.

Installs the peer, mounts `KeyboardProvider` at the app root, and adds a
sheet with a text field plus a bottom row, in both `inset` and `none` so the
difference is visible rather than asserted. With `inset` the sheet grows by
the keyboard height and the field, the counter row and the button below it
all stay reachable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants