| 
									
										
										
										
											2023-01-03 11:55:46 -08:00
										 |  |  | // Copyright 2021 Signal Messenger, LLC
 | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { ReactElement, ReactNode } from 'react'; | 
					
						
							| 
									
										
										
										
											2022-12-13 17:56:32 -08:00
										 |  |  | import React, { useEffect, useRef, useState } from 'react'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { ContentRect, MeasuredComponentProps } from 'react-measure'; | 
					
						
							|  |  |  | import Measure from 'react-measure'; | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | import classNames from 'classnames'; | 
					
						
							|  |  |  | import { noop } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2021-10-14 12:52:42 -04:00
										 |  |  | import { animated } from '@react-spring/web'; | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { LocalizerType } from '../types/Util'; | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | import { ModalHost } from './ModalHost'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { Theme } from '../util/theme'; | 
					
						
							| 
									
										
										
										
											2022-12-13 17:56:32 -08:00
										 |  |  | import { assertDev } from '../util/assert'; | 
					
						
							| 
									
										
										
										
											2021-05-10 20:50:43 -04:00
										 |  |  | import { getClassNamesFor } from '../util/getClassNamesFor'; | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  | import { useAnimated } from '../hooks/useAnimated'; | 
					
						
							| 
									
										
										
										
											2021-09-17 18:24:21 -04:00
										 |  |  | import { useHasWrapped } from '../hooks/useHasWrapped'; | 
					
						
							| 
									
										
										
										
											2021-10-01 18:53:00 -05:00
										 |  |  | import { useRefMerger } from '../hooks/useRefMerger'; | 
					
						
							| 
									
										
										
										
											2022-12-13 17:56:32 -08:00
										 |  |  | import * as log from '../logging/log'; | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | type PropsType = { | 
					
						
							|  |  |  |   children: ReactNode; | 
					
						
							| 
									
										
										
										
											2022-09-27 13:24:21 -07:00
										 |  |  |   modalName: string; | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   hasXButton?: boolean; | 
					
						
							| 
									
										
										
										
											2022-10-04 17:17:15 -06:00
										 |  |  |   hasHeaderDivider?: boolean; | 
					
						
							|  |  |  |   hasFooterDivider?: boolean; | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   i18n: LocalizerType; | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  |   modalFooter?: JSX.Element; | 
					
						
							| 
									
										
										
										
											2021-04-21 11:31:12 -05:00
										 |  |  |   moduleClassName?: string; | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |   onBackButtonClick?: () => unknown; | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   onClose?: () => void; | 
					
						
							|  |  |  |   title?: ReactNode; | 
					
						
							| 
									
										
										
										
											2022-03-04 16:14:52 -05:00
										 |  |  |   useFocusTrap?: boolean; | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |   padded?: boolean; | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  | export type ModalPropsType = PropsType & { | 
					
						
							| 
									
										
										
										
											2023-04-20 12:31:59 -04:00
										 |  |  |   noTransform?: boolean; | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |   noMouseClose?: boolean; | 
					
						
							| 
									
										
										
										
											2021-04-27 12:29:59 -07:00
										 |  |  |   theme?: Theme; | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 20:50:43 -04:00
										 |  |  | const BASE_CLASS_NAME = 'module-Modal'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | export function Modal({ | 
					
						
							|  |  |  |   children, | 
					
						
							| 
									
										
										
										
											2022-09-27 13:24:21 -07:00
										 |  |  |   modalName, | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   hasXButton, | 
					
						
							|  |  |  |   i18n, | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  |   modalFooter, | 
					
						
							| 
									
										
										
										
											2021-04-21 11:31:12 -05:00
										 |  |  |   moduleClassName, | 
					
						
							| 
									
										
										
										
											2021-05-28 12:15:17 -04:00
										 |  |  |   noMouseClose, | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |   onBackButtonClick, | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   onClose = noop, | 
					
						
							| 
									
										
										
										
											2021-04-27 12:29:59 -07:00
										 |  |  |   theme, | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  |   title, | 
					
						
							| 
									
										
										
										
											2022-03-04 16:14:52 -05:00
										 |  |  |   useFocusTrap, | 
					
						
							| 
									
										
										
										
											2022-10-04 17:17:15 -06:00
										 |  |  |   hasHeaderDivider = false, | 
					
						
							|  |  |  |   hasFooterDivider = false, | 
					
						
							| 
									
										
										
										
											2023-04-20 12:31:59 -04:00
										 |  |  |   noTransform = false, | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |   padded = true, | 
					
						
							| 
									
										
										
										
											2022-12-13 17:56:32 -08:00
										 |  |  | }: Readonly<ModalPropsType>): JSX.Element | null { | 
					
						
							| 
									
										
										
										
											2023-04-20 12:31:59 -04:00
										 |  |  |   const { close, isClosed, modalStyles, overlayStyles } = useAnimated( | 
					
						
							|  |  |  |     onClose, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // `background-position: fixed` cannot properly detect the viewport when
 | 
					
						
							|  |  |  |     // the parent element has `transform: translate*`. Even though it requires
 | 
					
						
							|  |  |  |     // layout recalculation - use `margin-top` if asked by the embedder.
 | 
					
						
							|  |  |  |     noTransform | 
					
						
							|  |  |  |       ? { | 
					
						
							|  |  |  |           getFrom: () => ({ opacity: 0, marginTop: '48px' }), | 
					
						
							|  |  |  |           getTo: isOpen => | 
					
						
							|  |  |  |             isOpen | 
					
						
							|  |  |  |               ? { opacity: 1, marginTop: '0px' } | 
					
						
							|  |  |  |               : { opacity: 0, marginTop: '48px' }, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       : { | 
					
						
							|  |  |  |           getFrom: () => ({ opacity: 0, transform: 'translateY(48px)' }), | 
					
						
							|  |  |  |           getTo: isOpen => | 
					
						
							|  |  |  |             isOpen | 
					
						
							|  |  |  |               ? { opacity: 1, transform: 'translateY(0px)' } | 
					
						
							|  |  |  |               : { opacity: 0, transform: 'translateY(48px)' }, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-13 17:56:32 -08:00
										 |  |  |   useEffect(() => { | 
					
						
							|  |  |  |     if (!isClosed) { | 
					
						
							|  |  |  |       return noop; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const timer = setTimeout(() => { | 
					
						
							|  |  |  |       log.error(`Modal ${modalName} is closed, but still visible`); | 
					
						
							|  |  |  |       assertDev(false, `Invisible modal ${modalName}`); | 
					
						
							|  |  |  |     }, 0); | 
					
						
							|  |  |  |     return () => { | 
					
						
							|  |  |  |       clearTimeout(timer); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }, [modalName, isClosed]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 12:41:36 -08:00
										 |  |  |   if (isClosed) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |   return ( | 
					
						
							| 
									
										
										
										
											2021-10-14 12:52:42 -04:00
										 |  |  |     <ModalHost | 
					
						
							| 
									
										
										
										
											2022-09-27 13:24:21 -07:00
										 |  |  |       modalName={modalName} | 
					
						
							| 
									
										
										
										
											2022-03-04 16:14:52 -05:00
										 |  |  |       moduleClassName={moduleClassName} | 
					
						
							| 
									
										
										
										
											2021-10-14 12:52:42 -04:00
										 |  |  |       noMouseClose={noMouseClose} | 
					
						
							|  |  |  |       onClose={close} | 
					
						
							| 
									
										
										
										
											2023-06-15 22:52:56 +02:00
										 |  |  |       onEscape={onBackButtonClick} | 
					
						
							| 
									
										
										
										
											2021-10-14 12:52:42 -04:00
										 |  |  |       overlayStyles={overlayStyles} | 
					
						
							|  |  |  |       theme={theme} | 
					
						
							| 
									
										
										
										
											2022-03-04 16:14:52 -05:00
										 |  |  |       useFocusTrap={useFocusTrap} | 
					
						
							| 
									
										
										
										
											2021-10-14 12:52:42 -04:00
										 |  |  |     > | 
					
						
							|  |  |  |       <animated.div style={modalStyles}> | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |         <ModalPage | 
					
						
							| 
									
										
										
										
											2022-09-27 13:24:21 -07:00
										 |  |  |           modalName={modalName} | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |           hasXButton={hasXButton} | 
					
						
							|  |  |  |           i18n={i18n} | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  |           modalFooter={modalFooter} | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |           moduleClassName={moduleClassName} | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |           onBackButtonClick={onBackButtonClick} | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |           onClose={close} | 
					
						
							|  |  |  |           title={title} | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |           padded={padded} | 
					
						
							| 
									
										
										
										
											2022-10-04 17:17:15 -06:00
										 |  |  |           hasHeaderDivider={hasHeaderDivider} | 
					
						
							|  |  |  |           hasFooterDivider={hasFooterDivider} | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |         > | 
					
						
							|  |  |  |           {children} | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |         </ModalPage> | 
					
						
							| 
									
										
										
										
											2021-10-14 12:52:42 -04:00
										 |  |  |       </animated.div> | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |     </ModalHost> | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  | type ModalPageProps = Readonly<{ | 
					
						
							|  |  |  |   // should be the one provided by PagedModal
 | 
					
						
							|  |  |  |   onClose: () => void; | 
					
						
							|  |  |  | }> & | 
					
						
							|  |  |  |   Omit<Readonly<PropsType>, 'onClose'>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Represents a single instance (or page) of a modal window. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * It should not be used by itself, either wrap it with PagedModal, | 
					
						
							|  |  |  |  * render it in a component that has PagedModal as an ancestor, or | 
					
						
							|  |  |  |  * use Modal instead. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * It does not provide open/close animation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * NOTE: When used in conjunction with PagedModal (almost always the case): | 
					
						
							|  |  |  |  * onClose" handler should be the one provided by the parent PagedModal, | 
					
						
							|  |  |  |  * not one that has any logic. If you have some logic to execute when the | 
					
						
							|  |  |  |  * modal closes, pass it to PagedModal. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function ModalPage({ | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |   children, | 
					
						
							|  |  |  |   hasXButton, | 
					
						
							|  |  |  |   i18n, | 
					
						
							| 
									
										
										
										
											2022-08-10 14:37:19 -04:00
										 |  |  |   modalFooter, | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |   moduleClassName, | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |   onBackButtonClick, | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |   onClose, | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |   title, | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |   padded = true, | 
					
						
							| 
									
										
										
										
											2022-10-04 17:17:15 -06:00
										 |  |  |   hasHeaderDivider = false, | 
					
						
							|  |  |  |   hasFooterDivider = false, | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  | }: ModalPageProps): JSX.Element { | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  |   const modalRef = useRef<HTMLDivElement | null>(null); | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-01 18:53:00 -05:00
										 |  |  |   const refMerger = useRefMerger(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 11:25:21 -05:00
										 |  |  |   const bodyRef = useRef<HTMLDivElement | null>(null); | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   const [scrolled, setScrolled] = useState(false); | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  |   const [hasOverflow, setHasOverflow] = useState(false); | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |   const hasHeader = Boolean(hasXButton || title || onBackButtonClick); | 
					
						
							| 
									
										
										
										
											2021-05-10 20:50:43 -04:00
										 |  |  |   const getClassName = getClassNamesFor(BASE_CLASS_NAME, moduleClassName); | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  |   function handleResize({ scroll }: ContentRect) { | 
					
						
							|  |  |  |     const modalNode = modalRef?.current; | 
					
						
							|  |  |  |     if (!modalNode) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (scroll) { | 
					
						
							|  |  |  |       setHasOverflow(scroll.height > modalNode.clientHeight); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   return ( | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |     <> | 
					
						
							| 
									
										
										
										
											2021-09-02 12:35:23 -07:00
										 |  |  |       {/* We don't want the click event to propagate to its container node. */} | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |       {/* eslint-disable-next-line max-len */} | 
					
						
							|  |  |  |       {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */} | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |       <div | 
					
						
							|  |  |  |         className={classNames( | 
					
						
							| 
									
										
										
										
											2021-05-10 20:50:43 -04:00
										 |  |  |           getClassName(''), | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  |           getClassName(hasHeader ? '--has-header' : '--no-header'), | 
					
						
							| 
									
										
										
										
											2022-10-04 17:17:15 -06:00
										 |  |  |           Boolean(modalFooter) && getClassName('--has-footer'), | 
					
						
							|  |  |  |           padded && getClassName('--padded'), | 
					
						
							|  |  |  |           hasHeaderDivider && getClassName('--header-divider'), | 
					
						
							|  |  |  |           hasFooterDivider && getClassName('--footer-divider') | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |         )} | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  |         ref={modalRef} | 
					
						
							| 
									
										
										
										
											2021-09-02 12:35:23 -07:00
										 |  |  |         onClick={event => { | 
					
						
							|  |  |  |           event.stopPropagation(); | 
					
						
							|  |  |  |         }} | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |       > | 
					
						
							|  |  |  |         {hasHeader && ( | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |           <div | 
					
						
							|  |  |  |             className={classNames( | 
					
						
							|  |  |  |               getClassName('__header'), | 
					
						
							|  |  |  |               onBackButtonClick | 
					
						
							|  |  |  |                 ? getClassName('__header--with-back-button') | 
					
						
							|  |  |  |                 : null | 
					
						
							|  |  |  |             )} | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             {onBackButtonClick && ( | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |               <button | 
					
						
							| 
									
										
										
										
											2023-03-29 17:03:25 -07:00
										 |  |  |                 aria-label={i18n('icu:back')} | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |                 className={getClassName('__back-button')} | 
					
						
							|  |  |  |                 onClick={onBackButtonClick} | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |                 tabIndex={0} | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |                 type="button" | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |               /> | 
					
						
							|  |  |  |             )} | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |             {title && ( | 
					
						
							|  |  |  |               <h1 | 
					
						
							|  |  |  |                 className={classNames( | 
					
						
							| 
									
										
										
										
											2021-05-10 20:50:43 -04:00
										 |  |  |                   getClassName('__title'), | 
					
						
							|  |  |  |                   hasXButton ? getClassName('__title--with-x-button') : null | 
					
						
							| 
									
										
										
										
											2021-05-05 17:09:29 -07:00
										 |  |  |                 )} | 
					
						
							|  |  |  |               > | 
					
						
							|  |  |  |                 {title} | 
					
						
							|  |  |  |               </h1> | 
					
						
							|  |  |  |             )} | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |             {hasXButton && !title && ( | 
					
						
							|  |  |  |               <div className={getClassName('__title')} /> | 
					
						
							|  |  |  |             )} | 
					
						
							|  |  |  |             {hasXButton && ( | 
					
						
							|  |  |  |               <button | 
					
						
							| 
									
										
										
										
											2023-03-29 17:03:25 -07:00
										 |  |  |                 aria-label={i18n('icu:close')} | 
					
						
							| 
									
										
										
										
											2022-07-20 20:07:09 -04:00
										 |  |  |                 className={getClassName('__close-button')} | 
					
						
							|  |  |  |                 onClick={onClose} | 
					
						
							|  |  |  |                 tabIndex={0} | 
					
						
							|  |  |  |                 type="button" | 
					
						
							|  |  |  |               /> | 
					
						
							|  |  |  |             )} | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |           </div> | 
					
						
							|  |  |  |         )} | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  |         <Measure scroll onResize={handleResize}> | 
					
						
							|  |  |  |           {({ measureRef }: MeasuredComponentProps) => ( | 
					
						
							|  |  |  |             <div | 
					
						
							|  |  |  |               className={classNames( | 
					
						
							|  |  |  |                 getClassName('__body'), | 
					
						
							|  |  |  |                 scrolled ? getClassName('__body--scrolled') : null, | 
					
						
							|  |  |  |                 hasOverflow || scrolled | 
					
						
							|  |  |  |                   ? getClassName('__body--overflow') | 
					
						
							|  |  |  |                   : null | 
					
						
							|  |  |  |               )} | 
					
						
							| 
									
										
										
										
											2021-09-21 11:25:21 -05:00
										 |  |  |               onScroll={() => { | 
					
						
							|  |  |  |                 const scrollTop = bodyRef.current?.scrollTop || 0; | 
					
						
							|  |  |  |                 setScrolled(scrollTop > 2); | 
					
						
							|  |  |  |               }} | 
					
						
							| 
									
										
										
										
											2021-10-01 18:53:00 -05:00
										 |  |  |               ref={refMerger(measureRef, bodyRef)} | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  |             > | 
					
						
							|  |  |  |               {children} | 
					
						
							|  |  |  |             </div> | 
					
						
							| 
									
										
										
										
											2021-05-10 20:50:43 -04:00
										 |  |  |           )} | 
					
						
							| 
									
										
										
										
											2021-08-05 20:17:05 -04:00
										 |  |  |         </Measure> | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |         {modalFooter && <Modal.ButtonFooter>{modalFooter}</Modal.ButtonFooter>} | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |       </div> | 
					
						
							| 
									
										
										
										
											2021-09-29 16:59:37 -04:00
										 |  |  |     </> | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-17 16:45:19 -08:00
										 |  |  | function ButtonFooter({ | 
					
						
							| 
									
										
										
										
											2021-04-13 09:20:02 -05:00
										 |  |  |   children, | 
					
						
							| 
									
										
										
										
											2021-05-10 20:50:43 -04:00
										 |  |  | }: Readonly<{ | 
					
						
							|  |  |  |   children: ReactNode; | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  | }>): ReactElement { | 
					
						
							|  |  |  |   const [ref, hasWrapped] = useHasWrapped<HTMLDivElement>(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |   const className = getClassNamesFor(BASE_CLASS_NAME)('__button-footer'); | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <div | 
					
						
							|  |  |  |       className={classNames( | 
					
						
							|  |  |  |         className, | 
					
						
							|  |  |  |         hasWrapped ? `${className}--one-button-per-line` : undefined | 
					
						
							|  |  |  |       )} | 
					
						
							|  |  |  |       ref={ref} | 
					
						
							|  |  |  |     > | 
					
						
							|  |  |  |       {children} | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2022-11-17 16:45:19 -08:00
										 |  |  | } | 
					
						
							|  |  |  | Modal.ButtonFooter = ButtonFooter; | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | type PagedModalProps = Readonly<{ | 
					
						
							|  |  |  |   modalName: string; | 
					
						
							|  |  |  |   children: RenderModalPage; | 
					
						
							|  |  |  |   moduleClassName?: string; | 
					
						
							|  |  |  |   onClose?: () => void; | 
					
						
							|  |  |  |   useFocusTrap?: boolean; | 
					
						
							|  |  |  |   noMouseClose?: boolean; | 
					
						
							|  |  |  |   theme?: Theme; | 
					
						
							|  |  |  | }>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Provides modal animation and click to close functionality to a | 
					
						
							|  |  |  |  * ModalPage descendant. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Useful when we want to swap between different ModalPages (possibly | 
					
						
							|  |  |  |  * rendered by different components) without triggering an open/close | 
					
						
							|  |  |  |  * transition animation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function PagedModal({ | 
					
						
							|  |  |  |   modalName, | 
					
						
							|  |  |  |   children, | 
					
						
							|  |  |  |   moduleClassName, | 
					
						
							|  |  |  |   noMouseClose, | 
					
						
							|  |  |  |   onClose = noop, | 
					
						
							|  |  |  |   theme, | 
					
						
							|  |  |  |   useFocusTrap, | 
					
						
							| 
									
										
										
										
											2022-12-13 17:56:32 -08:00
										 |  |  | }: PagedModalProps): JSX.Element | null { | 
					
						
							|  |  |  |   const { close, isClosed, modalStyles, overlayStyles } = useAnimated(onClose, { | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |     getFrom: () => ({ opacity: 0, transform: 'translateY(48px)' }), | 
					
						
							|  |  |  |     getTo: isOpen => | 
					
						
							|  |  |  |       isOpen | 
					
						
							|  |  |  |         ? { opacity: 1, transform: 'translateY(0px)' } | 
					
						
							|  |  |  |         : { opacity: 0, transform: 'translateY(48px)' }, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-13 17:56:32 -08:00
										 |  |  |   useEffect(() => { | 
					
						
							|  |  |  |     if (!isClosed) { | 
					
						
							|  |  |  |       return noop; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const timer = setTimeout(() => { | 
					
						
							|  |  |  |       log.error(`PagedModal ${modalName} is closed, but still visible`); | 
					
						
							|  |  |  |       assertDev(false, `Invisible paged modal ${modalName}`); | 
					
						
							|  |  |  |     }, 0); | 
					
						
							|  |  |  |     return () => { | 
					
						
							|  |  |  |       clearTimeout(timer); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }, [modalName, isClosed]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (isClosed) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 16:40:09 -06:00
										 |  |  |   return ( | 
					
						
							|  |  |  |     <ModalHost | 
					
						
							|  |  |  |       modalName={modalName} | 
					
						
							|  |  |  |       moduleClassName={moduleClassName} | 
					
						
							|  |  |  |       noMouseClose={noMouseClose} | 
					
						
							|  |  |  |       onClose={close} | 
					
						
							|  |  |  |       overlayStyles={overlayStyles} | 
					
						
							|  |  |  |       theme={theme} | 
					
						
							|  |  |  |       useFocusTrap={useFocusTrap} | 
					
						
							|  |  |  |     > | 
					
						
							|  |  |  |       <animated.div style={modalStyles}>{children(close)}</animated.div> | 
					
						
							|  |  |  |     </ModalHost> | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export type RenderModalPage = (onClose: () => void) => JSX.Element; |