Skip to content

V2 release#19

Merged
josephdburdick merged 4 commits into
mainfrom
feat/v2
Apr 30, 2026
Merged

V2 release#19
josephdburdick merged 4 commits into
mainfrom
feat/v2

Conversation

@josephdburdick
Copy link
Copy Markdown
Contributor

@josephdburdick josephdburdick commented Apr 30, 2026

Summary by CodeRabbit

  • Breaking Changes

    • sizing prop → snapPoints array; SnapPointValueSnapPoint
    • onSnapPointChange → controlled API setActiveSnapPoint(point, index)
    • DRAWER_SIZING removed; use SNAP_POINT tokens; SNAP_POINT.FULL is token 'full' (previous 0.9 → NEAR_FULL)
  • New Features

    • Optional uncontrolled defaultOpen
    • New props: handleOnly, fadeFromIndex, snapToSequentialPoint, nested
  • Documentation

    • Added v1→v2 migration guide; updated README examples and API tables

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c70183b4-22be-4a51-8250-8950354c55df

📥 Commits

Reviewing files that changed from the base of the PR and between 7a588d8 and bed5d67.

📒 Files selected for processing (2)
  • src/components/Drawer.tsx
  • src/types.ts

📝 Walkthrough

Walkthrough

Migrates drawer API from v1 sizing to v2 snapPoints: renames types/constants, replaces onSnapPointChange with setActiveSnapPoint, updates resolver/helpers and hooks, adds uncontrolled defaultOpen support and new props, updates tests/docs/examples, and bumps package to 2.0.0.

Changes

