@@ -47,6 +47,8 @@ import {
4747 DEFAULT_GRABBER_HEIGHT ,
4848 DEFAULT_GRABBER_TOP_MARGIN ,
4949 DEFAULT_GRABBER_WIDTH ,
50+ DEFAULT_FORM_SHEET_HEIGHT_RATIO ,
51+ DEFAULT_FORM_SHEET_WIDTH ,
5052 DEFAULT_MAX_WIDTH ,
5153} from './web/constants' ;
5254
@@ -75,6 +77,7 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
7577 footerStyle,
7678 scrollable = false ,
7779 scrollableOptions,
80+ pageSizing = true ,
7881 detached = false ,
7982 detachedOffset = DEFAULT_DETACHED_OFFSET ,
8083 elevation = 4 ,
@@ -619,20 +622,42 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
619622 [ ]
620623 ) ;
621624
625+ // Form-sheet style (iOS pageSizing=false): centered floating card with a
626+ // default width and a height capped to a fraction of the window. We reuse
627+ // the existing detached mechanic so drag/snap math stays correct — the
628+ // wrapper is bottom-attached with a computed offset that centers it
629+ // vertically.
630+ const isFormSheet =
631+ isLandscapeOrTablet && maxContentWidth == null && ! pageSizing ;
632+
633+ const effectiveMaxContentHeight =
634+ maxContentHeight ??
635+ ( isFormSheet ? windowHeight * DEFAULT_FORM_SHEET_HEIGHT_RATIO : undefined ) ;
636+
637+ const effectiveDetached = isFormSheet || detached ;
638+
639+ const effectiveDetachedOffset = isFormSheet
640+ ? Math . max ( 0 , ( windowHeight - ( effectiveMaxContentHeight ?? 0 ) ) / 2 )
641+ : detachedOffset ;
642+
622643 // The wrapper holds all horizontal sizing/anchoring so its rounded-bottom
623644 // clip (when detached) aligns with the drawer's horizontal bounds on
624645 // desktop — otherwise its corners sit at the far viewport edges.
625- const wrapperStyle = useMemo < React . CSSProperties | undefined > (
626- ( ) =>
627- isLandscapeOrTablet
628- ? {
629- maxWidth : maxContentWidth ?? DEFAULT_MAX_WIDTH ,
630- marginLeft : anchor === 'left' ? anchorOffset : 'auto' ,
631- marginRight : anchor === 'right' ? anchorOffset : 'auto' ,
632- }
633- : undefined ,
634- [ isLandscapeOrTablet , maxContentWidth , anchor , anchorOffset ]
635- ) ;
646+ // Mirrors iOS setupSheetSizing: maxContentWidth wins (forces pageSizing
647+ // off). pageSizing on → constrain to readable width (page-sheet);
648+ // pageSizing off with no maxContentWidth → form-sheet width.
649+ const wrapperStyle = useMemo < React . CSSProperties | undefined > ( ( ) => {
650+ if ( ! isLandscapeOrTablet ) return undefined ;
651+ const maxWidth = isFormSheet
652+ ? DEFAULT_FORM_SHEET_WIDTH
653+ : ( maxContentWidth ?? ( pageSizing ? DEFAULT_MAX_WIDTH : undefined ) ) ;
654+ if ( maxWidth == null ) return undefined ;
655+ return {
656+ maxWidth,
657+ marginLeft : isFormSheet || anchor !== 'left' ? 'auto' : anchorOffset ,
658+ marginRight : isFormSheet || anchor !== 'right' ? 'auto' : anchorOffset ,
659+ } ;
660+ } , [ isLandscapeOrTablet , isFormSheet , maxContentWidth , pageSizing , anchor , anchorOffset ] ) ;
636661
637662 const handleStyle = useMemo < React . CSSProperties > (
638663 ( ) => ( {
@@ -659,10 +684,10 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
659684 repositionInputs = { false }
660685 modal = { dimmed }
661686 nested = { isNested }
662- detached = { detached }
663- detachedOffset = { detachedOffset }
687+ detached = { effectiveDetached }
688+ detachedOffset = { effectiveDetachedOffset }
664689 detachedRadius = { effectiveCornerRadius }
665- maxContentHeight = { maxContentHeight }
690+ maxContentHeight = { effectiveMaxContentHeight }
666691 initialAnimated = { initialDetentAnimated }
667692 detachedWrapperStyle = { wrapperStyle }
668693 activeSnapPoint = { activeSnapPoint }
0 commit comments