1import * as React from 'react';
2import { View, StyleSheet } from 'react-native';
3import { useSafeAreaInsets } from './SafeAreaContext';
4import { Edge, NativeSafeAreaViewProps } from './SafeArea.types';
5
6// prettier-ignore
7const TOP    = 0b1000,
8      RIGHT  = 0b0100,
9      BOTTOM = 0b0010,
10      LEFT   = 0b0001,
11      ALL    = 0b1111;
12
13/* eslint-disable no-bitwise */
14
15const edgeBitmaskMap: Record<Edge, number> = {
16  top: TOP,
17  right: RIGHT,
18  bottom: BOTTOM,
19  left: LEFT,
20};
21
22export function SafeAreaView({
23  style = {},
24  mode,
25  edges,
26  ...rest
27}: NativeSafeAreaViewProps) {
28  const insets = useSafeAreaInsets();
29
30  const edgeBitmask =
31    edges != null
32      ? edges.reduce((accum, edge) => accum | edgeBitmaskMap[edge], 0)
33      : ALL;
34
35  const appliedStyle = React.useMemo(() => {
36    const insetTop = edgeBitmask & TOP ? insets.top : 0;
37    const insetRight = edgeBitmask & RIGHT ? insets.right : 0;
38    const insetBottom = edgeBitmask & BOTTOM ? insets.bottom : 0;
39    const insetLeft = edgeBitmask & LEFT ? insets.left : 0;
40
41    const flatStyle = StyleSheet.flatten(style) as Record<string, number>;
42
43    if (mode === 'margin') {
44      const {
45        margin = 0,
46        marginVertical = margin,
47        marginHorizontal = margin,
48        marginTop = marginVertical,
49        marginRight = marginHorizontal,
50        marginBottom = marginVertical,
51        marginLeft = marginHorizontal,
52      } = flatStyle;
53
54      const marginStyle = {
55        marginTop: marginTop + insetTop,
56        marginRight: marginRight + insetRight,
57        marginBottom: marginBottom + insetBottom,
58        marginLeft: marginLeft + insetLeft,
59      };
60
61      return [style, marginStyle];
62    } else {
63      const {
64        padding = 0,
65        paddingVertical = padding,
66        paddingHorizontal = padding,
67        paddingTop = paddingVertical,
68        paddingRight = paddingHorizontal,
69        paddingBottom = paddingVertical,
70        paddingLeft = paddingHorizontal,
71      } = flatStyle;
72
73      const paddingStyle = {
74        paddingTop: paddingTop + insetTop,
75        paddingRight: paddingRight + insetRight,
76        paddingBottom: paddingBottom + insetBottom,
77        paddingLeft: paddingLeft + insetLeft,
78      };
79
80      return [style, paddingStyle];
81    }
82  }, [style, insets, mode, edgeBitmask]);
83
84  return <View style={appliedStyle} {...rest} />;
85}
86