|  | @@ -0,0 +1,112 @@
 | 
	
		
			
			|  | 1 | +import * as React from 'react';
 | 
	
		
			
			|  | 2 | +import { ViewStyle, View } from 'react-native';
 | 
	
		
			
			|  | 3 | +
 | 
	
		
			
			|  | 4 | +import { InsetChangeNativeCallback } from './SafeArea.types';
 | 
	
		
			
			|  | 5 | +
 | 
	
		
			
			|  | 6 | +interface NativeSafeAreaViewProps {
 | 
	
		
			
			|  | 7 | +  children?: React.ReactNode;
 | 
	
		
			
			|  | 8 | +  style: ViewStyle;
 | 
	
		
			
			|  | 9 | +  onInsetsChange: InsetChangeNativeCallback;
 | 
	
		
			
			|  | 10 | +}
 | 
	
		
			
			|  | 11 | +
 | 
	
		
			
			|  | 12 | +enum CSSTransitions {
 | 
	
		
			
			|  | 13 | +  WebkitTransition = 'webkitTransitionEnd',
 | 
	
		
			
			|  | 14 | +  Transition = 'transitionEnd',
 | 
	
		
			
			|  | 15 | +  MozTransition = 'transitionend',
 | 
	
		
			
			|  | 16 | +  MSTransition = 'msTransitionEnd',
 | 
	
		
			
			|  | 17 | +  OTransition = 'oTransitionEnd',
 | 
	
		
			
			|  | 18 | +}
 | 
	
		
			
			|  | 19 | +
 | 
	
		
			
			|  | 20 | +export default function NativeSafeAreaView({
 | 
	
		
			
			|  | 21 | +  children,
 | 
	
		
			
			|  | 22 | +  style,
 | 
	
		
			
			|  | 23 | +  onInsetsChange,
 | 
	
		
			
			|  | 24 | +}: NativeSafeAreaViewProps) {
 | 
	
		
			
			|  | 25 | +  const element = createContextElement();
 | 
	
		
			
			|  | 26 | +  document.body.appendChild(element);
 | 
	
		
			
			|  | 27 | +
 | 
	
		
			
			|  | 28 | +  const onEnd = () => {
 | 
	
		
			
			|  | 29 | +    const {
 | 
	
		
			
			|  | 30 | +      paddingTop,
 | 
	
		
			
			|  | 31 | +      paddingBottom,
 | 
	
		
			
			|  | 32 | +      paddingLeft,
 | 
	
		
			
			|  | 33 | +      paddingRight,
 | 
	
		
			
			|  | 34 | +    } = getComputedStyle(element);
 | 
	
		
			
			|  | 35 | +
 | 
	
		
			
			|  | 36 | +    const insets = {
 | 
	
		
			
			|  | 37 | +      top: paddingTop ? parseInt(paddingTop, 10) : 0,
 | 
	
		
			
			|  | 38 | +      bottom: paddingBottom ? parseInt(paddingBottom, 10) : 0,
 | 
	
		
			
			|  | 39 | +      left: paddingLeft ? parseInt(paddingLeft, 10) : 0,
 | 
	
		
			
			|  | 40 | +      right: paddingRight ? parseInt(paddingRight, 10) : 0,
 | 
	
		
			
			|  | 41 | +    };
 | 
	
		
			
			|  | 42 | +
 | 
	
		
			
			|  | 43 | +    console.log('onEnd');
 | 
	
		
			
			|  | 44 | +    // @ts-ignore: missing properties
 | 
	
		
			
			|  | 45 | +    onInsetsChange({ nativeEvent: { insets } });
 | 
	
		
			
			|  | 46 | +  };
 | 
	
		
			
			|  | 47 | +  React.useEffect(() => {
 | 
	
		
			
			|  | 48 | +    console.log(
 | 
	
		
			
			|  | 49 | +      'SUPPORTED_TRANSITION_EVENT',
 | 
	
		
			
			|  | 50 | +      SUPPORTED_TRANSITION_EVENT,
 | 
	
		
			
			|  | 51 | +      SUPPORTED_ENV,
 | 
	
		
			
			|  | 52 | +    );
 | 
	
		
			
			|  | 53 | +    element.addEventListener(SUPPORTED_TRANSITION_EVENT, onEnd);
 | 
	
		
			
			|  | 54 | +    onEnd();
 | 
	
		
			
			|  | 55 | +    return () => {
 | 
	
		
			
			|  | 56 | +      document.body.removeChild(element);
 | 
	
		
			
			|  | 57 | +      element.removeEventListener(SUPPORTED_TRANSITION_EVENT, onEnd);
 | 
	
		
			
			|  | 58 | +    };
 | 
	
		
			
			|  | 59 | +  }, [onInsetsChange]);
 | 
	
		
			
			|  | 60 | +
 | 
	
		
			
			|  | 61 | +  return <View style={style}>{children}</View>;
 | 
	
		
			
			|  | 62 | +}
 | 
	
		
			
			|  | 63 | +
 | 
	
		
			
			|  | 64 | +const SUPPORTED_TRANSITION_EVENT: string = (() => {
 | 
	
		
			
			|  | 65 | +  const element = document.createElement('invalidtype');
 | 
	
		
			
			|  | 66 | +
 | 
	
		
			
			|  | 67 | +  for (const key in CSSTransitions) {
 | 
	
		
			
			|  | 68 | +    if (element.style[key] !== undefined) {
 | 
	
		
			
			|  | 69 | +      return CSSTransitions[key];
 | 
	
		
			
			|  | 70 | +    }
 | 
	
		
			
			|  | 71 | +  }
 | 
	
		
			
			|  | 72 | +  return CSSTransitions.Transition;
 | 
	
		
			
			|  | 73 | +})();
 | 
	
		
			
			|  | 74 | +
 | 
	
		
			
			|  | 75 | +const SUPPORTED_ENV: 'constant' | 'env' = (() => {
 | 
	
		
			
			|  | 76 | +  // @ts-ignore: Property 'CSS' does not exist on type 'Window'.ts(2339)
 | 
	
		
			
			|  | 77 | +  const { CSS } = window;
 | 
	
		
			
			|  | 78 | +  if (
 | 
	
		
			
			|  | 79 | +    CSS &&
 | 
	
		
			
			|  | 80 | +    CSS.supports &&
 | 
	
		
			
			|  | 81 | +    CSS.supports('top: constant(safe-area-inset-top)')
 | 
	
		
			
			|  | 82 | +  ) {
 | 
	
		
			
			|  | 83 | +    return 'constant';
 | 
	
		
			
			|  | 84 | +  }
 | 
	
		
			
			|  | 85 | +  return 'env';
 | 
	
		
			
			|  | 86 | +})();
 | 
	
		
			
			|  | 87 | +
 | 
	
		
			
			|  | 88 | +function getInset(side: string): string {
 | 
	
		
			
			|  | 89 | +  return `${SUPPORTED_ENV}(safe-area-inset-${side})`;
 | 
	
		
			
			|  | 90 | +}
 | 
	
		
			
			|  | 91 | +
 | 
	
		
			
			|  | 92 | +function createContextElement(): HTMLElement {
 | 
	
		
			
			|  | 93 | +  const element = document.createElement('div');
 | 
	
		
			
			|  | 94 | +  const { style } = element;
 | 
	
		
			
			|  | 95 | +  style.position = 'fixed';
 | 
	
		
			
			|  | 96 | +  style.left = '0';
 | 
	
		
			
			|  | 97 | +  style.top = '0';
 | 
	
		
			
			|  | 98 | +  style.width = '0';
 | 
	
		
			
			|  | 99 | +  style.height = '0';
 | 
	
		
			
			|  | 100 | +  style.zIndex = '-1';
 | 
	
		
			
			|  | 101 | +  style.overflow = 'hidden';
 | 
	
		
			
			|  | 102 | +  style.visibility = 'hidden';
 | 
	
		
			
			|  | 103 | +  // Bacon: Anything faster than this and the callback will be invoked too early with the wrong insets
 | 
	
		
			
			|  | 104 | +  style.transitionDuration = '0.05s';
 | 
	
		
			
			|  | 105 | +  style.transitionProperty = 'padding';
 | 
	
		
			
			|  | 106 | +  style.transitionDelay = '0s';
 | 
	
		
			
			|  | 107 | +  style.paddingTop = getInset('top');
 | 
	
		
			
			|  | 108 | +  style.paddingBottom = getInset('bottom');
 | 
	
		
			
			|  | 109 | +  style.paddingLeft = getInset('left');
 | 
	
		
			
			|  | 110 | +  style.paddingRight = getInset('right');
 | 
	
		
			
			|  | 111 | +  return element;
 | 
	
		
			
			|  | 112 | +}
 |