Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions native-views/react-native-native-overlay/docs/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ visible)`, `removePage(ownerKey)`.
dismissOnBackPress
dismissOnPanDown // sheet and toast only
anchor? // anchored only: a trigger ref or a window rect
contentSize? // native non-sheet only: content frame instead of the window size
onPresented onDismissRequest onDismissed(reason)
/>
```
Expand Down Expand Up @@ -432,6 +433,27 @@ the correction:
- Rects already in screen space (for example from native lists) need no
correction.

### Content size for windows about to rotate

Implemented; Android runtime-verified with OneKey's native chart fullscreen on
an API 36 emulator. iOS takes the same JS layout path but was not
runtime-verified. Non-sheet content is laid out by JS at the window size it
last saw (`useWindowDimensions`), and the Android content host clips the
content to that frame. JS hears about a rotation only after the window has
rotated, so until then the content keeps the previous orientation's frame and
is clipped to it.

- `contentSize?: { width, height }` (non-sheet presentations) replaces the
window size for the content frame. A caller that prepares content for the
window it is about to rotate into passes the rotated size, so the first
rotated frame shows all of it. Omitted, the content follows the window size
as before.
- The content stays anchored at the host origin. While the window has not
rotated yet, content larger than the window draws past its edges and takes
no touches there; the caller keeps it hidden until the window matches.
- Sheets ignore it: they size their content from the sheet geometry. Web
ignores it: the DOM overlay sizes itself with CSS.

### Web keyboard focus containment

The topmost active, unsuspended blocking Web entry MUST contain sequential
Expand Down Expand Up @@ -580,6 +602,9 @@ Automated (P0):
- page suspend, restore and removal;
- back resolution (global first, `block`, `pass`, focused host) and the
blocking top.
- `src/__tests__/contentSize.test.ts` covers the non-sheet content frame:
the window size by default, and a caller size before and after the window
size reaches JS.
- `src/__tests__/animation.test.ts` covers:
- the spring response in all damping regimes, overshoot clamping and settle
time;
Expand Down
11 changes: 10 additions & 1 deletion native-views/react-native-native-overlay/src/OverlayView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import NativeOverlay from './NativeOverlayNativeComponent';
import { OverlayLevelProvider } from './OverlayLevelContext';
import { isBlockingLevel } from './OverlayLevels';
import { resolveContentSize } from './contentSize';
import { resolveSheetWidth } from './sheetWidth';
import { useOverlayController } from './useOverlayController';

Expand Down Expand Up @@ -72,6 +73,7 @@
testID,
windowOffsetRequest,
onWindowOffset,
contentSize,
} = props;
// A plain sibling at the host's spot: its JS-measured origin shares the
// modal-relative space of the caller's measurements, while native reports
Expand Down Expand Up @@ -219,7 +221,7 @@
<View
collapsable={false}
onLayout={onSheetLayout}
style={{

Check warning on line 224 in native-views/react-native-native-overlay/src/OverlayView.tsx

View workflow job for this annotation

GitHub Actions / native-overlay-package

Inline style: { flexShrink: 0 }
width: sheetContainerWidth,
paddingHorizontal: sheetInset,
height: sheet?.height,
Expand Down Expand Up @@ -284,7 +286,14 @@
<View
collapsable={false}
pointerEvents="box-none"
style={{ width, height, flexShrink: 0 }}
style={{

Check warning on line 289 in native-views/react-native-native-overlay/src/OverlayView.tsx

View workflow job for this annotation

GitHub Actions / native-overlay-package

Inline style: { flexShrink: 0 }
...resolveContentSize({
windowWidth: width,
windowHeight: height,
contentSize,
}),
flexShrink: 0,
}}
>
<OverlayLevelProvider value={level}>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ export interface IOverlayViewProps {
*/
windowOffsetRequest?: number;
onWindowOffset?: (offset: { x: number; y: number }) => void;
/**
* Native only, non-sheet presentations. Lays the content out at this size
* instead of the window size JS last saw. The content host clips the
* content to that frame, and JS hears about a rotation only after the
* window has rotated: content prepared for the window it is about to rotate
* into passes the rotated size, so the first rotated frame shows all of it.
*/
contentSize?: { width: number; height: number };
/** The exit animation finished and the content unmounted. */
onClose?: (reason: IOverlayDismissReason) => void;
children?: ReactNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { resolveContentSize } from '../contentSize';

it('follows the window size by default', () => {
expect(resolveContentSize({ windowWidth: 360, windowHeight: 640 })).toEqual({
width: 360,
height: 640,
});
});

it('keeps a caller size while the window has not rotated yet', () => {
// Prepared for the landscape window the portrait one is rotating into.
expect(
resolveContentSize({
windowWidth: 360,
windowHeight: 640,
contentSize: { width: 640, height: 360 },
})
).toEqual({ width: 640, height: 360 });
Comment thread
limichange marked this conversation as resolved.
Resolved
});

it('keeps a caller size after the window size reaches JS', () => {
expect(
resolveContentSize({
windowWidth: 640,
windowHeight: 360,
contentSize: { width: 640, height: 360 },
})
).toEqual({ width: 640, height: 360 });
});
21 changes: 21 additions & 0 deletions native-views/react-native-native-overlay/src/contentSize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Non-sheet content geometry. JS lays the content out at the window size it
* last saw, and the native content host clips the content to that frame. Right
* after the window rotates, JS still holds the previous size, so a caller that
* prepared its content for the window it is about to rotate into passes that
* size instead.
*/
export function resolveContentSize({
windowWidth,
windowHeight,
contentSize,
}: {
windowWidth: number;
windowHeight: number;
contentSize?: { width: number; height: number };
}): { width: number; height: number } {
return {
width: contentSize?.width ?? windowWidth,
height: contentSize?.height ?? windowHeight,
};
}
Loading