first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
import type { ZoomTransitionEnablerProps } from './ZoomTransitionEnabler.types';
export declare function disableZoomTransition(): void;
export declare function isZoomTransitionEnabled(): boolean;
export declare function ZoomTransitionEnabler(props: ZoomTransitionEnablerProps): null;
//# sourceMappingURL=ZoomTransitionEnabler.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ZoomTransitionEnabler.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/ZoomTransitionEnabler.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAEhF,wBAAgB,qBAAqB,SAAK;AAE1C,wBAAgB,uBAAuB,YAEtC;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,QAEtE"}

View File

@@ -0,0 +1,16 @@
import type { ZoomTransitionEnablerProps } from './ZoomTransitionEnabler.types';
import { INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SCREEN_ID_PARAM_NAME, INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME } from '../../navigationParams';
export declare function disableZoomTransition(): void;
export declare function isZoomTransitionEnabled(): boolean;
export declare function ZoomTransitionEnabler({ route }: ZoomTransitionEnablerProps): import("react").JSX.Element | null;
/**
* @internal
*/
export declare function useShouldEnableZoomTransition(route: unknown): route is {
key: string;
params: {
[INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME]: string;
[INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SCREEN_ID_PARAM_NAME]: string;
};
};
//# sourceMappingURL=ZoomTransitionEnabler.ios.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ZoomTransitionEnabler.ios.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/ZoomTransitionEnabler.ios.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAGhF,OAAO,EAGL,yDAAyD,EACzD,yDAAyD,EAC1D,MAAM,wBAAwB,CAAC;AAOhC,wBAAgB,qBAAqB,SAEpC;AAED,wBAAgB,uBAAuB,YAEtC;AAED,wBAAgB,qBAAqB,CAAC,EAAE,KAAK,EAAE,EAAE,0BAA0B,sCAmC1E;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE;QACN,CAAC,yDAAyD,CAAC,EAAE,MAAM,CAAC;QACpE,CAAC,yDAAyD,CAAC,EAAE,MAAM,CAAC;KACrE,CAAC;CACH,CAuCA"}

View File

@@ -0,0 +1,84 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.disableZoomTransition = disableZoomTransition;
exports.isZoomTransitionEnabled = isZoomTransitionEnabled;
exports.ZoomTransitionEnabler = ZoomTransitionEnabler;
exports.useShouldEnableZoomTransition = useShouldEnableZoomTransition;
const react_1 = require("react");
const zoom_transition_context_1 = require("./zoom-transition-context");
const descriptors_context_1 = require("../../fork/native-stack/descriptors-context");
const navigationParams_1 = require("../../navigationParams");
const stackPresentation_1 = require("../../utils/stackPresentation");
const PreviewRouteContext_1 = require("../preview/PreviewRouteContext");
const native_1 = require("../preview/native");
let _isZoomTransitionEnabled = process.env.EXPO_OS === 'ios';
function disableZoomTransition() {
_isZoomTransitionEnabled = false;
}
function isZoomTransitionEnabled() {
return _isZoomTransitionEnabled;
}
function ZoomTransitionEnabler({ route }) {
const shouldEnableZoomTransition = useShouldEnableZoomTransition(route);
const targetContext = (0, react_1.use)(zoom_transition_context_1.ZoomTransitionTargetContext);
(0, react_1.useLayoutEffect)(() => {
if (shouldEnableZoomTransition && targetContext?.addEnabler && targetContext?.removeEnabler) {
targetContext.addEnabler();
return () => {
targetContext.removeEnabler();
};
}
return () => { };
}, [shouldEnableZoomTransition]);
if (shouldEnableZoomTransition) {
const params = route.params;
const zoomTransitionId = params[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME];
// Read dismissalBoundsRect from context (set by usePreventZoomTransitionDismissal hook)
const dismissalBoundsRect = targetContext.dismissalBoundsRect;
// Read gestureEnabled from the screen descriptor so that gestureEnabled: false
// automatically blocks the native zoom transition dismissal gesture,
// even when the user hasn't called usePreventZoomTransitionDismissal().
const descriptorsMap = (0, react_1.use)(descriptors_context_1.DescriptorsContext);
const gestureEnabled = descriptorsMap[route.key]?.options?.gestureEnabled;
const effectiveDismissalBoundsRect = dismissalBoundsRect ?? (gestureEnabled === false ? { maxX: 0, maxY: 0 } : null);
return (<native_1.LinkZoomTransitionEnabler zoomTransitionSourceIdentifier={zoomTransitionId} dismissalBoundsRect={effectiveDismissalBoundsRect}/>);
}
return null;
}
/**
* @internal
*/
function useShouldEnableZoomTransition(route) {
const isPreview = (0, PreviewRouteContext_1.useIsPreview)();
if (isZoomTransitionEnabled() &&
!isPreview &&
route &&
typeof route === 'object' &&
'params' in route &&
typeof route.params === 'object' &&
route.params &&
'key' in route &&
typeof route.key === 'string') {
const params = route.params;
const internalParams = (0, navigationParams_1.getInternalExpoRouterParams)(params);
const zoomTransitionId = internalParams[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME];
const zoomTransitionScreenId = internalParams[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SCREEN_ID_PARAM_NAME];
const hasZoomTransition = !!zoomTransitionId && zoomTransitionScreenId === route.key;
if (hasZoomTransition && typeof zoomTransitionId === 'string') {
// Read gestureEnabled from the screen descriptor so that gestureEnabled: false
// automatically blocks the native zoom transition dismissal gesture,
// even when the user hasn't called usePreventZoomTransitionDismissal().
const descriptorsMap = (0, react_1.use)(descriptors_context_1.DescriptorsContext);
const isLinkPreviewNavigation = !!internalParams[navigationParams_1.INTERNAL_EXPO_ROUTER_IS_PREVIEW_NAVIGATION_PARAM_NAME];
const isPresentedAsModal = (0, stackPresentation_1.isModalPresentation)(descriptorsMap[route.key]?.options);
if (isLinkPreviewNavigation && !isPresentedAsModal) {
console.warn('[expo-router] Zoom transition with link preview is only supported for screens presented modally. Please set the screen presentation to "fullScreenModal" or another modal presentation style.');
}
else {
return true;
}
}
}
return false;
}
//# sourceMappingURL=ZoomTransitionEnabler.ios.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.disableZoomTransition = disableZoomTransition;
exports.isZoomTransitionEnabled = isZoomTransitionEnabled;
exports.ZoomTransitionEnabler = ZoomTransitionEnabler;
function disableZoomTransition() { }
function isZoomTransitionEnabled() {
return false;
}
function ZoomTransitionEnabler(props) {
return null;
}
//# sourceMappingURL=ZoomTransitionEnabler.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ZoomTransitionEnabler.js","sourceRoot":"","sources":["../../../src/link/zoom/ZoomTransitionEnabler.tsx"],"names":[],"mappings":";;AAEA,sDAA0C;AAE1C,0DAEC;AAED,sDAEC;AARD,SAAgB,qBAAqB,KAAI,CAAC;AAE1C,SAAgB,uBAAuB;IACrC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,qBAAqB,CAAC,KAAiC;IACrE,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import type { ZoomTransitionEnablerProps } from './ZoomTransitionEnabler.types';\n\nexport function disableZoomTransition() {}\n\nexport function isZoomTransitionEnabled() {\n return false;\n}\n\nexport function ZoomTransitionEnabler(props: ZoomTransitionEnablerProps) {\n return null;\n}\n"]}

