Skip to content

Commit 575070b

Browse files
committed
feat(web): support pageSizing prop
Mirrors iOS prefersPageSizing. When false on landscape/tablet, renders a centered floating form-sheet (default width 580, height capped to 90% of window) by reusing the detached mechanic with a computed centering offset.
1 parent a1d0639 commit 575070b

4 files changed

Lines changed: 43 additions & 15 deletions

File tree

example/shared/src/components/Header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const Header = ({ children, style, ...rest }: HeaderProps) => {
1616

1717
const styles = StyleSheet.create({
1818
container: {
19-
height: Platform.select({ ios: HEADER_HEIGHT, default: HEADER_HEIGHT + SPACING }),
19+
height: Platform.select({ android: HEADER_HEIGHT + SPACING, default: HEADER_HEIGHT }),
2020
paddingTop: Platform.select({ android: SPACING * 2 }),
2121
justifyContent: 'center',
2222
padding: SPACING,

example/shared/src/components/sheets/BasicSheet.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
6161
name="basic"
6262
ref={sheetRef}
6363
detached
64+
pageSizing={false}
6465
grabberOptions={{
6566
width: 60,
6667
}}

src/TrueSheet.web.tsx

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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}

src/web/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export const DEFAULT_CORNER_RADIUS = 16;
22
export const DEFAULT_MAX_WIDTH = 640;
3+
export const DEFAULT_FORM_SHEET_WIDTH = 580;
4+
export const DEFAULT_FORM_SHEET_HEIGHT_RATIO = 0.9;
35
export const COLOR_SURFACE_CONTAINER_LOW_LIGHT = '#F7F2FA';
46
export const COLOR_SURFACE_CONTAINER_LOW_DARK = '#1D1B20';
57
export const DEFAULT_ANCHOR_OFFSET = 16;

0 commit comments

Comments
 (0)