98 lines
		
	
	
	
		
			2.2 KiB
			
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			98 lines
		
	
	
	
		
			2.2 KiB
			
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
// Copyright 2021 Signal Messenger, LLC
 | 
						|
// SPDX-License-Identifier: AGPL-3.0-only
 | 
						|
 | 
						|
import { useState, useCallback } from 'react';
 | 
						|
import type { SpringValues } from '@react-spring/web';
 | 
						|
import { useChain, useSpring, useSpringRef } from '@react-spring/web';
 | 
						|
import { useReducedMotion } from './useReducedMotion';
 | 
						|
 | 
						|
export type ModalConfigType = {
 | 
						|
  opacity: number;
 | 
						|
  transform?: string;
 | 
						|
  marginTop?: string;
 | 
						|
};
 | 
						|
 | 
						|
enum ModalState {
 | 
						|
  Opening = 'Opening',
 | 
						|
  Open = 'Open',
 | 
						|
  Closing = 'Closing',
 | 
						|
  Closed = 'Closed',
 | 
						|
}
 | 
						|
 | 
						|
export function useAnimated(
 | 
						|
  onClose: () => unknown,
 | 
						|
  {
 | 
						|
    getFrom,
 | 
						|
    getTo,
 | 
						|
  }: {
 | 
						|
    getFrom: (isOpen: boolean) => ModalConfigType;
 | 
						|
    getTo: (isOpen: boolean) => ModalConfigType;
 | 
						|
  }
 | 
						|
): {
 | 
						|
  close: () => unknown;
 | 
						|
  isClosed: boolean;
 | 
						|
  modalStyles: SpringValues<ModalConfigType>;
 | 
						|
  overlayStyles: SpringValues<ModalConfigType>;
 | 
						|
} {
 | 
						|
  const reducedMotion = useReducedMotion();
 | 
						|
  const [state, setState] = useState(ModalState.Opening);
 | 
						|
  const shouldShowModal =
 | 
						|
    state === ModalState.Open || state === ModalState.Opening;
 | 
						|
  const isClosed = state === ModalState.Closed;
 | 
						|
 | 
						|
  const modalRef = useSpringRef();
 | 
						|
 | 
						|
  const modalStyles = useSpring({
 | 
						|
    immediate: reducedMotion,
 | 
						|
    from: getFrom(shouldShowModal),
 | 
						|
    to: getTo(shouldShowModal),
 | 
						|
    onRest: () => {
 | 
						|
      if (state === ModalState.Closing) {
 | 
						|
        setState(ModalState.Closed);
 | 
						|
        onClose();
 | 
						|
      } else if (state === ModalState.Opening) {
 | 
						|
        setState(ModalState.Open);
 | 
						|
      }
 | 
						|
    },
 | 
						|
    config: {
 | 
						|
      clamp: true,
 | 
						|
      friction: 20,
 | 
						|
      mass: 0.5,
 | 
						|
      tension: 350,
 | 
						|
    },
 | 
						|
    ref: modalRef,
 | 
						|
  });
 | 
						|
 | 
						|
  const overlayRef = useSpringRef();
 | 
						|
 | 
						|
  const overlayStyles = useSpring({
 | 
						|
    from: { opacity: 0 },
 | 
						|
    to: { opacity: shouldShowModal ? 1 : 0 },
 | 
						|
    config: {
 | 
						|
      clamp: true,
 | 
						|
      friction: 22,
 | 
						|
      tension: 360,
 | 
						|
    },
 | 
						|
    ref: overlayRef,
 | 
						|
  });
 | 
						|
 | 
						|
  useChain(shouldShowModal ? [overlayRef, modalRef] : [modalRef, overlayRef]);
 | 
						|
  const close = useCallback(() => {
 | 
						|
    setState(currentState => {
 | 
						|
      if (
 | 
						|
        currentState === ModalState.Open ||
 | 
						|
        currentState === ModalState.Opening
 | 
						|
      ) {
 | 
						|
        return ModalState.Closing;
 | 
						|
      }
 | 
						|
      return currentState;
 | 
						|
    });
 | 
						|
  }, []);
 | 
						|
 | 
						|
  return {
 | 
						|
    close,
 | 
						|
    isClosed,
 | 
						|
    overlayStyles,
 | 
						|
    modalStyles,
 | 
						|
  };
 | 
						|
}
 |