12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import * as React from 'react';
  2. import { StyleSheet, View, ViewProps } from 'react-native';
  3. import { EdgeInsets as EdgeInsetsT, InsetChangedEvent } from './SafeArea.types';
  4. import NativeSafeAreaView from './NativeSafeAreaView';
  5. export const SafeAreaContext = React.createContext<EdgeInsetsT | null>(null);
  6. export interface SafeAreaViewProps {
  7. children?: React.ReactNode;
  8. initialSafeAreaInsets?: EdgeInsetsT | null;
  9. }
  10. export function SafeAreaProvider({
  11. children,
  12. initialSafeAreaInsets,
  13. }: SafeAreaViewProps) {
  14. const parentInsets = useParentSafeArea();
  15. const [insets, setInsets] = React.useState<EdgeInsetsT | null | undefined>(
  16. initialSafeAreaInsets || parentInsets,
  17. );
  18. const onInsetsChange = React.useCallback((event: InsetChangedEvent) => {
  19. setInsets(event.nativeEvent.insets);
  20. }, []);
  21. return (
  22. <NativeSafeAreaView style={styles.fill} onInsetsChange={onInsetsChange}>
  23. {insets != null ? (
  24. <SafeAreaContext.Provider value={insets}>
  25. {children}
  26. </SafeAreaContext.Provider>
  27. ) : null}
  28. </NativeSafeAreaView>
  29. );
  30. }
  31. const styles = StyleSheet.create({
  32. fill: { flex: 1 },
  33. });
  34. export const SafeAreaConsumer = SafeAreaContext.Consumer;
  35. function useParentSafeArea(): React.ContextType<typeof SafeAreaContext> {
  36. return React.useContext(SafeAreaContext);
  37. }
  38. export function useSafeArea(): EdgeInsetsT {
  39. const safeArea = React.useContext(SafeAreaContext);
  40. if (safeArea == null) {
  41. throw new Error(
  42. 'No safe area value available. Make sure you are rendering `<SafeAreaProvider>` at the top of your app.',
  43. );
  44. }
  45. return safeArea;
  46. }
  47. export function SafeAreaView({
  48. style,
  49. ...rest
  50. }: ViewProps & { children: React.ReactNode }) {
  51. const insets = useSafeArea();
  52. return (
  53. <View
  54. style={[
  55. {
  56. paddingTop: insets.top,
  57. paddingRight: insets.right,
  58. paddingBottom: insets.bottom,
  59. paddingLeft: insets.left,
  60. },
  61. style,
  62. ]}
  63. {...rest}
  64. />
  65. );
  66. }
  67. export type EdgeInsets = EdgeInsetsT;