您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

BaseDialog.tsx 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import React, { ReactNode, useCallback, useContext, useEffect } from 'react';
  2. import FocusLock from 'react-focus-lock';
  3. import { useTranslation } from 'react-i18next';
  4. import { keyframes } from 'tss-react';
  5. import { makeStyles } from 'tss-react/mui';
  6. import { withPixelLineHeight } from '../../../styles/functions.web';
  7. import { DialogTransitionContext } from './DialogTransition';
  8. const useStyles = makeStyles()(theme => {
  9. return {
  10. container: {
  11. width: '100%',
  12. height: '100%',
  13. position: 'fixed',
  14. color: theme.palette.text01,
  15. ...withPixelLineHeight(theme.typography.bodyLongRegular),
  16. top: 0,
  17. left: 0,
  18. display: 'flex',
  19. justifyContent: 'center',
  20. alignItems: 'flex-start',
  21. zIndex: 301,
  22. animation: `${keyframes`
  23. 0% {
  24. opacity: 0.4;
  25. }
  26. 100% {
  27. opacity: 1;
  28. }
  29. `} 0.2s forwards ease-out`,
  30. '&.unmount': {
  31. animation: `${keyframes`
  32. 0% {
  33. opacity: 1;
  34. }
  35. 100% {
  36. opacity: 0.5;
  37. }
  38. `} 0.15s forwards ease-in`
  39. }
  40. },
  41. backdrop: {
  42. position: 'absolute',
  43. width: '100%',
  44. height: '100%',
  45. top: 0,
  46. left: 0,
  47. backgroundColor: theme.palette.ui02,
  48. opacity: 0.75
  49. },
  50. modal: {
  51. backgroundColor: theme.palette.ui01,
  52. border: `1px solid ${theme.palette.ui03}`,
  53. boxShadow: '0px 4px 25px 4px rgba(20, 20, 20, 0.6)',
  54. borderRadius: `${theme.shape.borderRadius}px`,
  55. display: 'flex',
  56. flexDirection: 'column',
  57. height: 'auto',
  58. minHeight: '200px',
  59. maxHeight: '80vh',
  60. marginTop: '64px',
  61. animation: `${keyframes`
  62. 0% {
  63. margin-top: 85px
  64. }
  65. 100% {
  66. margin-top: 64px
  67. }
  68. `} 0.2s forwards ease-out`,
  69. '&.medium': {
  70. width: '400px'
  71. },
  72. '&.large': {
  73. width: '664px'
  74. },
  75. '&.unmount': {
  76. animation: `${keyframes`
  77. 0% {
  78. margin-top: 64px
  79. }
  80. 100% {
  81. margin-top: 40px
  82. }
  83. `} 0.15s forwards ease-in`
  84. },
  85. '@media (max-width: 448px)': {
  86. width: '100% !important',
  87. maxHeight: 'initial',
  88. height: '100%',
  89. margin: 0,
  90. position: 'absolute',
  91. top: 0,
  92. left: 0,
  93. bottom: 0,
  94. animation: `${keyframes`
  95. 0% {
  96. margin-top: 15px
  97. }
  98. 100% {
  99. margin-top: 0
  100. }
  101. `} 0.2s forwards ease-out`,
  102. '&.unmount': {
  103. animation: `${keyframes`
  104. 0% {
  105. margin-top: 0
  106. }
  107. 100% {
  108. margin-top: 15px
  109. }
  110. `} 0.15s forwards ease-in`
  111. }
  112. }
  113. },
  114. focusLock: {
  115. zIndex: 1
  116. }
  117. };
  118. });
  119. export interface IProps {
  120. children?: ReactNode;
  121. className?: string;
  122. description?: string;
  123. disableBackdropClose?: boolean;
  124. disableEnter?: boolean;
  125. onClose?: () => void;
  126. size?: 'large' | 'medium';
  127. submit?: () => void;
  128. title?: string;
  129. titleKey?: string;
  130. }
  131. const BaseDialog = ({
  132. children,
  133. className,
  134. description,
  135. disableBackdropClose,
  136. disableEnter,
  137. onClose,
  138. size = 'medium',
  139. submit,
  140. title,
  141. titleKey
  142. }: IProps) => {
  143. const { classes, cx } = useStyles();
  144. const { isUnmounting } = useContext(DialogTransitionContext);
  145. const { t } = useTranslation();
  146. const onBackdropClick = useCallback(() => {
  147. !disableBackdropClose && onClose?.();
  148. }, [ disableBackdropClose, onClose ]);
  149. const handleKeyDown = useCallback((e: KeyboardEvent) => {
  150. if (e.key === 'Escape') {
  151. onClose?.();
  152. }
  153. if (e.key === 'Enter' && !disableEnter) {
  154. submit?.();
  155. }
  156. }, []);
  157. useEffect(() => {
  158. window.addEventListener('keydown', handleKeyDown);
  159. return () => window.removeEventListener('keydown', handleKeyDown);
  160. }, []);
  161. return (
  162. <div className = { cx(classes.container, isUnmounting && 'unmount') }>
  163. <div
  164. className = { classes.backdrop }
  165. onClick = { onBackdropClick } />
  166. <FocusLock
  167. className = { classes.focusLock }
  168. returnFocus = { true }>
  169. <div
  170. aria-describedby = { description }
  171. aria-labelledby = { title ?? t(titleKey ?? '') }
  172. aria-modal = { true }
  173. className = { cx(classes.modal, isUnmounting && 'unmount', size, className) }
  174. role = 'dialog'>
  175. {children}
  176. </div>
  177. </FocusLock>
  178. </div>
  179. );
  180. };
  181. export default BaseDialog;