View File

@@ -0,0 +1,4 @@
export interface ZoomTransitionEnablerProps {
route?: unknown;
}
//# sourceMappingURL=ZoomTransitionEnabler.types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ZoomTransitionEnabler.types.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/ZoomTransitionEnabler.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=ZoomTransitionEnabler.types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ZoomTransitionEnabler.types.js","sourceRoot":"","sources":["../../../src/link/zoom/ZoomTransitionEnabler.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface ZoomTransitionEnablerProps {\n route?: unknown;\n}\n"]}

View File

@@ -0,0 +1,25 @@
import { type ReactNode } from 'react';
/**
* Defines the target for an Apple zoom transition.
*
* @example
* ```tsx
* import { Link } from 'expo-router';
*
* export default function Screen() {
* return (
* <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
* <Link.AppleZoomTarget>
* <Image source={require('../assets/image.png')} style={{ width: 200, height: 200 }} />
* </Link.AppleZoomTarget>
* </View>
* );
* }
* ```
*
* @platform ios 18+
*/
export declare function LinkAppleZoomTarget({ children }: {
children?: ReactNode;
}): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
//# sourceMappingURL=link-apple-zoom-target.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"link-apple-zoom-target.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/link-apple-zoom-target.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+TAgBzE"}

View File

@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LinkAppleZoomTarget = LinkAppleZoomTarget;
const react_1 = require("react");
const zoom_transition_context_1 = require("./zoom-transition-context");
const native_1 = require("../preview/native");
/**
* Defines the target for an Apple zoom transition.
*
* @example
* ```tsx
* import { Link } from 'expo-router';
*
* export default function Screen() {
* return (
* <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
* <Link.AppleZoomTarget>
* <Image source={require('../assets/image.png')} style={{ width: 200, height: 200 }} />
* </Link.AppleZoomTarget>
* </View>
* );
* }
* ```
*
* @platform ios 18+
*/
function LinkAppleZoomTarget({ children }) {
if (react_1.Children.count(children) > 1) {
console.warn('[expo-router] Link.AppleZoomTarget only accepts a single child component. Please wrap multiple children in a View or another container component.');
return null;
}
const { identifier } = (0, react_1.use)(zoom_transition_context_1.ZoomTransitionTargetContext);
if (!identifier) {
return children;
}
return (<native_1.LinkZoomTransitionAlignmentRectDetector identifier={identifier}>
{children}
</native_1.LinkZoomTransitionAlignmentRectDetector>);
}
//# sourceMappingURL=link-apple-zoom-target.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"link-apple-zoom-target.js","sourceRoot":"","sources":["../../../src/link/zoom/link-apple-zoom-target.tsx"],"names":[],"mappings":";;AAyBA,kDAgBC;AAzCD,iCAAsD;AAEtD,uEAAwE;AACxE,8CAA4E;AAE5E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAA4B;IACxE,IAAI,gBAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,IAAI,CACV,mJAAmJ,CACpJ,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,EAAE,UAAU,EAAE,GAAG,IAAA,WAAG,EAAC,qDAA2B,CAAC,CAAC;IACxD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,CACL,CAAC,gDAAuC,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,CAC9D;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,gDAAuC,CAAC,CAC3C,CAAC;AACJ,CAAC","sourcesContent":["import { Children, use, type ReactNode } from 'react';\n\nimport { ZoomTransitionTargetContext } from './zoom-transition-context';\nimport { LinkZoomTransitionAlignmentRectDetector } from '../preview/native';\n\n/**\n * Defines the target for an Apple zoom transition.\n *\n * @example\n * ```tsx\n * import { Link } from 'expo-router';\n *\n * export default function Screen() {\n * return (\n * <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>\n * <Link.AppleZoomTarget>\n * <Image source={require('../assets/image.png')} style={{ width: 200, height: 200 }} />\n * </Link.AppleZoomTarget>\n * </View>\n * );\n * }\n * ```\n *\n * @platform ios 18+\n */\nexport function LinkAppleZoomTarget({ children }: { children?: ReactNode }) {\n if (Children.count(children) > 1) {\n console.warn(\n '[expo-router] Link.AppleZoomTarget only accepts a single child component. Please wrap multiple children in a View or another container component.'\n );\n return null;\n }\n const { identifier } = use(ZoomTransitionTargetContext);\n if (!identifier) {\n return children;\n }\n return (\n <LinkZoomTransitionAlignmentRectDetector identifier={identifier}>\n {children}\n </LinkZoomTransitionAlignmentRectDetector>\n );\n}\n"]}

View File

