react-native-screens
Version:
Native navigation primitives for your React Native app.
173 lines (170 loc) • 8.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _warnOnce = _interopRequireDefault(require("warn-once"));
var _DebugContainer = _interopRequireDefault(require("./DebugContainer"));
var _ScreenStackHeaderConfig = require("./ScreenStackHeaderConfig");
var _Screen = _interopRequireDefault(require("./Screen"));
var _ScreenStack = _interopRequireDefault(require("./ScreenStack"));
var _contexts = require("../contexts");
var _ScreenFooter = require("./ScreenFooter");
var _SafeAreaView = _interopRequireDefault(require("./safe-area/SafeAreaView"));
var _flags = require("../flags");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
function ScreenStackItem({
children,
headerConfig,
activityState,
shouldFreeze,
stackPresentation,
sheetAllowedDetents,
contentStyle,
style,
screenId,
onHeaderHeightChange,
// eslint-disable-next-line camelcase
unstable_sheetFooter,
...rest
}, ref) {
const currentScreenRef = React.useRef(null);
const screenRefs = React.useContext(_contexts.RNSScreensRefContext);
React.useImperativeHandle(ref, () => currentScreenRef.current);
const stackPresentationWithDefault = stackPresentation ?? 'push';
const headerConfigHiddenWithDefault = headerConfig?.hidden ?? false;
const isHeaderInModal = _reactNative.Platform.OS === 'android' ? false : stackPresentationWithDefault !== 'push' && headerConfigHiddenWithDefault === false;
const headerHiddenPreviousRef = React.useRef(headerConfigHiddenWithDefault);
React.useEffect(() => {
(0, _warnOnce.default)(_reactNative.Platform.OS !== 'android' && stackPresentationWithDefault !== 'push' && headerHiddenPreviousRef.current !== headerConfigHiddenWithDefault, `Dynamically changing header's visibility in modals will result in remounting the screen and losing all local state.`);
headerHiddenPreviousRef.current = headerConfigHiddenWithDefault;
}, [headerConfigHiddenWithDefault, stackPresentationWithDefault]);
const hasEdgeEffects = rest?.scrollEdgeEffects === undefined || Object.values(rest.scrollEdgeEffects).some(propValue => propValue !== 'hidden');
const hasBlurEffect = headerConfig?.blurEffect !== undefined && headerConfig.blurEffect !== 'none';
(0, _warnOnce.default)(hasEdgeEffects && hasBlurEffect && _reactNative.Platform.OS === 'ios' && parseInt(_reactNative.Platform.Version, 10) >= 26, '[RNScreens] Using both `blurEffect` and `scrollEdgeEffects` simultaneously may cause overlapping effects.');
const debugContainerStyle = getPositioningStyle(sheetAllowedDetents, stackPresentationWithDefault);
// For iOS, we need to extract background color and apply it to Screen
// due to the safe area inset at the bottom of ScreenContentWrapper
let internalScreenStyle;
if (stackPresentationWithDefault === 'formSheet' && _reactNative.Platform.OS === 'ios' && contentStyle) {
const {
screenStyles,
contentWrapperStyles
} = extractScreenStyles(contentStyle);
internalScreenStyle = screenStyles;
contentStyle = contentWrapperStyles;
}
const shouldUseSafeAreaView = _reactNative.Platform.OS === 'ios' && parseInt(_reactNative.Platform.Version, 10) >= 26;
const content = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_DebugContainer.default, {
contentStyle: contentStyle,
style: debugContainerStyle,
stackPresentation: stackPresentationWithDefault
}, shouldUseSafeAreaView ? /*#__PURE__*/React.createElement(_SafeAreaView.default, {
edges: getSafeAreaEdges(headerConfig)
}, children) : children), /*#__PURE__*/React.createElement(_ScreenStackHeaderConfig.ScreenStackHeaderConfig, headerConfig), stackPresentationWithDefault === 'formSheet' && unstable_sheetFooter && /*#__PURE__*/React.createElement(_ScreenFooter.FooterComponent, null, unstable_sheetFooter()));
return /*#__PURE__*/React.createElement(_Screen.default, _extends({
ref: node => {
currentScreenRef.current = node;
if (screenRefs === null) {
console.warn('Looks like RNSScreensRefContext is missing. Make sure the ScreenStack component is wrapped in it');
return;
}
const currentRefs = screenRefs.current;
if (node === null) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete currentRefs[screenId];
} else {
currentRefs[screenId] = {
current: node
};
}
},
enabled: true,
isNativeStack: true,
activityState: activityState,
shouldFreeze: shouldFreeze,
screenId: screenId,
stackPresentation: stackPresentationWithDefault,
hasLargeHeader: headerConfig?.largeTitle ?? false,
sheetAllowedDetents: sheetAllowedDetents,
style: [style, internalScreenStyle],
onHeaderHeightChange: isHeaderInModal ? undefined : onHeaderHeightChange
}, rest), isHeaderInModal ? /*#__PURE__*/React.createElement(_ScreenStack.default, {
style: styles.container
}, /*#__PURE__*/React.createElement(_Screen.default, {
enabled: true,
isNativeStack: true,
activityState: activityState,
shouldFreeze: shouldFreeze,
hasLargeHeader: headerConfig?.largeTitle ?? false,
style: _reactNative.StyleSheet.absoluteFill,
onHeaderHeightChange: onHeaderHeightChange
}, content)) : content);
}
var _default = exports.default = /*#__PURE__*/React.forwardRef(ScreenStackItem);
function getPositioningStyle(allowedDetents, presentation) {
const isIOS = _reactNative.Platform.OS === 'ios';
const rnMinorVersion = _reactNative.Platform.constants.reactNativeVersion.minor;
if (presentation !== 'formSheet') {
return styles.container;
}
if (isIOS) {
if (allowedDetents !== 'fitToContents' && rnMinorVersion >= 82 && _flags.featureFlags.experiment.synchronousScreenUpdatesEnabled) {
return styles.container;
} else {
return styles.absoluteWithNoBottom;
}
}
// Other platforms, tested reliably only on Android
if (allowedDetents === 'fitToContents') {
return {};
}
return styles.container;
}
// TODO: figure out whether other styles, like borders, filters, etc.
// shouldn't be applied on the Screen level on iOS due to the inset.
function extractScreenStyles(style) {
const flatStyle = _reactNative.StyleSheet.flatten(style);
const {
backgroundColor,
...contentWrapperStyles
} = flatStyle;
const screenStyles = {
backgroundColor
};
return {
screenStyles,
contentWrapperStyles
};
}
function getSafeAreaEdges(headerConfig) {
if (_reactNative.Platform.OS !== 'ios' || parseInt(_reactNative.Platform.Version, 10) < 26) {
return {};
}
let defaultEdges;
if (headerConfig?.translucent || headerConfig?.hidden) {
defaultEdges = {};
} else {
defaultEdges = {
top: true
};
}
return defaultEdges;
}
const styles = _reactNative.StyleSheet.create({
container: {
flex: 1
},
absoluteWithNoBottom: {
position: 'absolute',
top: 0,
start: 0,
end: 0
}
});
//# sourceMappingURL=ScreenStackItem.js.map