@patternmode/stacksheet 1.3.8 → 1.4.0

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.
@@ -1,5 +1,3 @@
1
1
  import type { HeaderRenderProps } from "../types";
2
- export declare const DefaultHeader: ({ isNested, onBack, onClose, className, }: HeaderRenderProps & {
3
- className?: string;
4
- }) => import("react").JSX.Element;
2
+ export declare const DefaultHeader: ({ isNested, onBack, onClose }: HeaderRenderProps) => import("react").JSX.Element;
5
3
  //# sourceMappingURL=default-header.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"default-header.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/default-header.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,eAAO,MAAM,aAAa,GAAI,2CAK3B,iBAAiB,GAAG;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,gCAuBA,CAAC"}
1
+ {"version":3,"file":"default-header.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/default-header.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAIlD,eAAO,MAAM,aAAa,GAAI,+BAA+B,iBAAiB,gCAqB7E,CAAC"}
@@ -1,14 +1,13 @@
1
1
  import type { ComponentType, ReactNode } from "react";
2
2
  import type { HeaderRenderProps } from "../types";
3
3
  export declare const PanelInnerContent: {
4
- ({ isComposable, shouldRender, Content, data, renderHeader, headerProps, headerClassName, }: {
4
+ ({ isComposable, shouldRender, Content, data, renderHeader, headerProps, }: {
5
5
  isComposable: boolean;
6
6
  shouldRender: boolean;
7
7
  Content: ComponentType<Record<string, unknown>> | undefined;
8
8
  data: Record<string, unknown>;
9
9
  renderHeader?: false | ((props: HeaderRenderProps) => ReactNode);
10
10
  headerProps: HeaderRenderProps;
11
- headerClassName: string | undefined;
12
11
  }): import("react").JSX.Element | null;
13
12
  displayName: string;
14
13
  };
@@ -1 +1 @@
1
- {"version":3,"file":"sheet-panel-content.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/sheet-panel-content.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAGlD,eAAO,MAAM,iBAAiB;iGAQ3B;QACD,YAAY,EAAE,OAAO,CAAC;QACtB,YAAY,EAAE,OAAO,CAAC;QACtB,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;QAC5D,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,YAAY,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC,CAAC;QACjE,WAAW,EAAE,iBAAiB,CAAC;QAC/B,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;KACrC;;CAwBA,CAAC"}
1
+ {"version":3,"file":"sheet-panel-content.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/sheet-panel-content.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAGlD,eAAO,MAAM,iBAAiB;gFAO3B;QACD,YAAY,EAAE,OAAO,CAAC;QACtB,YAAY,EAAE,OAAO,CAAC;QACtB,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;QAC5D,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,YAAY,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC,CAAC;QACjE,WAAW,EAAE,iBAAiB,CAAC;KAChC;;CAoBA,CAAC"}
@@ -1,6 +1,7 @@
1
- import type { Side } from "../types";
2
- export declare const BottomHandle: ({ onDismiss }: {
1
+ import type { HandlePosition, Side } from "../types";
2
+ export declare const BottomHandle: ({ onDismiss, position, }: {
3
3
  onDismiss?: () => void;
4
+ position?: HandlePosition;
4
5
  }) => import("react").JSX.Element;
5
6
  export declare const SideHandle: ({ side, isHovered, onDismiss, }: {
6
7
  side: Side;
@@ -1 +1 @@
1
- {"version":3,"file":"sheet-panel-handles.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/sheet-panel-handles.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAErC,eAAO,MAAM,YAAY,GAAI,eAAe;IAAE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,gCAUrE,CAAC;AACF,eAAO,MAAM,UAAU,GAAI,iCAIxB;IACD,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB,gCAgBA,CAAC"}
1
+ {"version":3,"file":"sheet-panel-handles.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/sheet-panel-handles.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAErD,eAAO,MAAM,YAAY,GAAI,0BAG1B;IACD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B,gCAiBA,CAAC;AACF,eAAO,MAAM,UAAU,GAAI,iCAIxB;IACD,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB,gCAgBA,CAAC"}
@@ -13,6 +13,8 @@ export interface SheetPanelProps {
13
13
  isNested: boolean;
14
14
  isTop: boolean;
15
15
  item: SheetItem;
16
+ /** On-screen keyboard height (px) to lift bottom sheets above; 0 when inactive. */
17
+ keyboardInset: number;
16
18
  layout?: StacksheetLayout;
17
19
  onSnap: (index: number) => void;
18
20
  pop: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"sheet-panel-types.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/sheet-panel-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,IAAI,EACJ,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAC5D,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC,CAAC;IACjE,YAAY,EAAE,OAAO,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB"}
1
+ {"version":3,"file":"sheet-panel-types.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/sheet-panel-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,IAAI,EACJ,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAC5D,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,mFAAmF;IACnF,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC,CAAC;IACjE,YAAY,EAAE,OAAO,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"sheet-panel.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/sheet-panel.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAyT3D,eAAO,MAAM,UAAU,GAAI,OAAO,eAAe,gCAiFhD,CAAC"}
1
+ {"version":3,"file":"sheet-panel.d.ts","sourceRoot":"","sources":["../../src/SheetPanel/sheet-panel.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AA8U3D,eAAO,MAAM,UAAU,GAAI,OAAO,eAAe,gCAkFhD,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import type { SheetOptionalContentPartProps } from "./sheet-part-types";
2
- export declare const SheetHandle: ({ asChild, className, style, children, }: SheetOptionalContentPartProps) => import("react").JSX.Element;
2
+ export declare const SheetHandle: ({ asChild, className, style, children, }: SheetOptionalContentPartProps) => import("react").JSX.Element | null;
3
3
  //# sourceMappingURL=sheet-handle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sheet-handle.d.ts","sourceRoot":"","sources":["../../src/SheetParts/sheet-handle.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AAExE,eAAO,MAAM,WAAW,GAAI,0CAKzB,6BAA6B,gCAoB/B,CAAC"}
1
+ {"version":3,"file":"sheet-handle.d.ts","sourceRoot":"","sources":["../../src/SheetParts/sheet-handle.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AAExE,eAAO,MAAM,WAAW,GAAI,0CAKzB,6BAA6B,uCA0B/B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sheet-header.d.ts","sourceRoot":"","sources":["../../src/SheetParts/sheet-header.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,eAAO,MAAM,WAAW,GAAI,yCAAyC,cAAc,gCAalF,CAAC"}
1
+ {"version":3,"file":"sheet-header.d.ts","sourceRoot":"","sources":["../../src/SheetParts/sheet-header.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,eAAO,MAAM,WAAW,GAAI,yCAAyC,cAAc,gCAWlF,CAAC"}
@@ -13,7 +13,7 @@ export declare const Sheet: {
13
13
  readonly Close: ({ asChild, className, style, children, }: import("./sheet-part-types").SheetOptionalContentPartProps) => import("react").JSX.Element;
14
14
  readonly Description: ({ asChild, className, style, children }: import("./sheet-part-types").SheetPartProps) => import("react").JSX.Element;
15
15
  readonly Footer: ({ asChild, className, style, children }: import("./sheet-part-types").SheetPartProps) => import("react").JSX.Element;
16
- readonly Handle: ({ asChild, className, style, children, }: import("./sheet-part-types").SheetOptionalContentPartProps) => import("react").JSX.Element;
16
+ readonly Handle: ({ asChild, className, style, children, }: import("./sheet-part-types").SheetOptionalContentPartProps) => import("react").JSX.Element | null;
17
17
  readonly Header: ({ asChild, className, style, children }: import("./sheet-part-types").SheetPartProps) => import("react").JSX.Element;
18
18
  readonly Title: ({ asChild, className, style, children }: import("./sheet-part-types").SheetPartProps) => import("react").JSX.Element;
19
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EAKd,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAkDjB,wGAAwG;AACxG,eAAO,MAAM,aAAa,GAAI,SAAQ,gBAAqB,KAAG,cAa7D,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EAKd,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAoDjB,wGAAwG;AACxG,eAAO,MAAM,aAAa,GAAI,SAAQ,gBAAqB,KAAG,cAa7D,CAAC"}
package/dist/index.mjs CHANGED
@@ -56,10 +56,12 @@ const DEFAULT_CONFIG = {
56
56
  closeThreshold: .25,
57
57
  dismissible: true,
58
58
  drag: true,
59
+ handle: "inside",
59
60
  lockScroll: true,
60
61
  maxDepth: Number.POSITIVE_INFINITY,
61
62
  maxWidth: "90vw",
62
63
  modal: true,
64
+ repositionInputs: true,
63
65
  scaleBackgroundAmount: .97,
64
66
  shouldScaleBackground: false,
65
67
  showOverlay: true,
@@ -169,6 +171,79 @@ const useViewportHeight = (active) => {
169
171
  }, []);
170
172
  return active ? height ?? 0 : 0;
171
173
  };
174
+ /** Input types that don't summon the on-screen keyboard. */
175
+ const NON_TEXT_INPUT_TYPES = new Set([
176
+ "button",
177
+ "checkbox",
178
+ "color",
179
+ "file",
180
+ "hidden",
181
+ "image",
182
+ "radio",
183
+ "range",
184
+ "reset",
185
+ "submit"
186
+ ]);
187
+ /** True for elements whose focus raises the on-screen keyboard. */
188
+ const isEditableElement = (el) => {
189
+ if (!(el instanceof HTMLElement)) return false;
190
+ if (el.isContentEditable) return true;
191
+ if (el instanceof HTMLTextAreaElement) return true;
192
+ if (el instanceof HTMLInputElement) return !NON_TEXT_INPUT_TYPES.has(el.type);
193
+ return false;
194
+ };
195
+ /**
196
+ * Height (px) the on-screen keyboard occupies while a field inside `containerRef`
197
+ * is focused, else `0`. Derived from the gap between the layout viewport and the
198
+ * (keyboard-shrunk, possibly panned) visual viewport.
199
+ *
200
+ * Focus-gated to fields *inside the container* so unrelated `visualViewport`
201
+ * changes — Android URL-bar collapse, or typing into the page behind a
202
+ * non-modal sheet — don't move the sheet, and zero while pinch-zoomed since a
203
+ * shrunk visual viewport then isn't a keyboard. rAF-throttled because iOS
204
+ * fires many resize events over the keyboard's open animation.
205
+ */
206
+ const useKeyboardInset = (active, containerRef) => {
207
+ const [inset, setInset] = useState(0);
208
+ useEffect(() => {
209
+ const canListen = active && typeof window !== "undefined";
210
+ const container = containerRef.current;
211
+ const viewport = canListen ? window.visualViewport : void 0;
212
+ let frame = 0;
213
+ let scheduled = false;
214
+ const measure = () => {
215
+ const el = document.activeElement;
216
+ const focused = isEditableElement(el) && container !== null && container.contains(el);
217
+ const zoomed = (viewport?.scale ?? 1) > 1;
218
+ const visibleHeight = viewport?.height ?? window.innerHeight;
219
+ const gap = window.innerHeight - (viewport?.offsetTop ?? 0) - visibleHeight;
220
+ setInset(focused && !zoomed ? Math.max(0, gap) : 0);
221
+ };
222
+ const schedule = () => {
223
+ if (scheduled) return;
224
+ scheduled = true;
225
+ frame = window.requestAnimationFrame(() => {
226
+ scheduled = false;
227
+ measure();
228
+ });
229
+ };
230
+ if (canListen) {
231
+ container?.addEventListener("focusin", schedule);
232
+ container?.addEventListener("focusout", schedule);
233
+ viewport?.addEventListener("resize", schedule, { passive: true });
234
+ viewport?.addEventListener("scroll", schedule, { passive: true });
235
+ schedule();
236
+ }
237
+ return () => {
238
+ if (frame !== 0) window.cancelAnimationFrame(frame);
239
+ container?.removeEventListener("focusin", schedule);
240
+ container?.removeEventListener("focusout", schedule);
241
+ viewport?.removeEventListener("resize", schedule);
242
+ viewport?.removeEventListener("scroll", schedule);
243
+ };
244
+ }, [active, containerRef]);
245
+ return active ? inset : 0;
246
+ };
172
247
  const BODY_SCALE_TRANSITION = "transform 500ms cubic-bezier(0.32, 0.72, 0, 1), border-radius 500ms cubic-bezier(0.32, 0.72, 0, 1)";
173
248
  /** Fallback delay (transition duration + margin) if `transitionend` never fires. */
174
249
  const BODY_SCALE_RESET_FALLBACK_MS = 600;
@@ -319,14 +394,12 @@ const getSnapOffset = (snapIndex, snapHeights, panelHeight) => {
319
394
  //#region src/renderer-helpers.ts
320
395
  const EMPTY_CLASSNAMES = {
321
396
  backdrop: "",
322
- header: "",
323
397
  panel: ""
324
398
  };
325
399
  const resolveClassNames = (cn) => {
326
400
  if (!cn) return EMPTY_CLASSNAMES;
327
401
  return {
328
402
  backdrop: cn.backdrop ?? "",
329
- header: cn.header ?? "",
330
403
  panel: cn.panel ?? ""
331
404
  };
332
405
  };
@@ -358,8 +431,21 @@ const VISUAL_TWEEN = {
358
431
  const SHADOW_SM = "0px 1px 3px 0px rgba(0,0,0,0.06), 0px 6px 12px 0px rgba(0,0,0,0.06)";
359
432
  const SHADOW_LG = "0px 8px 24px 0px rgba(0,0,0,0.06), 0px 24px 48px 0px rgba(0,0,0,0.04), 0px 48px 96px 0px rgba(0,0,0,0.03)";
360
433
  const getShadow = (isNested) => isNested ? SHADOW_SM : SHADOW_LG;
361
- const buildPanelStyle = (panelStyles, isTop, hasPanelClass, isDragging) => ({
434
+ /**
435
+ * Clear the on-screen keyboard. Plain sheets stay anchored at bottom: 0 and
436
+ * pad their content up instead — the panel surface extends under the keyboard
437
+ * (and iOS Safari's floating URL-pill chrome), so no backdrop gap ever shows
438
+ * between sheet and keyboard, and measurement error hides behind the keyboard.
439
+ * Snap sheets are transform-anchored, so they lift via `bottom` (not the
440
+ * Motion `y` transform) and keep their viewport-driven sizing untouched.
441
+ */
442
+ const getKeyboardClearance = (keyboardInset, padForKeyboard) => {
443
+ if (keyboardInset <= 0) return {};
444
+ return padForKeyboard ? { paddingBottom: keyboardInset } : { bottom: keyboardInset };
445
+ };
446
+ const buildPanelStyle = (panelStyles, isTop, hasPanelClass, isDragging, keyboardInset, padForKeyboard) => ({
362
447
  ...panelStyles,
448
+ ...getKeyboardClearance(keyboardInset, padForKeyboard),
363
449
  pointerEvents: isTop ? "auto" : "none",
364
450
  ...isTop ? {} : { contain: "layout style paint" },
365
451
  ...isDragging ? { transition: "none" } : {},
@@ -742,6 +828,12 @@ const useSheetPanel = () => {
742
828
  //#endregion
743
829
  //#region src/stacking.ts
744
830
  /**
831
+ * Resting height of a bottom sheet. `dvh` tracks the dynamic viewport on iOS
832
+ * Safari (accounts for browser chrome). Shared so the keyboard-inset `calc()`
833
+ * in `buildPanelStyle` can't drift from the panel's base height.
834
+ */
835
+ const BOTTOM_SHEET_HEIGHT = "85dvh";
836
+ /**
745
837
  * Compute visual transforms for a panel at a given depth.
746
838
  * depth=0 is the top (foreground) panel.
747
839
  * Panels beyond renderThreshold are clamped to the edge position and faded out.
@@ -818,6 +910,7 @@ const getPanelStyles = (side, config, index) => {
818
910
  const base = {
819
911
  display: "flex",
820
912
  flexDirection: "column",
913
+ outline: "none",
821
914
  position: "fixed",
822
915
  transformOrigin: getTransformOrigin(side),
823
916
  willChange: "transform",
@@ -826,9 +919,9 @@ const getPanelStyles = (side, config, index) => {
826
919
  if (side === "bottom") return {
827
920
  ...base,
828
921
  bottom: 0,
829
- height: "85dvh",
922
+ height: BOTTOM_SHEET_HEIGHT,
830
923
  left: 0,
831
- maxHeight: "85dvh",
924
+ maxHeight: BOTTOM_SHEET_HEIGHT,
832
925
  right: 0
833
926
  };
834
927
  const sideStyles = side === "right" ? {
@@ -876,34 +969,25 @@ const XIcon = () => /* @__PURE__ */ jsx("svg", {
876
969
  });
877
970
  //#endregion
878
971
  //#region src/SheetPanel/default-header.tsx
879
- const DefaultHeader = ({ isNested, onBack, onClose, className }) => /* @__PURE__ */ jsxs("div", {
880
- className: `flex shrink-0 items-center justify-between px-4 pt-4 pb-2 ${className ?? ""}`,
881
- children: [/* @__PURE__ */ jsx("div", {
882
- className: "flex items-center gap-2",
883
- children: isNested && /* @__PURE__ */ jsx("button", {
884
- "aria-label": "Back",
885
- className: "flex min-h-11 min-w-11 shrink-0 cursor-pointer items-center justify-center rounded-full border-none bg-black/5 p-0 text-inherit opacity-70 transition-opacity duration-150 hover:opacity-100",
886
- onClick: onBack,
887
- type: "button",
888
- children: /* @__PURE__ */ jsx(ArrowLeftIcon, {})
889
- })
890
- }), /* @__PURE__ */ jsx("button", {
891
- "aria-label": "Close",
892
- className: "flex min-h-11 min-w-11 shrink-0 cursor-pointer items-center justify-center rounded-full border-none bg-black/5 p-0 text-inherit opacity-70 transition-opacity duration-150 hover:opacity-100",
893
- onClick: onClose,
894
- type: "button",
895
- children: /* @__PURE__ */ jsx(XIcon, {})
896
- })]
897
- });
972
+ const DefaultHeader = ({ isNested, onBack, onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [isNested && /* @__PURE__ */ jsx("button", {
973
+ "aria-label": "Back",
974
+ className: "absolute left-2 top-2 z-20 flex min-h-11 min-w-11 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-50 transition-opacity duration-150 hover:opacity-100",
975
+ onClick: onBack,
976
+ type: "button",
977
+ children: /* @__PURE__ */ jsx(ArrowLeftIcon, {})
978
+ }), /* @__PURE__ */ jsx("button", {
979
+ "aria-label": "Close",
980
+ className: "absolute right-2 top-2 z-20 flex min-h-11 min-w-11 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-50 transition-opacity duration-150 hover:opacity-100",
981
+ onClick: onClose,
982
+ type: "button",
983
+ children: /* @__PURE__ */ jsx(XIcon, {})
984
+ })] });
898
985
  //#endregion
899
986
  //#region src/SheetPanel/sheet-panel-content.tsx
900
- const PanelInnerContent = ({ isComposable, shouldRender, Content, data, renderHeader, headerProps, headerClassName }) => {
987
+ const PanelInnerContent = ({ isComposable, shouldRender, Content, data, renderHeader, headerProps }) => {
901
988
  if (isComposable) return shouldRender && Content !== void 0 ? /* @__PURE__ */ jsx(Content, { ...data }) : null;
902
989
  const customHeader = renderHeader !== void 0 && renderHeader !== false ? renderHeader : void 0;
903
- return /* @__PURE__ */ jsxs(Fragment, { children: [customHeader === void 0 ? /* @__PURE__ */ jsx(DefaultHeader, {
904
- ...headerProps,
905
- className: headerClassName
906
- }) : customHeader(headerProps), shouldRender && Content !== void 0 && /* @__PURE__ */ jsx("div", {
990
+ return /* @__PURE__ */ jsxs(Fragment, { children: [customHeader === void 0 ? /* @__PURE__ */ jsx(DefaultHeader, { ...headerProps }) : customHeader(headerProps), shouldRender && Content !== void 0 && /* @__PURE__ */ jsx("div", {
907
991
  className: "min-h-0 flex-1 overflow-y-auto overscroll-contain",
908
992
  "data-stacksheet-no-drag": "",
909
993
  children: /* @__PURE__ */ jsx(Content, { ...data })
@@ -955,9 +1039,9 @@ const ModalFocusTrap = ({ enabled, active, fallbackRef, children }) => {
955
1039
  };
956
1040
  //#endregion
957
1041
  //#region src/SheetPanel/sheet-panel-handles.tsx
958
- const BottomHandle = ({ onDismiss }) => /* @__PURE__ */ jsx("button", {
1042
+ const BottomHandle = ({ onDismiss, position = "inside" }) => /* @__PURE__ */ jsx("button", {
959
1043
  "aria-label": "Dismiss",
960
- className: "absolute inset-x-0 top-0 z-10 flex w-full cursor-grab touch-none items-center justify-center border-none bg-transparent pt-2.5 pb-2",
1044
+ className: joinClassNames("absolute inset-x-0 z-10 flex w-full cursor-grab touch-none items-center justify-center border-none bg-transparent text-inherit", position === "outside" ? "bottom-full pt-1 pb-2" : "top-0 pt-2.5 pb-2"),
961
1045
  "data-stacksheet-handle": "",
962
1046
  onClick: onDismiss,
963
1047
  type: "button",
@@ -1036,7 +1120,10 @@ const getOptionalSideHandle = ({ isHovered, onDismiss, show, side }) => show ? /
1036
1120
  onDismiss,
1037
1121
  side
1038
1122
  }) : null;
1039
- const getOptionalBottomHandle = (show, onDismiss) => show ? /* @__PURE__ */ jsx(BottomHandle, { onDismiss }) : null;
1123
+ const getOptionalBottomHandle = (show, onDismiss, position) => show ? /* @__PURE__ */ jsx(BottomHandle, {
1124
+ onDismiss,
1125
+ position
1126
+ }) : null;
1040
1127
  const completeOpeningAnimation = (hasEnteredRef, isTop, onOpenCompleteRef) => {
1041
1128
  if (isTop && !hasEnteredRef.current) {
1042
1129
  hasEnteredRef.current = true;
@@ -1118,7 +1205,7 @@ const useSheetPanelContext = ({ close, isNested, isTop, pop, side }, panelId) =>
1118
1205
  };
1119
1206
  };
1120
1207
  const useSheetPanelModel = (props) => {
1121
- const { item, index, depth, isTop, isNested, side, config, classNames, pop, close, snapHeights, activeSnapIndex, layout, renderHeader, slideFrom, slideTarget, spring, stackSpring } = props;
1208
+ const { item, index, depth, isTop, isNested, side, config, classNames, pop, close, snapHeights, keyboardInset, activeSnapIndex, layout, renderHeader, slideFrom, slideTarget, spring, stackSpring } = props;
1122
1209
  const panelRef = useRef(null);
1123
1210
  const [isHovered, setIsHovered] = useState(false);
1124
1211
  const measuredHeight = usePanelHeight(panelRef, snapHeights.length > 0);
@@ -1132,7 +1219,8 @@ const useSheetPanelModel = (props) => {
1132
1219
  const isComposable = resolvePanelLayout(layout, renderHeader) === "composable";
1133
1220
  const hasPanelClass = classNames.panel !== "";
1134
1221
  const dragOffset = getDragTransform(side, dragState.offset);
1135
- const panelStyle = buildPanelStyle(panelStyles, isTop, hasPanelClass, dragState.isDragging);
1222
+ const activeKeyboardInset = isTop && side === "bottom" ? keyboardInset : 0;
1223
+ const panelStyle = buildPanelStyle(panelStyles, isTop, hasPanelClass, dragState.isDragging, activeKeyboardInset, snapHeights.length === 0);
1136
1224
  const headerProps = getHeaderProps({
1137
1225
  close,
1138
1226
  isNested,
@@ -1155,7 +1243,7 @@ const useSheetPanelModel = (props) => {
1155
1243
  const animateTarget = buildAnimateTarget(slideTarget, getStackOffset(side, transform.offset), dragOffset, transform, animatedRadius, transition, snapYOffset, isTop);
1156
1244
  const initialRadius = getInitialRadius(side);
1157
1245
  const showSideHandle = isTop && side !== "bottom";
1158
- const showBottomHandle = isTop && side === "bottom";
1246
+ const showBottomHandle = isTop && side === "bottom" && !isComposable;
1159
1247
  const dismiss = isNested ? pop : close;
1160
1248
  const sideHandle = getOptionalSideHandle({
1161
1249
  isHovered,
@@ -1166,7 +1254,8 @@ const useSheetPanelModel = (props) => {
1166
1254
  return {
1167
1255
  animateTarget,
1168
1256
  ariaProps,
1169
- bottomHandle: getOptionalBottomHandle(showBottomHandle, dismiss),
1257
+ bottomHandle: getOptionalBottomHandle(showBottomHandle, dismiss, config.handle),
1258
+ bottomHandleOutside: showBottomHandle && config.handle === "outside",
1170
1259
  handleAnimationComplete,
1171
1260
  headerProps,
1172
1261
  hoverProps: getPanelHoverProps(showSideHandle, setIsHovered),
@@ -1182,7 +1271,7 @@ const useSheetPanelModel = (props) => {
1182
1271
  };
1183
1272
  const SheetPanel = (props) => {
1184
1273
  const { item, isTop, config, classNames, Content, shouldRender, renderHeader, prefersReducedMotion } = props;
1185
- const { animateTarget, ariaProps, bottomHandle, handleAnimationComplete, headerProps, hoverProps, inactivePanelProps, initialRadius, isComposable, panelContext, panelRef, panelStyle, resolvedSlideFrom, sideHandle } = useSheetPanelModel(props);
1274
+ const { animateTarget, ariaProps, bottomHandle, bottomHandleOutside, handleAnimationComplete, headerProps, hoverProps, inactivePanelProps, initialRadius, isComposable, panelContext, panelRef, panelStyle, resolvedSlideFrom, sideHandle } = useSheetPanelModel(props);
1186
1275
  const panelContent = /* @__PURE__ */ jsxs(m.div, {
1187
1276
  animate: animateTarget,
1188
1277
  className: classNames.panel || void 0,
@@ -1210,18 +1299,21 @@ const SheetPanel = (props) => {
1210
1299
  ...hoverProps,
1211
1300
  ...inactivePanelProps,
1212
1301
  ...ariaProps,
1213
- children: [sideHandle, /* @__PURE__ */ jsxs("div", {
1214
- className: "relative flex min-h-0 flex-1 flex-col overflow-hidden rounded-[inherit]",
1215
- children: [bottomHandle, /* @__PURE__ */ jsx(PanelInnerContent, {
1216
- Content,
1217
- data: item.data,
1218
- headerClassName: classNames.header || void 0,
1219
- headerProps,
1220
- isComposable,
1221
- renderHeader,
1222
- shouldRender
1223
- })]
1224
- })]
1302
+ children: [
1303
+ sideHandle,
1304
+ bottomHandleOutside ? bottomHandle : null,
1305
+ /* @__PURE__ */ jsxs("div", {
1306
+ className: "relative flex min-h-0 flex-1 flex-col overflow-hidden rounded-[inherit]",
1307
+ children: [bottomHandleOutside ? null : bottomHandle, /* @__PURE__ */ jsx(PanelInnerContent, {
1308
+ Content,
1309
+ data: item.data,
1310
+ headerProps,
1311
+ isComposable,
1312
+ renderHeader,
1313
+ shouldRender
1314
+ })]
1315
+ })
1316
+ ]
1225
1317
  }, item.id);
1226
1318
  return /* @__PURE__ */ jsx(SheetPanelContext.Provider, {
1227
1319
  value: panelContext,
@@ -1308,6 +1400,13 @@ const useSnapState = (config, isOpen, side, stack) => {
1308
1400
  snapHeights
1309
1401
  };
1310
1402
  };
1403
+ const usePanelKeyboardInset = (config, isOpen, side) => {
1404
+ const panelWrapperRef = useRef(null);
1405
+ return {
1406
+ keyboardInset: useKeyboardInset(isOpen && side === "bottom" && config.repositionInputs, panelWrapperRef),
1407
+ panelWrapperRef
1408
+ };
1409
+ };
1311
1410
  const useCloseControls = (rawClose, rawPop) => {
1312
1411
  const closeReasonRef = useRef("programmatic");
1313
1412
  const closeWith = (reason) => {
@@ -1422,6 +1521,7 @@ const SheetRenderer = ({ store, config, sheets, componentMap, classNames: classN
1422
1521
  const classNames = resolveClassNames(classNamesProp);
1423
1522
  const { activeSnapIndex, handleSnap, snapHeights } = useSnapState(config, isOpen, side, stack);
1424
1523
  const { close, closeReasonRef, closeWith, pop, popWith } = useCloseControls(rawClose, rawPop);
1524
+ const { panelWrapperRef, keyboardInset } = usePanelKeyboardInset(config, isOpen, side);
1425
1525
  useBodyScale(config, isOpen, prefersReducedMotion);
1426
1526
  useFocusRestore(isOpen);
1427
1527
  useDismissalEffects({
@@ -1467,6 +1567,7 @@ const SheetRenderer = ({ store, config, sheets, componentMap, classNames: classN
1467
1567
  }), /* @__PURE__ */ jsx(RemoveScroll, {
1468
1568
  enabled: shouldLockScroll,
1469
1569
  forwardProps: true,
1570
+ ref: panelWrapperRef,
1470
1571
  children: /* @__PURE__ */ jsx("div", {
1471
1572
  className: "pointer-events-none fixed inset-0 overflow-hidden",
1472
1573
  style: { zIndex: config.zIndex + 1 },
@@ -1488,6 +1589,7 @@ const SheetRenderer = ({ store, config, sheets, componentMap, classNames: classN
1488
1589
  isNested,
1489
1590
  isTop,
1490
1591
  item,
1592
+ keyboardInset,
1491
1593
  layout,
1492
1594
  onSnap: handleSnap,
1493
1595
  pop,
@@ -1918,7 +2020,7 @@ const SheetClose = ({ asChild, className, style, children }) => {
1918
2020
  const isAsChild = asChild === true;
1919
2021
  return /* @__PURE__ */ jsx(isAsChild ? Slot : "button", {
1920
2022
  "aria-label": children === void 0 || children === null ? "Close" : void 0,
1921
- className: joinClassNames(isAsChild ? void 0 : "flex min-h-11 min-w-11 shrink-0 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-60 transition-opacity duration-150 hover:opacity-100", className),
2023
+ className: joinClassNames(isAsChild ? void 0 : "absolute right-2 top-2 z-20 flex min-h-11 min-w-11 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-50 transition-opacity duration-150 hover:opacity-100", className),
1922
2024
  onClick: close,
1923
2025
  style,
1924
2026
  type: isAsChild ? void 0 : "button",
@@ -1950,7 +2052,8 @@ const SheetFooter = ({ asChild, className, style, children }) => {
1950
2052
  //#endregion
1951
2053
  //#region src/SheetParts/sheet-handle.tsx
1952
2054
  const SheetHandle = ({ asChild, className, style, children }) => {
1953
- const { close, back, isNested } = useSheetPanel();
2055
+ const { close, back, isNested, side } = useSheetPanel();
2056
+ if (side !== "bottom") return null;
1954
2057
  const dismiss = isNested ? back : close;
1955
2058
  const isAsChild = asChild === true;
1956
2059
  return /* @__PURE__ */ jsx(isAsChild ? Slot : "button", {
@@ -1971,7 +2074,7 @@ const SheetHandle = ({ asChild, className, style, children }) => {
1971
2074
  const SheetHeader = ({ asChild, className, style, children }) => {
1972
2075
  const isAsChild = asChild === true;
1973
2076
  return /* @__PURE__ */ jsx(isAsChild ? Slot : "header", {
1974
- className: joinClassNames(isAsChild ? "shrink-0" : "flex h-14 shrink-0 items-center justify-between border-b px-6", className),
2077
+ className: joinClassNames(isAsChild ? "shrink-0" : "flex shrink-0 items-center justify-between gap-3", className),
1975
2078
  style,
1976
2079
  children
1977
2080
  });