@@ -0,0 +1,22 @@
import { type PropsWithChildren } from 'react';
export interface LinkAppleZoomProps extends PropsWithChildren {
/**
* Defines the rectangle used for the zoom transition's alignment. This rectangle is specified in the zoomed screen's coordinate space.
*
* @platform ios 18+
*/
alignmentRect?: {
x: number;
y: number;
width: number;
height: number;
};
}
/**
* When this component is used inside a Link, [zoom transition](https://developer.apple.com/documentation/uikit/enhancing-your-app-with-fluid-transitions?language=objc)
* will be used when navigating to the link's href.
*
* @platform ios 18+
*/
export declare function LinkAppleZoom(props: LinkAppleZoomProps): import("react").JSX.Element;
//# sourceMappingURL=link-apple-zoom.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"link-apple-zoom.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/link-apple-zoom.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAOzE,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D;;;;OAIG;IACH,aAAa,CAAC,EAAE;QACd,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,+BAKtD"}

View File

@@ -0,0 +1,47 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.LinkAppleZoom = LinkAppleZoom;
const react_1 = require("react");
const ZoomTransitionEnabler_1 = require("./ZoomTransitionEnabler");
const zoom_transition_context_1 = require("./zoom-transition-context");
const Slot_1 = require("../../ui/Slot");
const native_1 = require("../preview/native");
/**
* When this component is used inside a Link, [zoom transition](https://developer.apple.com/documentation/uikit/enhancing-your-app-with-fluid-transitions?language=objc)
* will be used when navigating to the link's href.
*
* @platform ios 18+
*/
function LinkAppleZoom(props) {
if (!(0, ZoomTransitionEnabler_1.isZoomTransitionEnabled)()) {
return <Slot_1.Slot {...props}/>;
}
return <LinkAppleZoomImpl {...props}/>;
}
function LinkAppleZoomImpl({ children, alignmentRect, ...rest }) {
const value = (0, react_1.use)(zoom_transition_context_1.ZoomTransitionSourceContext);
if (!value) {
throw new Error('[expo-router] Link.ZoomTransitionSource must be used within a Link');
}
const { identifier, addSource, removeSource } = value;
(0, react_1.useEffect)(() => {
addSource();
return removeSource;
}, [addSource, removeSource]);
const hasTooManyChildren = react_1.Children.count(children) > 1;
(0, react_1.useEffect)(() => {
if (process.env.NODE_ENV !== 'production' && hasTooManyChildren) {
console.warn('[expo-router] Link.ZoomTransitionSource only accepts a single child component. Please wrap multiple children in a View or another container component.');
}
}, [hasTooManyChildren]);
if (hasTooManyChildren) {
return null;
}
return (<native_1.LinkZoomTransitionSource identifier={identifier} alignment={alignmentRect}
// Note(@ubax): Even though we always set this to true, I want to keep the prop here for easier future changes.
animateAspectRatioChange>
<Slot_1.Slot {...rest}>{children}</Slot_1.Slot>
</native_1.LinkZoomTransitionSource>);
}
//# sourceMappingURL=link-apple-zoom.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"link-apple-zoom.js","sourceRoot":"","sources":["../../../src/link/zoom/link-apple-zoom.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AA6Bb,sCAKC;AAhCD,iCAAyE;AAEzE,mEAAkE;AAClE,uEAAwE;AACxE,wCAAqC;AACrC,8CAA6D;AAgB7D;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,KAAyB;IACrD,IAAI,CAAC,IAAA,+CAAuB,GAAE,EAAE,CAAC;QAC/B,OAAO,CAAC,WAAI,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;IAC7B,CAAC;IACD,OAAO,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AAC1C,CAAC;AAMD,SAAS,iBAAiB,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,IAAI,EAA0B;IACrF,MAAM,KAAK,GAAG,IAAA,WAAG,EAAC,qDAA2B,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC;IAEtD,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,SAAS,EAAE,CAAC;QACZ,OAAO,YAAY,CAAC;IACtB,CAAC,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;IAE9B,MAAM,kBAAkB,GAAG,gBAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAExD,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,kBAAkB,EAAE,CAAC;YAChE,OAAO,CAAC,IAAI,CACV,wJAAwJ,CACzJ,CAAC;QACJ,CAAC;IACH,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAEzB,IAAI,kBAAkB,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CACL,CAAC,iCAAwB,CACvB,UAAU,CAAC,CAAC,UAAU,CAAC,CACvB,SAAS,CAAC,CAAC,aAAa,CAAC;IACzB,+GAA+G;IAC/G,wBAAwB,CACxB;MAAA,CAAC,WAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,WAAI,CAClC;IAAA,EAAE,iCAAwB,CAAC,CAC5B,CAAC;AACJ,CAAC","sourcesContent":["'use client';\n\nimport { Children, use, useEffect, type PropsWithChildren } from 'react';\n\nimport { isZoomTransitionEnabled } from './ZoomTransitionEnabler';\nimport { ZoomTransitionSourceContext } from './zoom-transition-context';\nimport { Slot } from '../../ui/Slot';\nimport { LinkZoomTransitionSource } from '../preview/native';\n\nexport interface LinkAppleZoomProps extends PropsWithChildren {\n /**\n * Defines the rectangle used for the zoom transition's alignment. This rectangle is specified in the zoomed screen's coordinate space.\n *\n * @platform ios 18+\n */\n alignmentRect?: {\n x: number;\n y: number;\n width: number;\n height: number;\n };\n}\n\n/**\n * When this component is used inside a Link, [zoom transition](https://developer.apple.com/documentation/uikit/enhancing-your-app-with-fluid-transitions?language=objc)\n * will be used when navigating to the link's href.\n *\n * @platform ios 18+\n */\nexport function LinkAppleZoom(props: LinkAppleZoomProps) {\n if (!isZoomTransitionEnabled()) {\n return <Slot {...props} />;\n }\n return <LinkAppleZoomImpl {...props} />;\n}\n\ntype LinkAppleZoomImplProps = LinkAppleZoomProps & {\n onPress?: () => void;\n};\n\nfunction LinkAppleZoomImpl({ children, alignmentRect, ...rest }: LinkAppleZoomImplProps) {\n const value = use(ZoomTransitionSourceContext);\n if (!value) {\n throw new Error('[expo-router] Link.ZoomTransitionSource must be used within a Link');\n }\n const { identifier, addSource, removeSource } = value;\n\n useEffect(() => {\n addSource();\n return removeSource;\n }, [addSource, removeSource]);\n\n const hasTooManyChildren = Children.count(children) > 1;\n\n useEffect(() => {\n if (process.env.NODE_ENV !== 'production' && hasTooManyChildren) {\n console.warn(\n '[expo-router] Link.ZoomTransitionSource only accepts a single child component. Please wrap multiple children in a View or another container component.'\n );\n }\n }, [hasTooManyChildren]);\n\n if (hasTooManyChildren) {\n return null;\n }\n\n return (\n <LinkZoomTransitionSource\n identifier={identifier}\n alignment={alignmentRect}\n // Note(@ubax): Even though we always set this to true, I want to keep the prop here for easier future changes.\n animateAspectRatioChange>\n <Slot {...rest}>{children}</Slot>\n </LinkZoomTransitionSource>\n );\n}\n"]}