Cohort / File(s) Summary
Documentation & Version
MIGRATION.md, README.md, package.json
Adds v1→v2 migration guide and codemod mappings; updates README examples/props to snapPoints/setActiveSnapPoint; bumps package version to 2.0.0.
Public Types & Exports
src/types.ts, src/index.ts
Removes DrawerSizing/DrawerSizingPreset/SnapPointValue; introduces SnapPoint (`number
Constants
src/constants.ts
Removes DRAWER_SIZING; defines SNAP_POINT with tokens 'auto' and 'full' and NEAR_FULL: 0.9; tightens typing and docs about token semantics and behavior changes.
Core Implementation
src/components/Drawer.tsx, src/hooks/useDrawerSnap.ts
Migrate sizingsnapPoints; replace resolveSizingToHeightsresolveSnapPointsToHeights; update internals/types to SnapPoint; add defaultOpen, controlled-mode notification suppression, snapToHeightAnimated(notify), drag/gesture changes (handleOnly, nested isolation), overlay fade via fadeFromIndex, sequential-swipe option, and update imperative ref.snapTo signature.
Hooks (auxiliary)
src/hooks/useDrawerKeyboardSnapMobile.ts
Retypes snap-before-keyboard storage from old value type to SnapPoint without behavioral changes.
Tests
src/components/Drawer*.test.tsx, src/components/drawer/DrawerParts.test.tsx, src/components/Drawer.coverage.test.tsx, src/hooks/useDrawerSnap*.test.ts
Update tests to use snapPoints and token values ('auto'/'full'), remove DRAWER_SIZING imports, add cases for empty snap arrays and token preservation; minor JSX formatting changes.
Playground / Examples
playground/src/PlaygroundApp.tsx
Convert examples to snapPoints/SnapPoint typing; update controlled/uncontrolled demos, ref wiring, default snap values, logs, and UI text to the new API.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client (UI / Example)
  participant Drawer as Drawer Component
  participant Hook as useDrawerSnap
  participant View as Viewport/Measurement
  participant Parent as Parent State (setActiveSnapPoint)

  Client->>Drawer: mount/open with `snapPoints: [...]`
  Drawer->>Hook: resolveSnapPointsToHeights(snapPoints, measurement)
  Hook->>View: request measurement if `'auto'` present
  View-->>Hook: measuredHeight px (or fallback)
  Hook-->>Drawer: heights[], rawValues[] (SnapPoint[])
  Drawer->>Drawer: animateToHeight(targetHeight)
  alt uncontrolled OR notify flag true
    Drawer->>Parent: setActiveSnapPoint(rawValue, index)
  else parent-controlled (activeSnapPoint present)
    Note right of Drawer: suppress notification to avoid feedback loop
  end
  Parent-->>Drawer: (optional) controlled activeSnapPoint prop update
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Hop-hop — I swapped the sizing map,

snapPoints now guide each drawer gap.
Tokens, types, docs all new,
Small hops, big leaps — v2 debut! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'V2 release' is generic and does not clearly convey the specific technical changes or primary focus of the v2 migration, such as the shift from sizing to snapPoints API. Consider a more descriptive title that highlights the main change, such as 'Refactor drawer API from sizing to snapPoints' or 'Migrate drawer to snapPoints-based snap configuration'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v2

Review rate limit: 1/3 review remaining, refill in 25 minutes and 32 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/hooks/useDrawerSnap.ts (1)

68-78: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the raw value as 'auto' in the single-stop fast path.

This returns the measured height as the raw snap value, so getActiveSnapPoint()/restore flows stop treating the active stop as 'auto' and start treating it as a fixed pixel snap. In particular, useDrawerKeyboardSnapMobile will restore a stale px value instead of the auto slot after content size changes.

💡 Suggested fix
-    return { heights: [resolvedHeight], rawValues: [resolvedHeight] }
+    return { heights: [resolvedHeight], rawValues: ['auto'] }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useDrawerSnap.ts` around lines 68 - 78, In the single-stop fast
path inside useDrawerSnap where snapPoints is ['auto'], keep the computed pixel
value for rendering but preserve the original 'auto' raw snap value so restore
flows still treat the stop as auto; specifically, return heights:
[resolvedHeight] but set rawValues: ['auto'] (or rawValues: [snapPoints[0]])
instead of rawValues: [resolvedHeight], so
getActiveSnapPoint()/useDrawerKeyboardSnapMobile continue to recognize and
restore the auto slot after content size changes.
src/components/Drawer.tsx (1)

235-248: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

setActiveSnapPoint is still drag-only here.

Because this call is gated behind dragMeta, snapTo()/expand()/collapse(), activeSnapPoint-driven resnaps, and the initial defaultSnapPoint open path never emit the callback. That breaks the documented contract and leaves controlled wrappers/listeners blind to non-drag snap changes.

💡 Suggested direction
         if (dragMeta && raw !== null) {
           const dragInfo: DragEndInfo = {
             y: dragMeta.endY,
             velocity: dragMeta.velocityY,
             progress: dragMeta.progress,
             targetSnapPoint: raw,
           }
           onDragEnd?.(
             new PointerEvent('pointerup') as unknown as PointerEvent,
             dragInfo,
           )
-          setActiveSnapPoint?.(raw, nextIdx)
         }
+        if (raw !== null) {
+          setActiveSnapPoint?.(raw, nextIdx)
+        }

You'll also want the initial open/default-snap animation to go through the same notifier path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Drawer.tsx` around lines 235 - 248, The code only calls
setActiveSnapPoint inside the dragMeta branch (when handling drag end), so
programmatic snaps (snapTo/expand/collapse), resnaps driven by activeSnapPoint,
and the initial defaultSnapPoint open path never notify external controllers;
move or add calls to setActiveSnapPoint(raw, nextIdx) (and the same onDragEnd
notifier behavior) into the general snap/animation completion flow rather than
only under dragMeta — specifically ensure indexToRawValue(nextIdx) is used to
compute raw and then call setActiveSnapPoint(raw, nextIdx) after non-drag snap
operations (snapTo, expand, collapse) and also trigger it during the initial
default-open path so controlled wrappers receive the callback for all snap
changes (refer to symbols: setActiveSnapPoint, onDragEnd, indexToRawValue,
snapTo, expand, collapse, defaultSnapPoint, activeSnapPoint).
playground/src/PlaygroundApp.tsx (1)

241-275: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Pick an initial snap that actually exists in snapPoints.

activeSnapPoint and defaultSnapPoint both start at 0.3, but this demo only exposes [0.25, 0.5, 0.75]. The drawer resolves that to 0.25, so the displayed/logged state starts out disagreeing with the actual stop.

💡 Suggested fix
-  const [active, setActive] = useState<SnapPoint>(0.3)
+  const [active, setActive] = useState<SnapPoint>(0.25)
...
-        defaultSnapPoint={0.3}
+        defaultSnapPoint={0.25}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playground/src/PlaygroundApp.tsx` around lines 241 - 275, The initial
active/default snap (active state via active and defaultSnapPoint) is set to 0.3
which does not exist in the Drawer prop snapPoints ([0.25,0.5,0.75]) causing a
mismatch; update the initial active state and defaultSnapPoint to one of the
real snapPoints (e.g., 0.25 or 0.5) or alternatively add 0.3 to the snapPoints
array so active/defaultSnapPoint matches the declared snap points; update the
constants using active, setActive, defaultSnapPoint, and the Drawer props
(snapPoints, defaultSnapPoint, activeSnapPoint) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@MIGRATION.md`:
- Around line 91-98: The example types `active` as the entire hook tuple by
doing `useState<SnapPoint>(0.5)`; instead destructure the state hook so `active`
is the SnapPoint value (e.g., use `[active, setActive] =
useState<SnapPoint>(0.5)`) so that `active` is a SnapPoint when passed to props
like `activeSnapPoint`, and keep `stops: SnapPoint[] = ['auto', 480, 'full']`
unchanged; update occurrences referencing `active` to use the value or setter
names accordingly.

In `@src/hooks/useDrawerSnap.test.ts`:
- Around line 159-163: The test assumes a zero fallback for a single 'auto' snap
but the resolver (resolveSnapPointsToHeights) currently returns a non-zero
fallback; update the test to match the resolver contract by asserting heights[0]
equals the resolver's defined fallback instead of 0—either import the exported
fallback constant from src/hooks/useDrawerSnap.ts (if available) or compute the
expected fallback via a direct call/reference to resolveSnapPointsToHeights and
use that value in the expect; ensure any other tests asserting zero are updated
consistently or, if you prefer to change behavior, modify
resolveSnapPointsToHeights and all related tests together to return zero.

---

Outside diff comments:
In `@playground/src/PlaygroundApp.tsx`:
- Around line 241-275: The initial active/default snap (active state via active
and defaultSnapPoint) is set to 0.3 which does not exist in the Drawer prop
snapPoints ([0.25,0.5,0.75]) causing a mismatch; update the initial active state
and defaultSnapPoint to one of the real snapPoints (e.g., 0.25 or 0.5) or
alternatively add 0.3 to the snapPoints array so active/defaultSnapPoint matches
the declared snap points; update the constants using active, setActive,
defaultSnapPoint, and the Drawer props (snapPoints, defaultSnapPoint,
activeSnapPoint) accordingly.

In `@src/components/Drawer.tsx`:
- Around line 235-248: The code only calls setActiveSnapPoint inside the
dragMeta branch (when handling drag end), so programmatic snaps
(snapTo/expand/collapse), resnaps driven by activeSnapPoint, and the initial
defaultSnapPoint open path never notify external controllers; move or add calls
to setActiveSnapPoint(raw, nextIdx) (and the same onDragEnd notifier behavior)
into the general snap/animation completion flow rather than only under dragMeta
— specifically ensure indexToRawValue(nextIdx) is used to compute raw and then
call setActiveSnapPoint(raw, nextIdx) after non-drag snap operations (snapTo,
expand, collapse) and also trigger it during the initial default-open path so
controlled wrappers receive the callback for all snap changes (refer to symbols:
setActiveSnapPoint, onDragEnd, indexToRawValue, snapTo, expand, collapse,
defaultSnapPoint, activeSnapPoint).

In `@src/hooks/useDrawerSnap.ts`:
- Around line 68-78: In the single-stop fast path inside useDrawerSnap where
snapPoints is ['auto'], keep the computed pixel value for rendering but preserve
the original 'auto' raw snap value so restore flows still treat the stop as
auto; specifically, return heights: [resolvedHeight] but set rawValues: ['auto']
(or rawValues: [snapPoints[0]]) instead of rawValues: [resolvedHeight], so
getActiveSnapPoint()/useDrawerKeyboardSnapMobile continue to recognize and
restore the auto slot after content size changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d8167158-9152-45c9-a113-29dcc3266fce

📥 Commits

Reviewing files that changed from the base of the PR and between ff06e8c and 48963b9.

📒 Files selected for processing (15)
  • MIGRATION.md
  • README.md
  • package.json
  • playground/src/PlaygroundApp.tsx
  • src/components/Drawer.coverage.test.tsx
  • src/components/Drawer.test.tsx
  • src/components/Drawer.tsx
  • src/components/drawer/DrawerParts.test.tsx
  • src/constants.ts
  • src/hooks/useDrawerKeyboardSnapMobile.ts
  • src/hooks/useDrawerSnap.hook.test.tsx
  • src/hooks/useDrawerSnap.test.ts
  • src/hooks/useDrawerSnap.ts
  • src/index.ts
  • src/types.ts

Comment thread MIGRATION.md Outdated
Comment thread src/hooks/useDrawerSnap.test.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/Drawer.tsx (1)

291-337: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor activeSnapPoint during the initial open.

The intro effect always animates to defaultIndex and emits setActiveSnapPoint, even when the drawer is controlled. That can briefly open to the wrong stop and push a stale value back upstream before the controlled effect runs.

🔧 Suggested fix
-      const h = snapHeights[defaultIndex] ?? minSnap
+      const initialIndex =
+        activeSnapPoint === undefined
+          ? defaultIndex
+          : resolveSnapToIndex(activeSnapPoint)
+      const h = snapHeights[initialIndex] ?? minSnap
@@
-      setSnapIndex(defaultIndex)
-      // Notify controllers of the resolved opening stop. Important when the
-      // drawer opens uncontrolled or via `defaultSnapPoint`: the parent
-      // otherwise has no way to know which numerical/raw stop the intro
-      // animation actually targeted (e.g. a measured 'auto' height) until
-      // the user drags or hits a ref control.
-      const introRaw = indexToRawValue(defaultIndex)
-      if (introRaw !== null) {
-        setActiveSnapPoint?.(introRaw, defaultIndex)
-      }
+      setSnapIndex(initialIndex)
+      if (activeSnapPoint === undefined) {
+        const introRaw = indexToRawValue(initialIndex)
+        if (introRaw !== null) {
+          setActiveSnapPoint?.(introRaw, initialIndex)
+        }
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Drawer.tsx` around lines 291 - 337, The intro useEffect
currently always targets defaultIndex and calls setActiveSnapPoint, which can
override a controlled activeSnapPoint; modify the effect to honor a controlled
activeSnapPoint by deriving the intro target from the current activeSnapPoint
(convert it to an index via your raw-to-index helper or reverse mapping) when
activeSnapPoint is not null/undefined, otherwise fall back to defaultIndex;
setSnapIndex, animate, heightMv and updateProgress should use that computed
targetIndex and only call setActiveSnapPoint when the component is uncontrolled
(i.e., when activeSnapPoint is null), and ensure you skip the intro if the
resolved target height from snapHeights is missing or <= 0.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/components/Drawer.tsx`:
- Around line 291-337: The intro useEffect currently always targets defaultIndex
and calls setActiveSnapPoint, which can override a controlled activeSnapPoint;
modify the effect to honor a controlled activeSnapPoint by deriving the intro
target from the current activeSnapPoint (convert it to an index via your
raw-to-index helper or reverse mapping) when activeSnapPoint is not
null/undefined, otherwise fall back to defaultIndex; setSnapIndex, animate,
heightMv and updateProgress should use that computed targetIndex and only call
setActiveSnapPoint when the component is uncontrolled (i.e., when
activeSnapPoint is null), and ensure you skip the intro if the resolved target
height from snapHeights is missing or <= 0.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 03783380-f073-47d5-b122-db4547166a66

📥 Commits

Reviewing files that changed from the base of the PR and between 48963b9 and 7d4f370.

📒 Files selected for processing (8)
  • MIGRATION.md
  • playground/src/PlaygroundApp.tsx
  • src/components/Drawer.coverage.test.tsx
  • src/components/Drawer.test.tsx
  • src/components/Drawer.tsx
  • src/components/drawer/DrawerParts.test.tsx
  • src/hooks/useDrawerSnap.test.ts
  • src/hooks/useDrawerSnap.ts
✅ Files skipped from review due to trivial changes (2)
  • src/components/Drawer.coverage.test.tsx
  • src/hooks/useDrawerSnap.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/Drawer.test.tsx
  • src/hooks/useDrawerSnap.test.ts
  • playground/src/PlaygroundApp.tsx

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/hooks/useDrawerSnap.ts (1)

89-100: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Dedupe can erase the 'auto' snap identity.

When 'auto' resolves to the same pixel height as a fixed stop (or 'full'), this keeps only the first raw value and drops 'auto'. Downstream indexToRawValue()/snap-index remapping then treat that slot as the fixed stop, so later content-size changes stop tracking the intrinsic-height snap. Preserving 'auto' on equal-height collisions, or storing aliases instead of a single raw value, would avoid that contract break.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useDrawerSnap.ts` around lines 89 - 100, The current dedupe loop in
useDrawerSnap.ts (building pairs from snapPoints via resolveSnapValueToPx and
then populating heights and rawValues) removes the 'auto' raw value when its
resolved px equals a fixed stop, which breaks indexToRawValue/mapping; change
the logic so equal-px collisions preserve 'auto' (or store aliases) instead of
discarding it—e.g., when encountering a px already in heights, if p.raw is the
intrinsic 'auto' value append or prefer it in rawValues for that px (or switch
to a structure that stores an array of raw values per px and adjust
indexToRawValue accordingly) so the identity of 'auto' is retained for
downstream remapping.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/hooks/useDrawerSnap.ts`:
- Around line 57-60: The hook useDrawerSnap should not return an empty
heights/rawValues set for snapPoints === [] because Drawer.tsx can end up with
open === true but no reachable height; update useDrawerSnap to either
throw/console.warn when snapPoints is an empty array (reject [] up front) or
normalize an empty snapPoints to a sane default (e.g., a single minimum stop)
before returning so heights/rawValues are never empty; locate the early-return
in useDrawerSnap that checks snapPoints.length === 0 and replace it with
normalization or an explicit error path and ensure Drawer.tsx still receives a
non-empty heights array when open is true.

In `@src/types.ts`:
- Around line 58-65: Update the JSDoc for setActiveSnapPoint to accurately
reflect when the callback is invoked: state that setActiveSnapPoint (signature:
setActiveSnapPoint?: (point: SnapPoint, index: number) => void) is called for
user/requested snap changes (drag, ref controls, defaultSnapPoint resolution, or
programmatic requests initiated by this component) but is deliberately NOT
invoked for parent-driven controlled updates to activeSnapPoint; replace wording
that claims it fires on arbitrary programmatic/parent updates with this
clarified controlled-state contract.

---

Outside diff comments:
In `@src/hooks/useDrawerSnap.ts`:
- Around line 89-100: The current dedupe loop in useDrawerSnap.ts (building
pairs from snapPoints via resolveSnapValueToPx and then populating heights and
rawValues) removes the 'auto' raw value when its resolved px equals a fixed
stop, which breaks indexToRawValue/mapping; change the logic so equal-px
collisions preserve 'auto' (or store aliases) instead of discarding it—e.g.,
when encountering a px already in heights, if p.raw is the intrinsic 'auto'
value append or prefer it in rawValues for that px (or switch to a structure
that stores an array of raw values per px and adjust indexToRawValue
accordingly) so the identity of 'auto' is retained for downstream remapping.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 096ccb84-9065-432f-a5a3-b0e3d739da67

📥 Commits

Reviewing files that changed from the base of the PR and between 7d4f370 and 7a588d8.

📒 Files selected for processing (3)
  • src/components/Drawer.tsx
  • src/hooks/useDrawerSnap.ts
  • src/types.ts

Comment on lines +57 to +60
// Empty array: caller passed `snapPoints={[]}` (rare but legal). Keep the
// hook output empty so the Drawer renders nothing snappable.
if (snapPoints.length === 0) {
return { heights: [], rawValues: [] }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Empty snapPoints currently opens a backdrop-only drawer.

Returning an empty snap set here leaves Drawer.tsx with open === true but no reachable height, so the overlay/body lock/focus handling still mount while the panel stays at 0px. In modal mode that effectively traps the page behind an invisible drawer. Please either reject [] up front or normalize it to a real stop before the component renders.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useDrawerSnap.ts` around lines 57 - 60, The hook useDrawerSnap
should not return an empty heights/rawValues set for snapPoints === [] because
Drawer.tsx can end up with open === true but no reachable height; update
useDrawerSnap to either throw/console.warn when snapPoints is an empty array
(reject [] up front) or normalize an empty snapPoints to a sane default (e.g., a
single minimum stop) before returning so heights/rawValues are never empty;
locate the early-return in useDrawerSnap that checks snapPoints.length === 0 and
replace it with normalization or an explicit error path and ensure Drawer.tsx
still receives a non-empty heights array when open is true.

Comment thread src/types.ts Outdated
@josephdburdick josephdburdick merged commit 235ecf6 into main Apr 30, 2026
5 checks passed
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.

1 participant