+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 | + + + + + + + + + + + + + + +6x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +6x + + + +68x +68x + + + + +68x +68x +68x + +68x + + + + + + +68x + +273x +206x + + +68x +69x + + +68x + + + + +68x +4x +4x + + +68x +77x +67x +67x + +67x + + +6x +31x + + +6x +1x + + + +68x + + + + + + + + +132x + + + +68x +68x + + + + + + + +68x +68x +68x +68x + + + +68x +68x + + + + + + + + + +134x +66x + + + +66x + + +66x + + + +66x +66x +1x + + +66x +66x + + + + +66x + +66x + + + + + + + + + + + + +66x + +66x +66x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | import React from 'react';
+import {
+ Dimensions,
+ type LayoutChangeEvent,
+ Platform,
+ StatusBar,
+ type StyleProp,
+ View,
+ type ViewStyle,
+} from 'react-native';
+
+import She, { Commands } from './SheetViewNativeComponent';
+import SheetModule from './NativeSheet';
+import { Portal } from '@gorhom/portal';
+
+const _FittedSheet = She;
+
+export interface FittedSheetParams {
+ applyMaxHeightToMinHeight?: boolean;
+ dismissable?: boolean;
+ maxPortraitWidth?: number;
+ maxLandscapeWidth?: number;
+ maxHeight?: number;
+ minHeight?: number;
+ topLeftRightCornerRadius?: number;
+ backgroundColor?: string;
+ /**
+ * Android only
+ */
+ isSystemUILight?: boolean;
+}
+
+type FittedSheetChildren =
+ | ((data: any) => React.ReactElement)
+ | React.ReactElement
+ | React.ReactElement[];
+
+export interface SheetProps {
+ params?: FittedSheetParams;
+ onSheetDismiss?: (passThroughParam?: any) => void;
+ children?: FittedSheetChildren;
+ rootViewStyle?: StyleProp<Omit<ViewStyle, 'flex' | 'flexGrow' | 'position'>>;
+}
+
+interface State {
+ show: boolean;
+ data: any | null;
+ height?: number;
+ passScrollViewReactTag?: string;
+ isLandscape: boolean;
+}
+
+let index = 0;
+export class PrivateFittedSheet extends React.PureComponent<SheetProps, State> {
+ private cleanup?: () => void;
+ private onHidePassThroughParam?: any;
+ private sheetRef = React.createRef<any>();
+ private uniqueId = 0;
+
+ private dimensions: { width: number; height: number };
+
+ constructor(props: SheetProps) {
+ super(props);
+ this.uniqueId = ++index;
+ this.dimensions = this.viewportSize();
+
+ this.state = {
+ show: false,
+ data: null,
+ isLandscape: this.dimensions.width > this.dimensions.height,
+ };
+ }
+
+ private log = (key: string, message: any | undefined = undefined) => {
+ //if (true) return;
+ if (message) console.log(`${this.uniqueId} - FittedSheet.${key}`, message);
+ else console.log(`${this.uniqueId} - FittedSheet.${key}`);
+ };
+
+ show = (data?: any) => {
+ this.setState({ show: true, data });
+ };
+
+ onLayout = (e: LayoutChangeEvent) => {
+ this.log('onLayout', e.nativeEvent.layout.height);
+ this.setState({ height: e.nativeEvent.layout.height });
+ };
+
+ attachScrollViewToSheet = () => {
+ this.log(`attachScrollViewToSheet`);
+ this.setState({ passScrollViewReactTag: Date.now().toString() });
+ };
+
+ hide = (passThroughParam?: any) => {
+ if (!this.state.show) return;
+ this.onHidePassThroughParam = passThroughParam;
+ this.log('hide', Commands.dismissSheet);
+ //@ts-ignore
+ Commands.dismissSheet(this.sheetRef.current);
+ };
+
+ static dismissAll = () => {
+ SheetModule.dismissAll();
+ };
+
+ static dismissPresented = () => {
+ SheetModule.dismissPresented();
+ };
+
+ // @ts-ignore
+ private onDismiss = () => {
+ this.log('onDismiss');
+ this.setState({ show: false, height: undefined });
+ const passValue = this.onHidePassThroughParam;
+ this.onHidePassThroughParam = undefined;
+ this.props.onSheetDismiss?.(passValue);
+ };
+
+ private insets(): { top: number; bottom: number } {
+ return (SheetModule.getConstants().insets as any) ?? { top: 0, bottom: 0 };
+ }
+
+ componentDidMount() {
+ this.log('componentDidMount');
+ this.cleanup = Dimensions.addEventListener('change', () => {
+ this.dimensions = this.viewportSize();
+ const isLandscape = this.dimensions.width > this.dimensions.height;
+ this.setState({ isLandscape });
+ }).remove;
+ }
+
+ componentWillUnmount() {
+ this.log('componentWillUnmount');
+ this.hide();
+ this.cleanup?.();
+ this.cleanup = undefined;
+ }
+
+ private viewportSize(): { width: number; height: number } {
+ Eif (Platform.OS === 'ios') {
+ return SheetModule.viewportSize();
+ }
+ return Dimensions.get('window');
+ }
+
+ private _shouldSetResponder(): boolean {
+ return true;
+ }
+
+ render() {
+ if (!this.state.show) return null;
+ let maxHeight = Math.min(
+ this.props.params?.maxHeight ?? Number.MAX_VALUE,
+ this.dimensions.height - this.insets().top - this.insets().bottom
+ );
+ const paramsMaxWidth = this.state.isLandscape
+ ? this.props.params?.maxLandscapeWidth
+ : this.props.params?.maxPortraitWidth;
+ let maxWidth = Math.min(
+ paramsMaxWidth ?? Number.MAX_VALUE,
+ this.dimensions.width
+ );
+ let minHeight = this.props.params?.minHeight;
+ if (this.props.params?.applyMaxHeightToMinHeight) {
+ minHeight = maxHeight;
+ }
+
+ let nativeHeight = this.state.height;
+ Iif (nativeHeight) {
+ nativeHeight = Math.max(nativeHeight ?? 0, minHeight ?? 0);
+ nativeHeight = Math.min(nativeHeight, maxHeight);
+ }
+
+ Eif (__DEV__) {
+ //@ts-ignore
+ const logJson = JSON.stringify(
+ {
+ maxHeight,
+ maxWidth,
+ nativeHeight,
+ isLandscape: this.state.isLandscape,
+ h: Dimensions.get('window').height,
+ sb: StatusBar.currentHeight,
+ dimensions: this.dimensions,
+ },
+ undefined,
+ 2
+ );
+ this.log('render');
+ }
+ const background = this.props?.params?.backgroundColor;
+ return (
+ <Portal hostName={'SheetHost'}>
+ <_FittedSheet
+ key={this.uniqueId.toString()}
+ onSheetDismiss={this.onDismiss}
+ uniqueId={this.uniqueId.toString()}
+ ref={this.sheetRef}
+ onStartShouldSetResponder={this._shouldSetResponder}
+ style={{ width: maxWidth, position: 'absolute' }}
+ dismissable={this.props.params?.dismissable ?? true}
+ maxWidth={maxWidth}
+ maxHeight={maxHeight}
+ sheetBackgroundColor={background}
+ minHeight={minHeight}
+ topLeftRightCornerRadius={
+ this.props.params?.topLeftRightCornerRadius ?? 20
+ }
+ isSystemUILight={this.props.params?.isSystemUILight ?? true}
+ calculatedHeight={nativeHeight}
+ passScrollViewReactTag={this.state.passScrollViewReactTag}
+ >
+ <View
+ nativeID={'fitted-sheet-root-view'}
+ onLayout={this.onLayout}
+ style={[
+ {
+ width: maxWidth,
+ maxHeight,
+ backgroundColor: background,
+ },
+ this.props.rootViewStyle,
+ ]}
+ >
+ {this.props.children &&
+ typeof this.props.children === 'function' &&
+ this.props.children(this.state.data)}
+ {this.props.children &&
+ typeof this.props.children !== 'function' &&
+ this.props.children}
+ </View>
+ </_FittedSheet>
+ </Portal>
+ );
+ }
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 | + + + + + + + + + + + +33x +33x + + + +19x +19x + + + +3x +3x +3x +2x + + +2x + +1x +1x + + + + + + + +6x +6x + + + + + + +6x +34x +17x +17x + + + + +17x +17x +17x + + +17x +17x +17x + + + + + + + +6x + + + + + + + + + + + + + + + + + + +6x + + + + +17x +17x +17x + + + + +17x + +34x + + + + + + + + + + + + + +3x + +3x + + + + +3x + + + +6x +40x +40x + + + + + + +40x +20x + +20x +52x +52x + + +20x +33x + + + +33x +33x + + + +33x + + +20x +19x + + + +19x +19x +19x + + + +19x + + +20x +20x + + + + +20x +20x + + +20x + + + + +20x + + + + +20x + +20x + + + +40x +25x +25x + + +15x +15x + + +20x + + + + + | import React, { memo, useEffect, useState } from 'react';
+import { FittedSheet, type FittedSheetRef } from './index';
+import type { SheetProps } from './FittedSheet';
+
+type GlobalProps = {
+ name: string;
+ onDismiss?: () => void;
+ sheetProps?: SheetProps;
+ children: React.ReactElement | React.ReactElement[];
+};
+
+export function presentGlobalFittedSheet(params: GlobalProps): void {
+ console.log('[presentGlobalFittedSheet] called', { name: params.name });
+ presentGlobalSheet?.(params);
+}
+
+export function dismissGlobalFittedSheet(name: string): void {
+ console.log('[dismissGlobalFittedSheet] called', { name });
+ dismissGlobalSheet?.(name);
+}
+
+export function attachScrollViewToGlobalFittedSheet(name: string): boolean {
+ console.log('[attachScrollViewToGlobalFittedSheet] called', { name });
+ const ref = sheetRefs.get(name);
+ if (!ref) {
+ console.warn(
+ `[attachScrollViewToGlobalFittedSheet] Sheet "${name}" not found`
+ );
+ return false;
+ }
+ ref.attachScrollViewToSheet();
+ return true;
+}
+
+interface Props {
+ props: SheetProps | undefined;
+}
+
+// Global state at module level
+const sheetRefs = new Map<string, FittedSheetRef>();
+const sheets = new Map<string, GlobalProps>();
+let forceUpdate: () => void;
+
+let presentGlobalSheet: (params: GlobalProps) => void;
+let dismissGlobalSheet: (name: string) => void;
+
+// Global stable callbacks
+const handleSheetRef = (name: string, ref: FittedSheetRef | null) => {
+ if (ref) {
+ const isNewSheet = !sheetRefs.has(name);
+ console.log('[GlobalSheetView.handleSheetRef] mounting', {
+ name,
+ isNewSheet,
+ totalRefs: sheetRefs.size,
+ });
+ sheetRefs.set(name, ref);
+ Eif (isNewSheet) {
+ ref.show();
+ }
+ } else {
+ const hadRef = sheetRefs.has(name);
+ sheetRefs.delete(name);
+ console.log('[GlobalSheetView.handleSheetRef] unmounting', {
+ name,
+ hadRef,
+ totalRefs: sheetRefs.size,
+ });
+ }
+};
+
+const handleSheetDismiss = (name: string) => {
+ console.log('[GlobalSheetView.handleSheetDismiss] start', {
+ name,
+ totalSheets: sheets.size,
+ totalRefs: sheetRefs.size,
+ });
+ const dataToRender = sheets.get(name);
+ dataToRender?.onDismiss?.();
+ sheets.delete(name);
+ // Note: Don't delete from sheetRefs here - let React unmounting clean it up via handleSheetRef
+ console.log('[GlobalSheetView.handleSheetDismiss] after cleanup', {
+ name,
+ totalSheets: sheets.size,
+ totalRefs: sheetRefs.size,
+ });
+ forceUpdate?.();
+};
+
+// Memoized sheet component to avoid unnecessary re-renders
+const GlobalSheetItem = memo<{
+ name: string;
+ defaultProps: SheetProps | undefined;
+}>(
+ ({ name, defaultProps }) => {
+ console.log('[GlobalSheetItem] render', { name });
+ const dataToRender = sheets.get(name);
+ Iif (!dataToRender) {
+ console.log('[GlobalSheetItem] no data, returning null', { name });
+ return null;
+ }
+
+ return (
+ <FittedSheet
+ ref={(ref) => handleSheetRef(name, ref)}
+ name={name}
+ params={dataToRender.sheetProps?.params ?? defaultProps?.params}
+ rootViewStyle={
+ dataToRender.sheetProps?.rootViewStyle ?? defaultProps?.rootViewStyle
+ }
+ onSheetDismiss={() => handleSheetDismiss(name)}
+ children={dataToRender?.children}
+ />
+ );
+ },
+ (prevProps, nextProps) => {
+ // Custom comparison to log when memo blocks re-renders
+ const shouldSkip =
+ prevProps.name === nextProps.name &&
+ prevProps.defaultProps === nextProps.defaultProps;
+ console.log('[GlobalSheetItem] memo check', {
+ name: prevProps.name,
+ shouldSkip,
+ propsChanged: !shouldSkip,
+ });
+ return shouldSkip;
+ }
+);
+
+export const GlobalSheetView: React.FC<Props> = memo((props) => {
+ const [tick, setTick] = useState(0);
+ console.log('[GlobalSheetView] render', {
+ tick,
+ totalSheets: sheets.size,
+ totalRefs: sheetRefs.size,
+ sheetNames: Array.from(sheets.keys()),
+ });
+
+ useEffect(() => {
+ console.log('[GlobalSheetView] mounted - initializing global functions');
+
+ forceUpdate = () => {
+ console.log('[GlobalSheetView.forceUpdate] triggering re-render');
+ setTick((t) => t + 1);
+ };
+
+ presentGlobalSheet = (params) => {
+ console.log('[GlobalSheetView.presentGlobalSheet]', {
+ name: params.name,
+ beforeAdd: sheets.size,
+ });
+ sheets.set(params.name, params);
+ console.log('[GlobalSheetView.presentGlobalSheet] after add', {
+ name: params.name,
+ afterAdd: sheets.size,
+ });
+ forceUpdate();
+ };
+
+ dismissGlobalSheet = (name) => {
+ console.log('[GlobalSheetView.dismissGlobalSheet]', {
+ name,
+ beforeDelete: sheets.size,
+ });
+ sheets.delete(name);
+ sheetRefs.delete(name); // Also clean up ref to prevent memory leak
+ console.log('[GlobalSheetView.dismissGlobalSheet] after delete', {
+ name,
+ afterDelete: sheets.size,
+ });
+ forceUpdate();
+ };
+
+ return () => {
+ console.log('[GlobalSheetView] unmounting - cleaning up', {
+ sheetsToClean: sheets.size,
+ refsToClean: sheetRefs.size,
+ });
+ // Clean up all sheets and refs to prevent memory leaks
+ sheets.clear();
+ sheetRefs.clear();
+
+ // Reset functions to no-op
+ presentGlobalSheet = () => {
+ console.warn(
+ '[GlobalSheetView] presentGlobalSheet called after unmount!'
+ );
+ };
+ dismissGlobalSheet = () => {
+ console.warn(
+ '[GlobalSheetView] dismissGlobalSheet called after unmount!'
+ );
+ };
+ forceUpdate = () => {};
+
+ console.log('[GlobalSheetView] cleanup complete');
+ };
+ }, []);
+
+ if (sheets.size === 0) {
+ console.log('[GlobalSheetView] rendering null (no sheets)');
+ return null;
+ }
+
+ console.log('[GlobalSheetView] rendering sheets', Array.from(sheets.keys()));
+ return (
+ <>
+ {Array.from(sheets.keys()).map((name) => (
+ <GlobalSheetItem key={name} name={name} defaultProps={props.props} />
+ ))}
+ </>
+ );
+});
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 | + + + + + + + + + + + + + + + + + + | import { type TurboModule, TurboModuleRegistry } from 'react-native';
+import type {
+ Double,
+ UnsafeObject,
+ //@ts-ignore
+} from 'react-native/Libraries/Types/CodegenTypes';
+
+export interface Spec extends TurboModule {
+ getConstants: () => {
+ insets: UnsafeObject;
+ };
+
+ viewportSize(): { width: Double; height: Double };
+ dismissAll(): void;
+ dismissPresented(): void;
+}
+
+export default TurboModuleRegistry.getEnforcing<Spec>('Sheet');
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 | + + + + + + + + + + +6x + +24x +22x +22x + + + +3x +2x +2x + + + +3x +2x +2x + + + +31x +31x + + + +1x +1x + + + + + + + +6x + +68x + +68x + +68x +68x +40x +1x +40x + +68x +68x +40x + + + +68x + + + | import React, {
+ forwardRef,
+ memo,
+ useEffect,
+ useImperativeHandle,
+ useRef,
+} from 'react';
+import { PrivateFittedSheet, type SheetProps } from './FittedSheet';
+
+const handles: {
+ [name: string]: React.RefObject<PrivateFittedSheet | null>;
+} = {};
+export function presentFittedSheet(name: string, data?: any): boolean {
+ if (!handles[name]) return false;
+ handles[name]?.current?.show(data);
+ return true;
+}
+
+export function attachScrollViewToFittedSheet(name: string): boolean {
+ if (!handles[name]) return false;
+ handles[name]?.current?.attachScrollViewToSheet();
+ return true;
+}
+
+export function dismissFittedSheet(name: string): boolean {
+ if (!handles[name]) return false;
+ handles[name]?.current?.hide();
+ return true;
+}
+
+export function dismissFittedSheetsAll(): boolean {
+ PrivateFittedSheet.dismissAll();
+ return true;
+}
+
+export function dismissFittedPresented(): boolean {
+ PrivateFittedSheet.dismissPresented();
+ return true;
+}
+
+type Props = SheetProps & { name?: string };
+export type FittedSheetRef = Pick<
+ PrivateFittedSheet,
+ 'show' | 'hide' | 'attachScrollViewToSheet'
+>;
+export const PublicSheetView = memo(
+ forwardRef<FittedSheetRef, Props>((props, ref) => {
+ const sheetRef = useRef<PrivateFittedSheet>(null);
+
+ useImperativeHandle(ref, () => sheetRef.current!, []);
+
+ useEffect(() => {
+ if (props.name) {
+ if (handles[props.name])
+ console.warn(`Sheet with name ${props.name} exists`);
+ handles[props.name] = sheetRef;
+ }
+ return () => {
+ if (!props.name) return;
+ delete handles[props.name];
+ };
+ }, [props.name]);
+
+ return <PrivateFittedSheet ref={sheetRef} {...props} />;
+ })
+);
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | import {
+ type ColorValue,
+ type HostComponent,
+ type ViewProps,
+ codegenNativeComponent,
+ codegenNativeCommands,
+} from 'react-native';
+import type {
+ DirectEventHandler,
+ Double,
+ //@ts-ignore
+} from 'react-native/Libraries/Types/CodegenTypes';
+import React from 'react';
+
+interface NativeProps extends ViewProps {
+ dismissable: boolean;
+ maxWidth?: Double;
+ maxHeight?: Double;
+ minHeight?: Double;
+ calculatedHeight?: Double;
+ topLeftRightCornerRadius?: Double;
+ isSystemUILight: boolean;
+ passScrollViewReactTag?: string;
+ uniqueId: string;
+ sheetBackgroundColor?: ColorValue;
+ onSheetDismiss: DirectEventHandler<null>;
+}
+
+export default codegenNativeComponent<NativeProps>('SheetView');
+
+type MyNativeViewType = HostComponent<NativeProps>;
+export interface NativeCommands {
+ dismissSheet: (viewRef: React.ElementRef<MyNativeViewType>) => void;
+}
+
+export const Commands = codegenNativeCommands<NativeCommands>({
+ supportedCommands: ['dismissSheet'],
+});
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| FittedSheet.tsx | +
+
+ |
+ 79.1% | +53/67 | +76.92% | +30/39 | +75% | +12/16 | +78.12% | +50/64 | +
| GlobalSheetView.tsx | +
+
+ |
+ 85.89% | +67/78 | +87.5% | +14/16 | +75% | +15/20 | +85.71% | +66/77 | +
| NativeSheet.ts | +
+
+ |
+ 0% | +0/0 | +0% | +0/0 | +0% | +0/0 | +0% | +0/0 | +
| PublicSheetView.tsx | +
+
+ |
+ 100% | +31/31 | +100% | +12/12 | +100% | +9/9 | +100% | +26/26 | +
| SheetViewNativeComponent.ts | +
+
+ |
+ 0% | +0/1 | +100% | +0/0 | +100% | +0/0 | +0% | +0/1 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 | + + + + + + + + + + + + + + +6x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +6x + + + +68x +68x + + + + +68x +68x +68x + +68x + + + + + + +68x + +273x +206x + + +68x +69x + + +68x + + + + +68x +4x +4x + + +68x +77x +67x +67x + +67x + + +6x +31x + + +6x +1x + + + +68x + + + + + + + + +132x + + + +68x +68x + + + + + + + +68x +68x +68x +68x + + + +68x +68x + + + + + + + + + +134x +66x + + + +66x + + +66x + + + +66x +66x +1x + + +66x +66x + + + + +66x + +66x + + + + + + + + + + + + +66x + +66x +66x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | import React from 'react';
+import {
+ Dimensions,
+ type LayoutChangeEvent,
+ Platform,
+ StatusBar,
+ type StyleProp,
+ View,
+ type ViewStyle,
+} from 'react-native';
+
+import She, { Commands } from './SheetViewNativeComponent';
+import SheetModule from './NativeSheet';
+import { Portal } from '@gorhom/portal';
+
+const _FittedSheet = She;
+
+export interface FittedSheetParams {
+ applyMaxHeightToMinHeight?: boolean;
+ dismissable?: boolean;
+ maxPortraitWidth?: number;
+ maxLandscapeWidth?: number;
+ maxHeight?: number;
+ minHeight?: number;
+ topLeftRightCornerRadius?: number;
+ backgroundColor?: string;
+ /**
+ * Android only
+ */
+ isSystemUILight?: boolean;
+}
+
+type FittedSheetChildren =
+ | ((data: any) => React.ReactElement)
+ | React.ReactElement
+ | React.ReactElement[];
+
+export interface SheetProps {
+ params?: FittedSheetParams;
+ onSheetDismiss?: (passThroughParam?: any) => void;
+ children?: FittedSheetChildren;
+ rootViewStyle?: StyleProp<Omit<ViewStyle, 'flex' | 'flexGrow' | 'position'>>;
+}
+
+interface State {
+ show: boolean;
+ data: any | null;
+ height?: number;
+ passScrollViewReactTag?: string;
+ isLandscape: boolean;
+}
+
+let index = 0;
+export class PrivateFittedSheet extends React.PureComponent<SheetProps, State> {
+ private cleanup?: () => void;
+ private onHidePassThroughParam?: any;
+ private sheetRef = React.createRef<any>();
+ private uniqueId = 0;
+
+ private dimensions: { width: number; height: number };
+
+ constructor(props: SheetProps) {
+ super(props);
+ this.uniqueId = ++index;
+ this.dimensions = this.viewportSize();
+
+ this.state = {
+ show: false,
+ data: null,
+ isLandscape: this.dimensions.width > this.dimensions.height,
+ };
+ }
+
+ private log = (key: string, message: any | undefined = undefined) => {
+ //if (true) return;
+ if (message) console.log(`${this.uniqueId} - FittedSheet.${key}`, message);
+ else console.log(`${this.uniqueId} - FittedSheet.${key}`);
+ };
+
+ show = (data?: any) => {
+ this.setState({ show: true, data });
+ };
+
+ onLayout = (e: LayoutChangeEvent) => {
+ this.log('onLayout', e.nativeEvent.layout.height);
+ this.setState({ height: e.nativeEvent.layout.height });
+ };
+
+ attachScrollViewToSheet = () => {
+ this.log(`attachScrollViewToSheet`);
+ this.setState({ passScrollViewReactTag: Date.now().toString() });
+ };
+
+ hide = (passThroughParam?: any) => {
+ if (!this.state.show) return;
+ this.onHidePassThroughParam = passThroughParam;
+ this.log('hide', Commands.dismissSheet);
+ //@ts-ignore
+ Commands.dismissSheet(this.sheetRef.current);
+ };
+
+ static dismissAll = () => {
+ SheetModule.dismissAll();
+ };
+
+ static dismissPresented = () => {
+ SheetModule.dismissPresented();
+ };
+
+ // @ts-ignore
+ private onDismiss = () => {
+ this.log('onDismiss');
+ this.setState({ show: false, height: undefined });
+ const passValue = this.onHidePassThroughParam;
+ this.onHidePassThroughParam = undefined;
+ this.props.onSheetDismiss?.(passValue);
+ };
+
+ private insets(): { top: number; bottom: number } {
+ return (SheetModule.getConstants().insets as any) ?? { top: 0, bottom: 0 };
+ }
+
+ componentDidMount() {
+ this.log('componentDidMount');
+ this.cleanup = Dimensions.addEventListener('change', () => {
+ this.dimensions = this.viewportSize();
+ const isLandscape = this.dimensions.width > this.dimensions.height;
+ this.setState({ isLandscape });
+ }).remove;
+ }
+
+ componentWillUnmount() {
+ this.log('componentWillUnmount');
+ this.hide();
+ this.cleanup?.();
+ this.cleanup = undefined;
+ }
+
+ private viewportSize(): { width: number; height: number } {
+ Eif (Platform.OS === 'ios') {
+ return SheetModule.viewportSize();
+ }
+ return Dimensions.get('window');
+ }
+
+ private _shouldSetResponder(): boolean {
+ return true;
+ }
+
+ render() {
+ if (!this.state.show) return null;
+ let maxHeight = Math.min(
+ this.props.params?.maxHeight ?? Number.MAX_VALUE,
+ this.dimensions.height - this.insets().top - this.insets().bottom
+ );
+ const paramsMaxWidth = this.state.isLandscape
+ ? this.props.params?.maxLandscapeWidth
+ : this.props.params?.maxPortraitWidth;
+ let maxWidth = Math.min(
+ paramsMaxWidth ?? Number.MAX_VALUE,
+ this.dimensions.width
+ );
+ let minHeight = this.props.params?.minHeight;
+ if (this.props.params?.applyMaxHeightToMinHeight) {
+ minHeight = maxHeight;
+ }
+
+ let nativeHeight = this.state.height;
+ Iif (nativeHeight) {
+ nativeHeight = Math.max(nativeHeight ?? 0, minHeight ?? 0);
+ nativeHeight = Math.min(nativeHeight, maxHeight);
+ }
+
+ Eif (__DEV__) {
+ //@ts-ignore
+ const logJson = JSON.stringify(
+ {
+ maxHeight,
+ maxWidth,
+ nativeHeight,
+ isLandscape: this.state.isLandscape,
+ h: Dimensions.get('window').height,
+ sb: StatusBar.currentHeight,
+ dimensions: this.dimensions,
+ },
+ undefined,
+ 2
+ );
+ this.log('render');
+ }
+ const background = this.props?.params?.backgroundColor;
+ return (
+ <Portal hostName={'SheetHost'}>
+ <_FittedSheet
+ key={this.uniqueId.toString()}
+ onSheetDismiss={this.onDismiss}
+ uniqueId={this.uniqueId.toString()}
+ ref={this.sheetRef}
+ onStartShouldSetResponder={this._shouldSetResponder}
+ style={{ width: maxWidth, position: 'absolute' }}
+ dismissable={this.props.params?.dismissable ?? true}
+ maxWidth={maxWidth}
+ maxHeight={maxHeight}
+ sheetBackgroundColor={background}
+ minHeight={minHeight}
+ topLeftRightCornerRadius={
+ this.props.params?.topLeftRightCornerRadius ?? 20
+ }
+ isSystemUILight={this.props.params?.isSystemUILight ?? true}
+ calculatedHeight={nativeHeight}
+ passScrollViewReactTag={this.state.passScrollViewReactTag}
+ >
+ <View
+ nativeID={'fitted-sheet-root-view'}
+ onLayout={this.onLayout}
+ style={[
+ {
+ width: maxWidth,
+ maxHeight,
+ backgroundColor: background,
+ },
+ this.props.rootViewStyle,
+ ]}
+ >
+ {this.props.children &&
+ typeof this.props.children === 'function' &&
+ this.props.children(this.state.data)}
+ {this.props.children &&
+ typeof this.props.children !== 'function' &&
+ this.props.children}
+ </View>
+ </_FittedSheet>
+ </Portal>
+ );
+ }
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 | + + + + + + + + + + + +33x +33x + + + +19x +19x + + + +3x +3x +3x +2x + + +2x + +1x +1x + + + + + + + +6x +6x + + + + + + +6x +34x +17x +17x + + + + +17x +17x +17x + + +17x +17x +17x + + + + + + + +6x + + + + + + + + + + + + + + + + + + +6x + + + + +17x +17x +17x + + + + +17x + +34x + + + + + + + + + + + + + +3x + +3x + + + + +3x + + + +6x +40x +40x + + + + + + +40x +20x + +20x +52x +52x + + +20x +33x + + + +33x +33x + + + +33x + + +20x +19x + + + +19x +19x +19x + + + +19x + + +20x +20x + + + + +20x +20x + + +20x + + + + +20x + + + + +20x + +20x + + + +40x +25x +25x + + +15x +15x + + +20x + + + + + | import React, { memo, useEffect, useState } from 'react';
+import { FittedSheet, type FittedSheetRef } from './index';
+import type { SheetProps } from './FittedSheet';
+
+type GlobalProps = {
+ name: string;
+ onDismiss?: () => void;
+ sheetProps?: SheetProps;
+ children: React.ReactElement | React.ReactElement[];
+};
+
+export function presentGlobalFittedSheet(params: GlobalProps): void {
+ console.log('[presentGlobalFittedSheet] called', { name: params.name });
+ presentGlobalSheet?.(params);
+}
+
+export function dismissGlobalFittedSheet(name: string): void {
+ console.log('[dismissGlobalFittedSheet] called', { name });
+ dismissGlobalSheet?.(name);
+}
+
+export function attachScrollViewToGlobalFittedSheet(name: string): boolean {
+ console.log('[attachScrollViewToGlobalFittedSheet] called', { name });
+ const ref = sheetRefs.get(name);
+ if (!ref) {
+ console.warn(
+ `[attachScrollViewToGlobalFittedSheet] Sheet "${name}" not found`
+ );
+ return false;
+ }
+ ref.attachScrollViewToSheet();
+ return true;
+}
+
+interface Props {
+ props: SheetProps | undefined;
+}
+
+// Global state at module level
+const sheetRefs = new Map<string, FittedSheetRef>();
+const sheets = new Map<string, GlobalProps>();
+let forceUpdate: () => void;
+
+let presentGlobalSheet: (params: GlobalProps) => void;
+let dismissGlobalSheet: (name: string) => void;
+
+// Global stable callbacks
+const handleSheetRef = (name: string, ref: FittedSheetRef | null) => {
+ if (ref) {
+ const isNewSheet = !sheetRefs.has(name);
+ console.log('[GlobalSheetView.handleSheetRef] mounting', {
+ name,
+ isNewSheet,
+ totalRefs: sheetRefs.size,
+ });
+ sheetRefs.set(name, ref);
+ Eif (isNewSheet) {
+ ref.show();
+ }
+ } else {
+ const hadRef = sheetRefs.has(name);
+ sheetRefs.delete(name);
+ console.log('[GlobalSheetView.handleSheetRef] unmounting', {
+ name,
+ hadRef,
+ totalRefs: sheetRefs.size,
+ });
+ }
+};
+
+const handleSheetDismiss = (name: string) => {
+ console.log('[GlobalSheetView.handleSheetDismiss] start', {
+ name,
+ totalSheets: sheets.size,
+ totalRefs: sheetRefs.size,
+ });
+ const dataToRender = sheets.get(name);
+ dataToRender?.onDismiss?.();
+ sheets.delete(name);
+ // Note: Don't delete from sheetRefs here - let React unmounting clean it up via handleSheetRef
+ console.log('[GlobalSheetView.handleSheetDismiss] after cleanup', {
+ name,
+ totalSheets: sheets.size,
+ totalRefs: sheetRefs.size,
+ });
+ forceUpdate?.();
+};
+
+// Memoized sheet component to avoid unnecessary re-renders
+const GlobalSheetItem = memo<{
+ name: string;
+ defaultProps: SheetProps | undefined;
+}>(
+ ({ name, defaultProps }) => {
+ console.log('[GlobalSheetItem] render', { name });
+ const dataToRender = sheets.get(name);
+ Iif (!dataToRender) {
+ console.log('[GlobalSheetItem] no data, returning null', { name });
+ return null;
+ }
+
+ return (
+ <FittedSheet
+ ref={(ref) => handleSheetRef(name, ref)}
+ name={name}
+ params={dataToRender.sheetProps?.params ?? defaultProps?.params}
+ rootViewStyle={
+ dataToRender.sheetProps?.rootViewStyle ?? defaultProps?.rootViewStyle
+ }
+ onSheetDismiss={() => handleSheetDismiss(name)}
+ children={dataToRender?.children}
+ />
+ );
+ },
+ (prevProps, nextProps) => {
+ // Custom comparison to log when memo blocks re-renders
+ const shouldSkip =
+ prevProps.name === nextProps.name &&
+ prevProps.defaultProps === nextProps.defaultProps;
+ console.log('[GlobalSheetItem] memo check', {
+ name: prevProps.name,
+ shouldSkip,
+ propsChanged: !shouldSkip,
+ });
+ return shouldSkip;
+ }
+);
+
+export const GlobalSheetView: React.FC<Props> = memo((props) => {
+ const [tick, setTick] = useState(0);
+ console.log('[GlobalSheetView] render', {
+ tick,
+ totalSheets: sheets.size,
+ totalRefs: sheetRefs.size,
+ sheetNames: Array.from(sheets.keys()),
+ });
+
+ useEffect(() => {
+ console.log('[GlobalSheetView] mounted - initializing global functions');
+
+ forceUpdate = () => {
+ console.log('[GlobalSheetView.forceUpdate] triggering re-render');
+ setTick((t) => t + 1);
+ };
+
+ presentGlobalSheet = (params) => {
+ console.log('[GlobalSheetView.presentGlobalSheet]', {
+ name: params.name,
+ beforeAdd: sheets.size,
+ });
+ sheets.set(params.name, params);
+ console.log('[GlobalSheetView.presentGlobalSheet] after add', {
+ name: params.name,
+ afterAdd: sheets.size,
+ });
+ forceUpdate();
+ };
+
+ dismissGlobalSheet = (name) => {
+ console.log('[GlobalSheetView.dismissGlobalSheet]', {
+ name,
+ beforeDelete: sheets.size,
+ });
+ sheets.delete(name);
+ sheetRefs.delete(name); // Also clean up ref to prevent memory leak
+ console.log('[GlobalSheetView.dismissGlobalSheet] after delete', {
+ name,
+ afterDelete: sheets.size,
+ });
+ forceUpdate();
+ };
+
+ return () => {
+ console.log('[GlobalSheetView] unmounting - cleaning up', {
+ sheetsToClean: sheets.size,
+ refsToClean: sheetRefs.size,
+ });
+ // Clean up all sheets and refs to prevent memory leaks
+ sheets.clear();
+ sheetRefs.clear();
+
+ // Reset functions to no-op
+ presentGlobalSheet = () => {
+ console.warn(
+ '[GlobalSheetView] presentGlobalSheet called after unmount!'
+ );
+ };
+ dismissGlobalSheet = () => {
+ console.warn(
+ '[GlobalSheetView] dismissGlobalSheet called after unmount!'
+ );
+ };
+ forceUpdate = () => {};
+
+ console.log('[GlobalSheetView] cleanup complete');
+ };
+ }, []);
+
+ if (sheets.size === 0) {
+ console.log('[GlobalSheetView] rendering null (no sheets)');
+ return null;
+ }
+
+ console.log('[GlobalSheetView] rendering sheets', Array.from(sheets.keys()));
+ return (
+ <>
+ {Array.from(sheets.keys()).map((name) => (
+ <GlobalSheetItem key={name} name={name} defaultProps={props.props} />
+ ))}
+ </>
+ );
+});
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 | + + + + + + + + + + + + + + + + + + | import { type TurboModule, TurboModuleRegistry } from 'react-native';
+import type {
+ Double,
+ UnsafeObject,
+ //@ts-ignore
+} from 'react-native/Libraries/Types/CodegenTypes';
+
+export interface Spec extends TurboModule {
+ getConstants: () => {
+ insets: UnsafeObject;
+ };
+
+ viewportSize(): { width: Double; height: Double };
+ dismissAll(): void;
+ dismissPresented(): void;
+}
+
+export default TurboModuleRegistry.getEnforcing<Spec>('Sheet');
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 | + + + + + + + + + + +6x + +24x +22x +22x + + + +3x +2x +2x + + + +3x +2x +2x + + + +31x +31x + + + +1x +1x + + + + + + + +6x + +68x + +68x + +68x +68x +40x +1x +40x + +68x +68x +40x + + + +68x + + + | import React, {
+ forwardRef,
+ memo,
+ useEffect,
+ useImperativeHandle,
+ useRef,
+} from 'react';
+import { PrivateFittedSheet, type SheetProps } from './FittedSheet';
+
+const handles: {
+ [name: string]: React.RefObject<PrivateFittedSheet | null>;
+} = {};
+export function presentFittedSheet(name: string, data?: any): boolean {
+ if (!handles[name]) return false;
+ handles[name]?.current?.show(data);
+ return true;
+}
+
+export function attachScrollViewToFittedSheet(name: string): boolean {
+ if (!handles[name]) return false;
+ handles[name]?.current?.attachScrollViewToSheet();
+ return true;
+}
+
+export function dismissFittedSheet(name: string): boolean {
+ if (!handles[name]) return false;
+ handles[name]?.current?.hide();
+ return true;
+}
+
+export function dismissFittedSheetsAll(): boolean {
+ PrivateFittedSheet.dismissAll();
+ return true;
+}
+
+export function dismissFittedPresented(): boolean {
+ PrivateFittedSheet.dismissPresented();
+ return true;
+}
+
+type Props = SheetProps & { name?: string };
+export type FittedSheetRef = Pick<
+ PrivateFittedSheet,
+ 'show' | 'hide' | 'attachScrollViewToSheet'
+>;
+export const PublicSheetView = memo(
+ forwardRef<FittedSheetRef, Props>((props, ref) => {
+ const sheetRef = useRef<PrivateFittedSheet>(null);
+
+ useImperativeHandle(ref, () => sheetRef.current!, []);
+
+ useEffect(() => {
+ if (props.name) {
+ if (handles[props.name])
+ console.warn(`Sheet with name ${props.name} exists`);
+ handles[props.name] = sheetRef;
+ }
+ return () => {
+ if (!props.name) return;
+ delete handles[props.name];
+ };
+ }, [props.name]);
+
+ return <PrivateFittedSheet ref={sheetRef} {...props} />;
+ })
+);
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | import {
+ type ColorValue,
+ type HostComponent,
+ type ViewProps,
+ codegenNativeComponent,
+ codegenNativeCommands,
+} from 'react-native';
+import type {
+ DirectEventHandler,
+ Double,
+ //@ts-ignore
+} from 'react-native/Libraries/Types/CodegenTypes';
+import React from 'react';
+
+interface NativeProps extends ViewProps {
+ dismissable: boolean;
+ maxWidth?: Double;
+ maxHeight?: Double;
+ minHeight?: Double;
+ calculatedHeight?: Double;
+ topLeftRightCornerRadius?: Double;
+ isSystemUILight: boolean;
+ passScrollViewReactTag?: string;
+ uniqueId: string;
+ sheetBackgroundColor?: ColorValue;
+ onSheetDismiss: DirectEventHandler<null>;
+}
+
+export default codegenNativeComponent<NativeProps>('SheetView');
+
+type MyNativeViewType = HostComponent<NativeProps>;
+export interface NativeCommands {
+ dismissSheet: (viewRef: React.ElementRef<MyNativeViewType>) => void;
+}
+
+export const Commands = codegenNativeCommands<NativeCommands>({
+ supportedCommands: ['dismissSheet'],
+});
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| FittedSheet.tsx | +
+
+ |
+ 79.1% | +53/67 | +76.92% | +30/39 | +75% | +12/16 | +78.12% | +50/64 | +
| GlobalSheetView.tsx | +
+
+ |
+ 85.89% | +67/78 | +87.5% | +14/16 | +75% | +15/20 | +85.71% | +66/77 | +
| NativeSheet.ts | +
+
+ |
+ 0% | +0/0 | +0% | +0/0 | +0% | +0/0 | +0% | +0/0 | +
| PublicSheetView.tsx | +
+
+ |
+ 100% | +31/31 | +100% | +12/12 | +100% | +9/9 | +100% | +26/26 | +
| SheetViewNativeComponent.ts | +
+
+ |
+ 0% | +0/1 | +100% | +0/0 | +100% | +0/0 | +0% | +0/1 | +