View File

@@ -0,0 +1,36 @@
import type { UsePreventZoomTransitionDismissalOptions } from './usePreventZoomTransitionDismissal.types';
/**
* Limits the screen area where interactive dismissal gestures are allowed for zoom transitions.
*
* This hook must be called from the destination screen of a zoom transition (the screen you navigate to, not the source).
* It restricts where app users can start swipe gestures to dismiss the screen and return to the previous screen.
*
* When a dismissal gesture starts inside the bounds, the screen can be dismissed. When a dismissal gesture starts outside
* the bounds, dismissal is blocked completely. Undefined coordinates place no restriction on that dimension.
*
* > **Note**: Only one instance of this hook should be used per screen. If multiple instances exist, the last one to render will take effect.
*
* @example
* ```tsx
* // In your destination screen (e.g., app/image.tsx)
* import { usePreventZoomTransitionDismissal } from 'expo-router';
* import { useWindowDimensions } from 'react-native';
* import { Image } from 'expo-image';
*
* export default function ImageScreen() {
* const dimensions = useWindowDimensions();
* // Only allow dismissal from the bottom 200px of the screen
* usePreventZoomTransitionDismissal({
* unstable_dismissalBoundsRect: {
* minY: dimensions.height - 200
* }
* });
*
* return <Image source={...} style={{ flex: 1 }} />;
* }
* ```
*
* @platform ios
*/
export declare function usePreventZoomTransitionDismissal(_options?: UsePreventZoomTransitionDismissalOptions): void;
//# sourceMappingURL=usePreventZoomTransitionDismissal.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"usePreventZoomTransitionDismissal.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/usePreventZoomTransitionDismissal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wCAAwC,EAAE,MAAM,2CAA2C,CAAC;AAE1G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,CAAC,EAAE,wCAAwC,QAGpD"}

View File

@@ -0,0 +1,3 @@
import type { UsePreventZoomTransitionDismissalOptions } from './usePreventZoomTransitionDismissal.types';
export declare function usePreventZoomTransitionDismissal(options?: UsePreventZoomTransitionDismissalOptions): void;
//# sourceMappingURL=usePreventZoomTransitionDismissal.ios.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"usePreventZoomTransitionDismissal.ios.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/usePreventZoomTransitionDismissal.ios.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wCAAwC,EAAE,MAAM,2CAA2C,CAAC;AAU1G,wBAAgB,iCAAiC,CAC/C,OAAO,CAAC,EAAE,wCAAwC,QA6EnD"}

View File

