Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | 1x 1x 1x 1x 1x 1x 9x 1x 1x 5x 5x 5x 5x 5x 5x 4x 4x 4x 5x 5x 5x 30x | import { Close as CloseIcon } from '@mui/icons-material';
import {
AppBar,
Box,
Container,
Dialog,
IconButton,
Slide,
SlideProps,
Toolbar,
Typography,
useMediaQuery,
useTheme,
} from '@mui/material';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { meta } from '@/constants';
export type PrivacyPolicyDialogProps = {
open: boolean;
closeAction: { (): void };
};
export type PrivacyPolicyDialogRef = {
scrollTop: () => void;
};
const Transition = React.forwardRef(function _(props: SlideProps & { children: React.ReactElement }, ref) {
return <Slide ref={ref} direction="up" {...props} />;
});
const titleKeys = ['acquisition', 'purpose', 'thirdParty', 'analysis', 'disclaimer', 'copyright'] as const;
export const PrivacyPolicyDialog = React.memo(function _(props: PrivacyPolicyDialogProps) {
const dialogRef = React.useRef<HTMLDivElement>(null);
const theme = useTheme();
const ltSm = useMediaQuery(theme.breakpoints.down('sm'));
const [t] = useTranslation();
const handleClose = React.useCallback(() => props.closeAction && props.closeAction(), [props]);
const scrollTop = React.useCallback(() => {
// ダイアログを開く際にスクロール位置を先頭に戻す
const element = dialogRef.current?.getElementsByClassName('MuiDialog-paperScrollPaper')[0] as Element;
if (element) element.scrollTop = 0;
/* istanbul ignore if */
if (process.env.NODE_ENV === 'test') console.debug('ScrollTop Completed.');
}, []);
React.useEffect(() => {
if (props.open) scrollTop();
}, [props.open, scrollTop]);
return (
<Dialog
ref={dialogRef}
open={props.open}
onClose={handleClose}
TransitionComponent={Transition}
keepMounted
maxWidth={false}
fullWidth={!ltSm}
fullScreen={ltSm}
sx={ltSm ? { maxHeight: '85vh', mt: '15vh' } : null} // モバイル端末時は Bottom Sheet 風の表示にする
style={{ whiteSpace: 'pre-wrap' }} // 空白と改行を反映させる
>
<AppBar sx={{ position: 'sticky' }}>
<Container maxWidth="xl">
<Toolbar sx={{ justifyContent: 'space-between' }} disableGutters>
<Typography variant="h6" data-testid="PrivacyPolicyDialog__Title">
{t('privacyPolicy.title')}
</Typography>
<IconButton onClick={handleClose} data-testid="PrivacyPolicyDialog__Close">
<CloseIcon />
</IconButton>
</Toolbar>
</Container>
</AppBar>
<Container sx={{ pb: 10 }} data-testid="PrivacyPolicyDialog__Body">
<Box sx={{ my: 5 }}>
<Typography variant="subtitle1">{t('privacyPolicy.summary', { title: meta.title })}</Typography>
</Box>
{titleKeys.map((key) => (
<Box key={`PrivacyPolicyDialog__Body--${key}`} sx={{ my: 5 }}>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>
{t('privacyPolicy.title', { context: key })}
</Typography>
<Typography variant="body1" sx={{ color: 'text.secondary', mt: 2 }}>
{t('privacyPolicy.body', { context: key })}
</Typography>
</Box>
))}
</Container>
</Dialog>
);
});
|