react-native-edge-to-edge
Version:
Effortlessly enable edge-to-edge display in React Native
192 lines (183 loc) • 7.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SystemBars = SystemBars;
var _react = require("react");
var _reactNative = require("react-native");
var _NativeEdgeToEdgeModule = _interopRequireDefault(require("./specs/NativeEdgeToEdgeModule"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function getColorScheme() {
return _reactNative.Appearance?.getColorScheme() ?? "light";
}
function toNativeBarStyle(style) {
return style === "light" || style === "dark" ? `${style}-content` : "default";
}
/**
* Merges the entries stack.
*/
function mergeEntriesStack(entriesStack) {
return entriesStack.reduce((prev, cur) => {
for (const prop in cur) {
if (cur[prop] != null) {
// @ts-expect-error
prev[prop] = cur[prop];
}
}
return prev;
}, {
statusBarStyle: undefined,
navigationBarStyle: undefined,
statusBarHidden: false,
navigationBarHidden: false
});
}
/**
* Returns an object to insert in the props stack from the props.
*/
function createStackEntry(props) {
return {
statusBarStyle: typeof props.style === "string" ? props.style : props.style?.statusBar,
navigationBarStyle: typeof props.style === "string" ? props.style : props.style?.navigationBar,
statusBarHidden: typeof props.hidden === "boolean" ? props.hidden : props.hidden?.statusBar,
navigationBarHidden: typeof props.hidden === "boolean" ? props.hidden : props.hidden?.navigationBar
};
}
const entriesStack = [];
// Timer for updating the native module values at the end of the frame.
let updateImmediate = null;
// The current merged values from the entries stack.
let currentMergedEntries = {
statusBarStyle: undefined,
navigationBarStyle: undefined,
statusBarHidden: false,
navigationBarHidden: false
};
/**
* Updates the native system bars with the entries from the stack.
*/
function updateEntriesStack() {
if (_reactNative.Platform.OS === "android" || _reactNative.Platform.OS === "ios") {
if (updateImmediate != null) {
clearImmediate(updateImmediate);
}
updateImmediate = setImmediate(() => {
const isLightColorScheme = getColorScheme() === "light";
const autoBarStyle = isLightColorScheme ? "dark" : "light";
const invertedBarStyle = isLightColorScheme ? "light" : "dark";
const mergedEntries = mergeEntriesStack(entriesStack);
const statusBarStyle = mergedEntries.statusBarStyle === "auto" ? autoBarStyle : mergedEntries.statusBarStyle === "inverted" ? invertedBarStyle : mergedEntries.statusBarStyle;
const navigationBarStyle = mergedEntries.navigationBarStyle === "auto" ? autoBarStyle : mergedEntries.navigationBarStyle === "inverted" ? invertedBarStyle : mergedEntries.navigationBarStyle;
const {
statusBarHidden,
navigationBarHidden
} = mergedEntries;
if (statusBarStyle !== currentMergedEntries.statusBarStyle) {
const style = toNativeBarStyle(statusBarStyle);
_reactNative.Platform.OS === "android" ? _NativeEdgeToEdgeModule.default?.setStatusBarStyle(style) : _reactNative.StatusBar.setBarStyle(style, true);
}
if (statusBarHidden !== currentMergedEntries.statusBarHidden) {
_reactNative.Platform.OS === "android" ? _NativeEdgeToEdgeModule.default?.setStatusBarHidden(statusBarHidden) : _reactNative.StatusBar.setHidden(statusBarHidden, "fade"); // 'slide' doesn't work in this context
}
if (_reactNative.Platform.OS === "android") {
if (navigationBarStyle !== currentMergedEntries.navigationBarStyle) {
const style = toNativeBarStyle(navigationBarStyle);
_NativeEdgeToEdgeModule.default?.setNavigationBarStyle(style);
}
if (navigationBarHidden !== currentMergedEntries.navigationBarHidden) {
_NativeEdgeToEdgeModule.default?.setNavigationBarHidden(navigationBarHidden);
}
}
currentMergedEntries = {
statusBarStyle,
navigationBarStyle,
statusBarHidden,
navigationBarHidden
};
});
}
}
/**
* Push a SystemBars entry onto the stack.
* The return value should be passed to `popStackEntry` when complete.
*
* @param props Object containing the SystemBars props to use in the stack entry.
*/
function pushStackEntry(props) {
const entry = createStackEntry(props);
entriesStack.push(entry);
updateEntriesStack();
return entry;
}
/**
* Pop a SystemBars entry from the stack.
*
* @param entry Entry returned from `pushStackEntry`.
*/
function popStackEntry(entry) {
const index = entriesStack.indexOf(entry);
if (index !== -1) {
entriesStack.splice(index, 1);
}
updateEntriesStack();
}
/**
* Replace an existing SystemBars stack entry with new props.
*
* @param entry Entry returned from `pushStackEntry` to replace.
* @param props Object containing the SystemBars props to use in the replacement stack entry.
*/
function replaceStackEntry(entry, props) {
const newEntry = createStackEntry(props);
const index = entriesStack.indexOf(entry);
if (index !== -1) {
entriesStack[index] = newEntry;
}
updateEntriesStack();
return newEntry;
}
function SystemBars({
hidden,
style
}) {
const statusBarStyle = typeof style === "string" ? style : style?.statusBar;
const navigationBarStyle = typeof style === "string" ? style : style?.navigationBar;
const statusBarHidden = typeof hidden === "boolean" ? hidden : hidden?.statusBar;
const navigationBarHidden = typeof hidden === "boolean" ? hidden : hidden?.navigationBar;
const stableProps = (0, _react.useMemo)(() => ({
style: statusBarStyle === navigationBarStyle ? statusBarStyle : {
statusBar: statusBarStyle,
navigationBar: navigationBarStyle
},
hidden: statusBarHidden === navigationBarHidden ? statusBarHidden : {
statusBar: statusBarHidden,
navigationBar: navigationBarHidden
}
}), [statusBarStyle, navigationBarStyle, statusBarHidden, navigationBarHidden]);
const colorScheme = (0, _reactNative.useColorScheme)();
const stackEntryRef = (0, _react.useRef)(null);
(0, _react.useEffect)(() => {
// Every time a SystemBars component is mounted, we push it's prop to a stack
// and always update the native system bars with the props from the top of then
// stack. This allows having multiple SystemBars components and the one that is
// added last or is deeper in the view hierarchy will have priority.
stackEntryRef.current = pushStackEntry(stableProps);
return () => {
// When a SystemBars is unmounted, remove itself from the stack and update
// the native bars with the next props.
if (stackEntryRef.current) {
popStackEntry(stackEntryRef.current);
}
};
}, []);
(0, _react.useEffect)(() => {
if (stackEntryRef.current) {
stackEntryRef.current = replaceStackEntry(stackEntryRef.current, stableProps);
}
}, [colorScheme, stableProps]);
return null;
}
SystemBars.pushStackEntry = pushStackEntry;
SystemBars.popStackEntry = popStackEntry;
SystemBars.replaceStackEntry = replaceStackEntry;
//# sourceMappingURL=SystemBars.js.map