@@ -0,0 +1,83 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePreventZoomTransitionDismissal = usePreventZoomTransitionDismissal;
const native_1 = require("@react-navigation/native");
const react_1 = require("react");
const zoom_transition_context_1 = require("./zoom-transition-context");
const descriptors_context_1 = require("../../fork/native-stack/descriptors-context");
const navigationParams_1 = require("../../navigationParams");
const useNavigation_1 = require("../../useNavigation");
const stack_1 = require("../../utils/stack");
const stackPresentation_1 = require("../../utils/stackPresentation");
const useSafeLayoutEffect_1 = require("../../views/useSafeLayoutEffect");
const PreviewRouteContext_1 = require("../preview/PreviewRouteContext");
function usePreventZoomTransitionDismissal(options) {
const context = (0, react_1.use)(zoom_transition_context_1.ZoomTransitionTargetContext);
const route = (0, native_1.useRoute)();
const navigation = (0, useNavigation_1.useNavigation)();
const isPreview = (0, PreviewRouteContext_1.useIsPreview)();
const isFocused = navigation.isFocused();
const isPreloaded = isPreview ? false : (0, stack_1.isRoutePreloadedInStack)(navigation.getState(), route);
const descriptorsMap = (0, react_1.use)(descriptors_context_1.DescriptorsContext);
const currentDescriptor = descriptorsMap[route.key];
const gestureEnabled = currentDescriptor?.options?.gestureEnabled;
const isModal = (0, stackPresentation_1.isModalPresentation)(currentDescriptor?.options);
(0, useSafeLayoutEffect_1.useSafeLayoutEffect)(() => {
if (isPreview)
return;
if (isModal) {
console.warn('[expo-router] usePreventZoomTransitionDismissal has no effect on screens presented modally. Please remove this hook from the screen component or change the screen presentation to a non-modal style.');
return;
}
if (!context.hasEnabler) {
return;
}
const rect = options?.unstable_dismissalBoundsRect;
// Validate rect if provided
if (rect) {
const { minX, maxX, minY, maxY } = rect;
// Validate that max > min when both are defined
if (minX !== undefined && maxX !== undefined && minX >= maxX) {
console.warn('[expo-router] unstable_dismissalBoundsRect: minX must be less than maxX');
return;
}
if (minY !== undefined && maxY !== undefined && minY >= maxY) {
console.warn('[expo-router] unstable_dismissalBoundsRect: minY must be less than maxY');
return;
}
}
// Determine the final rect to use for dismissal bounds:
// 1. If user provided a rect, use it
// 2. If user disabled gestures entirely (gestureEnabled={false}), block the whole screen
// by setting impossible bounds { maxX: 0, maxY: 0 }
// 3. Otherwise, allow normal dismissal (null rect)
const computedRect = rect ?? (gestureEnabled === false ? { maxX: 0, maxY: 0 } : null);
context.setDismissalBoundsRect?.(computedRect);
if (!isPreloaded || (isPreloaded && isFocused)) {
// Disable React Navigation's gesture handler when we have custom bounds to prevent conflicts.
// The native zoom transition's interactiveDismissShouldBegin callback handles dismissal instead.
// We use the internal option to preserve the user's gestureEnabled setting.
navigation.setOptions({
[navigationParams_1.INTERNAL_EXPO_ROUTER_GESTURE_ENABLED_OPTION_NAME]: computedRect ? false : undefined,
});
}
// Cleanup on unmount
return () => {
context.setDismissalBoundsRect?.(null);
};
}, [
options?.unstable_dismissalBoundsRect?.minX,
options?.unstable_dismissalBoundsRect?.maxX,
options?.unstable_dismissalBoundsRect?.minY,
options?.unstable_dismissalBoundsRect?.maxY,
context.setDismissalBoundsRect,
gestureEnabled,
navigation,
isFocused,
isPreloaded,
isModal,
isPreview,
]);
}
//# sourceMappingURL=usePreventZoomTransitionDismissal.ios.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,41 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePreventZoomTransitionDismissal = usePreventZoomTransitionDismissal;
/**
* Limits the screen area where interactive dismissal gestures are allowed for zoom transitions.
*
* This hook must be called from the destination screen of a zoom transition (the screen you navigate to, not the source).
* It restricts where app users can start swipe gestures to dismiss the screen and return to the previous screen.
*
* When a dismissal gesture starts inside the bounds, the screen can be dismissed. When a dismissal gesture starts outside
* the bounds, dismissal is blocked completely. Undefined coordinates place no restriction on that dimension.
*
* > **Note**: Only one instance of this hook should be used per screen. If multiple instances exist, the last one to render will take effect.
*
* @example
* ```tsx
* // In your destination screen (e.g., app/image.tsx)
* import { usePreventZoomTransitionDismissal } from 'expo-router';
* import { useWindowDimensions } from 'react-native';
* import { Image } from 'expo-image';
*
* export default function ImageScreen() {
* const dimensions = useWindowDimensions();
* // Only allow dismissal from the bottom 200px of the screen
* usePreventZoomTransitionDismissal({
* unstable_dismissalBoundsRect: {
* minY: dimensions.height - 200
* }
* });
*
* return <Image source={...} style={{ flex: 1 }} />;
* }
* ```
*
* @platform ios
*/
function usePreventZoomTransitionDismissal(_options) {
// No-op on non-iOS platforms
}
//# sourceMappingURL=usePreventZoomTransitionDismissal.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"usePreventZoomTransitionDismissal.js","sourceRoot":"","sources":["../../../src/link/zoom/usePreventZoomTransitionDismissal.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AAqCb,8EAIC;AArCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,SAAgB,iCAAiC,CAC/C,QAAmD;IAEnD,6BAA6B;AAC/B,CAAC","sourcesContent":["'use client';\n\nimport type { UsePreventZoomTransitionDismissalOptions } from './usePreventZoomTransitionDismissal.types';\n\n/**\n * Limits the screen area where interactive dismissal gestures are allowed for zoom transitions.\n *\n * This hook must be called from the destination screen of a zoom transition (the screen you navigate to, not the source).\n * It restricts where app users can start swipe gestures to dismiss the screen and return to the previous screen.\n *\n * When a dismissal gesture starts inside the bounds, the screen can be dismissed. When a dismissal gesture starts outside\n * the bounds, dismissal is blocked completely. Undefined coordinates place no restriction on that dimension.\n *\n * > **Note**: Only one instance of this hook should be used per screen. If multiple instances exist, the last one to render will take effect.\n *\n * @example\n * ```tsx\n * // In your destination screen (e.g., app/image.tsx)\n * import { usePreventZoomTransitionDismissal } from 'expo-router';\n * import { useWindowDimensions } from 'react-native';\n * import { Image } from 'expo-image';\n *\n * export default function ImageScreen() {\n * const dimensions = useWindowDimensions();\n * // Only allow dismissal from the bottom 200px of the screen\n * usePreventZoomTransitionDismissal({\n * unstable_dismissalBoundsRect: {\n * minY: dimensions.height - 200\n * }\n * });\n *\n * return <Image source={...} style={{ flex: 1 }} />;\n * }\n * ```\n *\n * @platform ios\n */\nexport function usePreventZoomTransitionDismissal(\n _options?: UsePreventZoomTransitionDismissalOptions\n) {\n // No-op on non-iOS platforms\n}\n"]}

View File

@@ -0,0 +1,14 @@
import type { DismissalBoundsRect } from './zoom-transition-context';
export interface UsePreventZoomTransitionDismissalOptions {
/**
* Defines the screen bounds where interactive dismissal gestures are allowed.
*
* Each coordinate is optional. Undefined coordinates place no restriction on that dimension.
* For example, if only `minY` and `maxY` are defined, horizontal gestures are unrestricted
* while vertical gestures must stay within the Y bounds.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiviewcontroller/transition/zoomoptions/interactivedismissshouldbegin) for more information.
*/
unstable_dismissalBoundsRect?: DismissalBoundsRect;
}
//# sourceMappingURL=usePreventZoomTransitionDismissal.types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"usePreventZoomTransitionDismissal.types.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/usePreventZoomTransitionDismissal.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,MAAM,WAAW,wCAAwC;IACvD;;;;;;;;OAQG;IACH,4BAA4B,CAAC,EAAE,mBAAmB,CAAC;CACpD"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=usePreventZoomTransitionDismissal.types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"usePreventZoomTransitionDismissal.types.js","sourceRoot":"","sources":["../../../src/link/zoom/usePreventZoomTransitionDismissal.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { DismissalBoundsRect } from './zoom-transition-context';\n\nexport interface UsePreventZoomTransitionDismissalOptions {\n /**\n * Defines the screen bounds where interactive dismissal gestures are allowed.\n *\n * Each coordinate is optional. Undefined coordinates place no restriction on that dimension.\n * For example, if only `minY` and `maxY` are defined, horizontal gestures are unrestricted\n * while vertical gestures must stay within the Y bounds.\n *\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiviewcontroller/transition/zoomoptions/interactivedismissshouldbegin) for more information.\n */\n unstable_dismissalBoundsRect?: DismissalBoundsRect;\n}\n"]}

View File

@@ -0,0 +1,3 @@
import { LinkProps } from '../useLinkHooks';
export declare function useZoomHref({ href }: LinkProps): string | import("../..").HrefObject;
//# sourceMappingURL=useZoomHref.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useZoomHref.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/useZoomHref.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,uCAE9C"}

View File

@@ -0,0 +1,3 @@
import { LinkProps } from '../useLinkHooks';
export declare function useZoomHref({ href }: LinkProps): string | import("../..").HrefObject;
//# sourceMappingURL=useZoomHref.ios.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useZoomHref.ios.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/useZoomHref.ios.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,uCA8B9C"}

View File

@@ -0,0 +1,38 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.useZoomHref = useZoomHref;
const react_1 = require("react");
const navigationParams_1 = require("../../navigationParams");
const url_1 = require("../../utils/url");
const zoom_transition_context_1 = require("./zoom-transition-context");
function useZoomHref({ href }) {
const value = (0, react_1.use)(zoom_transition_context_1.ZoomTransitionSourceContext);
if (!value) {
throw new Error('[expo-router] useZoomHref must be used within a ZoomTransitionSourceContextProvider. This is most likely a bug in expo-router.');
}
const { hasZoomSource, identifier } = value;
return (0, react_1.useMemo)(() => {
if (!hasZoomSource) {
return href;
}
if (typeof href === 'string') {
const { pathname, searchParams } = (0, url_1.parseUrlUsingCustomBase)(href);
return {
pathname,
params: {
...Object.fromEntries(searchParams.entries()),
[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME]: identifier,
},
};
}
return {
pathname: href.pathname,
params: {
...(href.params ?? {}),
[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME]: identifier,
},
};
}, [href, identifier, hasZoomSource]);
}
//# sourceMappingURL=useZoomHref.ios.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useZoomHref.ios.js","sourceRoot":"","sources":["../../../src/link/zoom/useZoomHref.ios.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AASb,kCA8BC;AArCD,iCAAqC;AAErC,6DAAmG;AACnG,yCAA0D;AAE1D,uEAAwE;AAExE,SAAgB,WAAW,CAAC,EAAE,IAAI,EAAa;IAC7C,MAAM,KAAK,GAAG,IAAA,WAAG,EAAC,qDAA2B,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,gIAAgI,CACjI,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAC5C,OAAO,IAAA,eAAO,EAAC,GAAG,EAAE;QAClB,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAA,6BAAuB,EAAC,IAAI,CAAC,CAAC;YACjE,OAAO;gBACL,QAAQ;gBACR,MAAM,EAAE;oBACN,GAAG,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;oBAC7C,CAAC,4EAAyD,CAAC,EAAE,UAAU;iBACxE;aACF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE;gBACN,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;gBACtB,CAAC,4EAAyD,CAAC,EAAE,UAAU;aACxE;SACF,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC;AACxC,CAAC","sourcesContent":["'use client';\n\nimport { use, useMemo } from 'react';\n\nimport { INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME } from '../../navigationParams';\nimport { parseUrlUsingCustomBase } from '../../utils/url';\nimport { LinkProps } from '../useLinkHooks';\nimport { ZoomTransitionSourceContext } from './zoom-transition-context';\n\nexport function useZoomHref({ href }: LinkProps) {\n const value = use(ZoomTransitionSourceContext);\n if (!value) {\n throw new Error(\n '[expo-router] useZoomHref must be used within a ZoomTransitionSourceContextProvider. This is most likely a bug in expo-router.'\n );\n }\n const { hasZoomSource, identifier } = value;\n return useMemo(() => {\n if (!hasZoomSource) {\n return href;\n }\n if (typeof href === 'string') {\n const { pathname, searchParams } = parseUrlUsingCustomBase(href);\n return {\n pathname,\n params: {\n ...Object.fromEntries(searchParams.entries()),\n [INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME]: identifier,\n },\n };\n }\n return {\n pathname: href.pathname,\n params: {\n ...(href.params ?? {}),\n [INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME]: identifier,\n },\n };\n }, [href, identifier, hasZoomSource]);\n}\n"]}

View File

@@ -0,0 +1,8 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.useZoomHref = useZoomHref;
function useZoomHref({ href }) {
return href;
}
//# sourceMappingURL=useZoomHref.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useZoomHref.js","sourceRoot":"","sources":["../../../src/link/zoom/useZoomHref.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AAIb,kCAEC;AAFD,SAAgB,WAAW,CAAC,EAAE,IAAI,EAAa;IAC7C,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["'use client';\n\nimport { LinkProps } from '../useLinkHooks';\n\nexport function useZoomHref({ href }: LinkProps) {\n return href;\n}\n"]}

View File

@@ -0,0 +1,4 @@
import type { ZoomTransitionSourceContextProviderProps, ZoomTransitionTargetContextProviderProps } from './zoom-transition-context-providers.types';
export declare function ZoomTransitionSourceContextProvider({ children, }: ZoomTransitionSourceContextProviderProps): import("react").ReactNode;
export declare function ZoomTransitionTargetContextProvider({ children, }: ZoomTransitionTargetContextProviderProps): import("react").ReactNode;
//# sourceMappingURL=zoom-transition-context-providers.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"zoom-transition-context-providers.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/zoom-transition-context-providers.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wCAAwC,EACxC,wCAAwC,EACzC,MAAM,2CAA2C,CAAC;AAEnD,wBAAgB,mCAAmC,CAAC,EAClD,QAAQ,GACT,EAAE,wCAAwC,6BAE1C;AAED,wBAAgB,mCAAmC,CAAC,EAClD,QAAQ,GACT,EAAE,wCAAwC,6BAE1C"}

View File

@@ -0,0 +1,4 @@
import type { ZoomTransitionSourceContextProviderProps, ZoomTransitionTargetContextProviderProps } from './zoom-transition-context-providers.types';
export declare function ZoomTransitionSourceContextProvider({ children, linkProps, }: ZoomTransitionSourceContextProviderProps): import("react").JSX.Element;
export declare function ZoomTransitionTargetContextProvider({ route, children, }: ZoomTransitionTargetContextProviderProps): string | number | bigint | boolean | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
//# sourceMappingURL=zoom-transition-context-providers.ios.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"zoom-transition-context-providers.ios.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/zoom-transition-context-providers.ios.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,wCAAwC,EACxC,wCAAwC,EACzC,MAAM,2CAA2C,CAAC;AASnD,wBAAgB,mCAAmC,CAAC,EAClD,QAAQ,EACR,SAAS,GACV,EAAE,wCAAwC,+BAgD1C;AAED,wBAAgB,mCAAmC,CAAC,EAClD,KAAK,EACL,QAAQ,GACT,EAAE,wCAAwC,+VA+C1C"}

View File

@@ -0,0 +1,89 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZoomTransitionSourceContextProvider = ZoomTransitionSourceContextProvider;
exports.ZoomTransitionTargetContextProvider = ZoomTransitionTargetContextProvider;
const react_1 = require("react");
const ZoomTransitionEnabler_1 = require("./ZoomTransitionEnabler");
const zoom_transition_context_1 = require("./zoom-transition-context");
const navigationParams_1 = require("../../navigationParams");
const url_1 = require("../../utils/url");
const PreviewRouteContext_1 = require("../preview/PreviewRouteContext");
function ZoomTransitionSourceContextProvider({ children, linkProps, }) {
const { href, asChild } = linkProps;
const isExternalHref = typeof href === 'string' ? (0, url_1.shouldLinkExternally)(href) : (0, url_1.shouldLinkExternally)(href.pathname);
const numberOfSources = (0, react_1.useRef)(0);
const [hasZoomSource, setHasZoomSource] = (0, react_1.useState)(false);
const zoomTransitionId = (0, react_1.useId)();
const addSource = (0, react_1.useCallback)(() => {
if (!(0, ZoomTransitionEnabler_1.isZoomTransitionEnabled)()) {
throw new Error('[expo-router] Zoom transitions are not enabled.');
}
if (numberOfSources.current >= 1) {
throw new Error('[expo-router] Only one Link.ZoomTransitionSource can be used within a single Link component.');
}
if (!asChild) {
throw new Error('[expo-router] Link must be used with `asChild` prop to enable zoom transitions.');
}
if (isExternalHref) {
throw new Error('[expo-router] Zoom transitions can only be used with internal links.');
}
numberOfSources.current += 1;
setHasZoomSource(true);
}, [asChild, isExternalHref]);
const removeSource = (0, react_1.useCallback)(() => {
numberOfSources.current -= 1;
if (numberOfSources.current <= 0) {
setHasZoomSource(false);
}
}, []);
const value = (0, react_1.useMemo)(() => ({
identifier: zoomTransitionId,
hasZoomSource,
addSource,
removeSource,
}), [zoomTransitionId, hasZoomSource, addSource, removeSource]);
return <zoom_transition_context_1.ZoomTransitionSourceContext value={value}>{children}</zoom_transition_context_1.ZoomTransitionSourceContext>;
}
function ZoomTransitionTargetContextProvider({ route, children, }) {
const [dismissalBoundsRect, setDismissalBoundsRect] = (0, react_1.useState)(null);
// TODO(@ubax): Move this logic to within NativeStackView
// https://linear.app/expo/issue/ENG-19580/remove-hasenabler-logic-from-zoomtransitiontargetcontext
// This is a temporary solution to detect if zoom transition was enabled for the screen
// In theory we could do all the checks here and only mount the enabler when all conditions are met
// However this would require using use(DescriptorsContext) here,
// which would cause unnecessary re-renders of the entire screen whenever descriptors change
const [numberOfEnablers, setNumberOfEnablers] = (0, react_1.useState)(0);
const addEnabler = (0, react_1.useCallback)(() => setNumberOfEnablers((prev) => prev + 1), []);
const removeEnabler = (0, react_1.useCallback)(() => setNumberOfEnablers((prev) => prev - 1), []);
const hasEnabler = numberOfEnablers > 0;
const isPreview = (0, PreviewRouteContext_1.useIsPreview)();
if ((0, ZoomTransitionEnabler_1.isZoomTransitionEnabled)() &&
!isPreview &&
route &&
typeof route === 'object' &&
'params' in route &&
typeof route.params === 'object' &&
'key' in route &&
typeof route.key === 'string') {
const params = route.params ?? {};
const internalParams = (0, navigationParams_1.getInternalExpoRouterParams)(params);
const zoomTransitionId = internalParams[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SOURCE_ID_PARAM_NAME];
const zoomTransitionScreenId = internalParams[navigationParams_1.INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SCREEN_ID_PARAM_NAME];
const hasZoomTransition = !!zoomTransitionId && zoomTransitionScreenId === route.key;
if (hasZoomTransition && typeof zoomTransitionId === 'string') {
return (<zoom_transition_context_1.ZoomTransitionTargetContext value={{
identifier: zoomTransitionId,
dismissalBoundsRect,
setDismissalBoundsRect,
addEnabler,
removeEnabler,
hasEnabler,
}}>
{children}
</zoom_transition_context_1.ZoomTransitionTargetContext>);
}
}
return children;
}
//# sourceMappingURL=zoom-transition-context-providers.ios.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZoomTransitionSourceContextProvider = ZoomTransitionSourceContextProvider;
exports.ZoomTransitionTargetContextProvider = ZoomTransitionTargetContextProvider;
function ZoomTransitionSourceContextProvider({ children, }) {
return children;
}
function ZoomTransitionTargetContextProvider({ children, }) {
return children;
}
//# sourceMappingURL=zoom-transition-context-providers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"zoom-transition-context-providers.js","sourceRoot":"","sources":["../../../src/link/zoom/zoom-transition-context-providers.tsx"],"names":[],"mappings":";;AAKA,kFAIC;AAED,kFAIC;AAVD,SAAgB,mCAAmC,CAAC,EAClD,QAAQ,GACiC;IACzC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAgB,mCAAmC,CAAC,EAClD,QAAQ,GACiC;IACzC,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["import type {\n ZoomTransitionSourceContextProviderProps,\n ZoomTransitionTargetContextProviderProps,\n} from './zoom-transition-context-providers.types';\n\nexport function ZoomTransitionSourceContextProvider({\n children,\n}: ZoomTransitionSourceContextProviderProps) {\n return children;\n}\n\nexport function ZoomTransitionTargetContextProvider({\n children,\n}: ZoomTransitionTargetContextProviderProps) {\n return children;\n}\n"]}

View File

@@ -0,0 +1,9 @@
import { type PropsWithChildren } from 'react';
import type { LinkProps } from '../useLinkHooks';
export interface ZoomTransitionTargetContextProviderProps extends PropsWithChildren {
route: unknown;
}
export interface ZoomTransitionSourceContextProviderProps extends PropsWithChildren {
linkProps: LinkProps;
}
//# sourceMappingURL=zoom-transition-context-providers.types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"zoom-transition-context-providers.types.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/zoom-transition-context-providers.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,wCAAyC,SAAQ,iBAAiB;IACjF,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,wCAAyC,SAAQ,iBAAiB;IACjF,SAAS,EAAE,SAAS,CAAC;CACtB"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=zoom-transition-context-providers.types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"zoom-transition-context-providers.types.js","sourceRoot":"","sources":["../../../src/link/zoom/zoom-transition-context-providers.types.ts"],"names":[],"mappings":"","sourcesContent":["import { type PropsWithChildren } from 'react';\n\nimport type { LinkProps } from '../useLinkHooks';\n\nexport interface ZoomTransitionTargetContextProviderProps extends PropsWithChildren {\n route: unknown;\n}\n\nexport interface ZoomTransitionSourceContextProviderProps extends PropsWithChildren {\n linkProps: LinkProps;\n}\n"]}

View File

@@ -0,0 +1,40 @@
export type ZoomTransitionSourceContextValueType = {
identifier: string;
hasZoomSource: boolean;
addSource: () => void;
removeSource: () => void;
} | undefined;
export declare const ZoomTransitionSourceContext: import("react").Context<ZoomTransitionSourceContextValueType>;
/**
* Defines the screen bounds where interactive dismissal gestures are allowed for zoom transitions.
*
* @platform ios
*/
export interface DismissalBoundsRect {
/**
* Minimum X coordinate (left edge) where dismissal gestures are allowed.
*/
minX?: number;
/**
* Maximum X coordinate (right edge) where dismissal gestures are allowed.
*/
maxX?: number;
/**
* Minimum Y coordinate (top edge) where dismissal gestures are allowed.
*/
minY?: number;
/**
* Maximum Y coordinate (bottom edge) where dismissal gestures are allowed.
*/
maxY?: number;
}
export interface ZoomTransitionTargetContextValueType {
identifier: string | null;
dismissalBoundsRect: DismissalBoundsRect | undefined | null;
setDismissalBoundsRect: (rect: DismissalBoundsRect | null) => void;
addEnabler: () => void;
removeEnabler: () => void;
hasEnabler: boolean;
}
export declare const ZoomTransitionTargetContext: import("react").Context<ZoomTransitionTargetContextValueType>;
//# sourceMappingURL=zoom-transition-context.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"zoom-transition-context.d.ts","sourceRoot":"","sources":["../../../src/link/zoom/zoom-transition-context.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,oCAAoC,GAC5C;IACE,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,GACD,SAAS,CAAC;AAEd,eAAO,MAAM,2BAA2B,+DACwB,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oCAAoC;IACnD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mBAAmB,EAAE,mBAAmB,GAAG,SAAS,GAAG,IAAI,CAAC;IAC5D,sBAAsB,EAAE,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,KAAK,IAAI,CAAC;IACnE,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,2BAA2B,+DAOtC,CAAC"}

View File

@@ -0,0 +1,15 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZoomTransitionTargetContext = exports.ZoomTransitionSourceContext = void 0;
const react_1 = require("react");
exports.ZoomTransitionSourceContext = (0, react_1.createContext)(undefined);
exports.ZoomTransitionTargetContext = (0, react_1.createContext)({
identifier: null,
dismissalBoundsRect: null,
setDismissalBoundsRect: () => { },
addEnabler: () => { },
removeEnabler: () => { },
hasEnabler: false,
});
//# sourceMappingURL=zoom-transition-context.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"zoom-transition-context.js","sourceRoot":"","sources":["../../../src/link/zoom/zoom-transition-context.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;AAEb,iCAAsC;AAWzB,QAAA,2BAA2B,GACtC,IAAA,qBAAa,EAAuC,SAAS,CAAC,CAAC;AAmCpD,QAAA,2BAA2B,GAAG,IAAA,qBAAa,EAAuC;IAC7F,UAAU,EAAE,IAAI;IAChB,mBAAmB,EAAE,IAAI;IACzB,sBAAsB,EAAE,GAAG,EAAE,GAAE,CAAC;IAChC,UAAU,EAAE,GAAG,EAAE,GAAE,CAAC;IACpB,aAAa,EAAE,GAAG,EAAE,GAAE,CAAC;IACvB,UAAU,EAAE,KAAK;CAClB,CAAC,CAAC","sourcesContent":["'use client';\n\nimport { createContext } from 'react';\n\nexport type ZoomTransitionSourceContextValueType =\n | {\n identifier: string;\n hasZoomSource: boolean;\n addSource: () => void;\n removeSource: () => void;\n }\n | undefined;\n\nexport const ZoomTransitionSourceContext =\n createContext<ZoomTransitionSourceContextValueType>(undefined);\n\n/**\n * Defines the screen bounds where interactive dismissal gestures are allowed for zoom transitions.\n *\n * @platform ios\n */\nexport interface DismissalBoundsRect {\n /**\n * Minimum X coordinate (left edge) where dismissal gestures are allowed.\n */\n minX?: number;\n /**\n * Maximum X coordinate (right edge) where dismissal gestures are allowed.\n */\n maxX?: number;\n /**\n * Minimum Y coordinate (top edge) where dismissal gestures are allowed.\n */\n minY?: number;\n /**\n * Maximum Y coordinate (bottom edge) where dismissal gestures are allowed.\n */\n maxY?: number;\n}\n\nexport interface ZoomTransitionTargetContextValueType {\n identifier: string | null;\n dismissalBoundsRect: DismissalBoundsRect | undefined | null;\n setDismissalBoundsRect: (rect: DismissalBoundsRect | null) => void;\n addEnabler: () => void;\n removeEnabler: () => void;\n hasEnabler: boolean;\n}\n\nexport const ZoomTransitionTargetContext = createContext<ZoomTransitionTargetContextValueType>({\n identifier: null,\n dismissalBoundsRect: null,\n setDismissalBoundsRect: () => {},\n addEnabler: () => {},\n removeEnabler: () => {},\n hasEnabler: false,\n});\n"]}