modal 반영 1차
parent
462017bfdd
commit
c12d4b49ce
@ -0,0 +1,40 @@
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Modal from '@mui/material/Modal';
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
border: '2px solid #000',
|
||||
boxShadow: 24,
|
||||
p: 4
|
||||
};
|
||||
|
||||
export default function BasicModal() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const handleOpen = () => setOpen(true);
|
||||
const handleClose = () => setOpen(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={handleOpen}>Open BasicModal</Button>
|
||||
<Modal hideBackdrop open={open} onClose={handleClose} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description">
|
||||
<Box sx={style}>
|
||||
<Typography id="modal-modal-title" variant="h6" component="h2">
|
||||
Text in a modal
|
||||
</Typography>
|
||||
<Typography id="modal-modal-description" sx={{ mt: 2 }}>
|
||||
Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
|
||||
</Typography>
|
||||
<Button onClick={handleClose}>Close Modal</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import { useState } from 'react';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import { Button } from '@mui/material';
|
||||
import DraggableResizeModal from './DraggableResizeModal';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
modal: {
|
||||
'& > *': {
|
||||
margin: theme.spacing(1)
|
||||
},
|
||||
padding: theme.spacing(2)
|
||||
}
|
||||
}));
|
||||
|
||||
function DraggableModal() {
|
||||
const classes = useStyles();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [resizeOpen, setResizeOpen] = useState(false);
|
||||
|
||||
const handleOpenToggle = (evt) => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
const handleOpenResizeToggle = (evt) => {
|
||||
setResizeOpen(!resizeOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.modal}>
|
||||
<Button variant="outlined" color="primary" onClick={handleOpenToggle}>
|
||||
Open Draggable Modal
|
||||
</Button>
|
||||
<Button variant="outlined" color="primary" onClick={handleOpenResizeToggle}>
|
||||
Open Draggable(+resize) Modal
|
||||
</Button>
|
||||
<DraggableResizeModal title="모달 테스트1" open={open} width={450} height={450} onClose={handleOpenToggle}>
|
||||
test1
|
||||
</DraggableResizeModal>
|
||||
<DraggableResizeModal title="모달 테스트2" open={resizeOpen} isResize width={450} height={450} onClose={handleOpenResizeToggle}>
|
||||
test2
|
||||
</DraggableResizeModal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DraggableModal;
|
@ -0,0 +1,164 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Paper, makeStyles, withStyles, IconButton, Typography, Divider } from '@mui/material';
|
||||
|
||||
import { Close as CloseIcon, Remove as RemoveIcon, WebAsset as WebAssetIcon } from '@mui/icons-material';
|
||||
|
||||
import Draggable from 'react-draggable';
|
||||
import { ResizableBox } from 'react-resizable';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const styles = (theme) => ({
|
||||
modal: {
|
||||
margin: 0,
|
||||
padding: theme.spacing(2),
|
||||
cursor: 'move',
|
||||
userSelect: 'none',
|
||||
minWidth: 200
|
||||
},
|
||||
title: {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
closeButton: {
|
||||
position: 'fixed',
|
||||
right: theme.spacing(1),
|
||||
top: theme.spacing(1),
|
||||
color: theme.palette.grey[500]
|
||||
},
|
||||
minimize: {
|
||||
position: 'fixed',
|
||||
right: theme.spacing(6),
|
||||
top: theme.spacing(1),
|
||||
color: theme.palette.grey[500]
|
||||
}
|
||||
});
|
||||
|
||||
// modal 의 타이틀과 최소화 및 닫기버튼 구성
|
||||
const ModalTitle = withStyles(styles)((props) => {
|
||||
const { children, classes, width, isMinimized, onMinimized, onClose, ...other } = props;
|
||||
return (
|
||||
<div className={classes.modal} {...other} style={{ width }}>
|
||||
<Typography variant="h6" className={classes.title}>
|
||||
{children}
|
||||
</Typography>
|
||||
{onClose ? (
|
||||
<IconButton aria-label="close" className={classes.closeButton} onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
) : null}
|
||||
{onMinimized ? (
|
||||
<IconButton aria-label="close" className={classes.minimize} onClick={onMinimized}>
|
||||
{isMinimized ? <WebAssetIcon /> : <RemoveIcon />}
|
||||
</IconButton>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const useContentStyles = (width, height) =>
|
||||
makeStyles((theme) => ({
|
||||
resizable: {
|
||||
padding: theme.spacing(2),
|
||||
position: 'relative',
|
||||
'& .react-resizable-handle': {
|
||||
position: 'absolute',
|
||||
userSelect: 'none',
|
||||
width: 20,
|
||||
height: 20,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
background:
|
||||
"url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+')",
|
||||
'background-position': 'bottom right',
|
||||
padding: '0 3px 3px 0',
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-origin': 'content-box',
|
||||
'box-sizing': 'border-box',
|
||||
cursor: 'se-resize'
|
||||
}
|
||||
},
|
||||
content: {
|
||||
padding: theme.spacing(2),
|
||||
maxHeight: height,
|
||||
maxWidth: width
|
||||
}
|
||||
}));
|
||||
|
||||
// modal의 content 영역으로 isResize 여부에 따라 다른 컴포넌트를 사용
|
||||
const ModalContent = ({ width, height, isResize, children }) => {
|
||||
const classes = useContentStyles(width, height)();
|
||||
return (
|
||||
<>
|
||||
{isResize ? (
|
||||
<ResizableBox height={height} width={width} className={classes.resizable}>
|
||||
{children}
|
||||
</ResizableBox>
|
||||
) : (
|
||||
<Paper className={classes.content}>{children}</Paper>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const PaperComponent = (props) => (
|
||||
<Draggable handle="#draggable-modal-title">
|
||||
<Paper {...props} />
|
||||
</Draggable>
|
||||
);
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
modal: {
|
||||
position: 'fixed',
|
||||
top: '10%',
|
||||
left: '10%',
|
||||
zIndex: 1300,
|
||||
userSelect: 'none'
|
||||
}
|
||||
}));
|
||||
|
||||
const Modal = ({ title, children, width, height, isResize, onClose }) => {
|
||||
const classes = useStyles();
|
||||
const [isMinimized, setIsMinimized] = useState(false);
|
||||
|
||||
const handleMinimized = (evt) => {
|
||||
setIsMinimized(!isMinimized);
|
||||
};
|
||||
|
||||
useEffect(() => () => {}, []);
|
||||
|
||||
return (
|
||||
<PaperComponent className={classes.modal}>
|
||||
<ModalTitle id="draggable-modal-title" onClose={onClose} width={width} isMinimized={isMinimized} onMinimized={handleMinimized}>
|
||||
{title}
|
||||
</ModalTitle>
|
||||
<Divider />
|
||||
{!isMinimized && (
|
||||
<ModalContent width={width} height={height} isResize={isResize}>
|
||||
{children}
|
||||
</ModalContent>
|
||||
)}
|
||||
</PaperComponent>
|
||||
);
|
||||
};
|
||||
|
||||
// open 여부에 따라 mount 및 unmount 처리
|
||||
const DraggableResizeModal = ({ open, ...other }) => <>{open && <Modal {...other} />}</>;
|
||||
|
||||
// property 의 기본값 셋팅
|
||||
DraggableResizeModal.defaultProps = {
|
||||
title: '목록',
|
||||
isResize: false,
|
||||
width: 500,
|
||||
height: 500
|
||||
};
|
||||
|
||||
// property 의 타입 지정
|
||||
DraggableResizeModal.propTypes = {
|
||||
title: PropTypes.string,
|
||||
open: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func,
|
||||
isResize: PropTypes.bool,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number
|
||||
};
|
||||
|
||||
export default DraggableResizeModal;
|
@ -0,0 +1,44 @@
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Modal from '@mui/material/Modal';
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
border: '2px solid #000',
|
||||
boxShadow: 24,
|
||||
p: 4
|
||||
};
|
||||
|
||||
export default function GridModal({ children }) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const handleOpen = () => setOpen(true);
|
||||
const handleClose = () => setOpen(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={handleOpen}>Grid Modal(List)</Button>
|
||||
<Modal hideBackdrop open={open} onClose={handleClose} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description">
|
||||
<Box sx={style}>
|
||||
<Typography id="modal-modal-title" variant="h2" component="h2">
|
||||
Text in a modal
|
||||
</Typography>
|
||||
{children}
|
||||
<Button onClick={handleClose}>Close Modal</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
GridModal.propTypes = {
|
||||
children: PropTypes.node
|
||||
};
|
@ -0,0 +1,82 @@
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import Modal from '@mui/material/Modal';
|
||||
import Button from '@mui/material/Button';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { IconButton } from '@mui/material';
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
border: '2px solid #000',
|
||||
boxShadow: 24,
|
||||
pt: 2,
|
||||
px: 4,
|
||||
pb: 3
|
||||
};
|
||||
|
||||
function ChildModal() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={handleOpen}>Open Child Modal</Button>
|
||||
<Modal hideBackdrop open={open} onClose={handleClose} aria-labelledby="child-modal-title" aria-describedby="child-modal-description">
|
||||
<Box sx={{ ...style, width: 200 }}>
|
||||
<h2 id="child-modal-title">Text in a child modal</h2>
|
||||
<p id="child-modal-description">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</p>
|
||||
<Button onClick={handleClose}>Close Child Modal</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function NestedModal() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
tabIndex={-1}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
width: { xs: 280, lg: 450 },
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)'
|
||||
}}
|
||||
>
|
||||
<Button onClick={handleOpen}>Open Nested modal</Button>
|
||||
<Modal
|
||||
hideBackdrop
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="parent-modal-title"
|
||||
aria-describedby="parent-modal-description"
|
||||
>
|
||||
<Box content sx={{ ...style, width: 400 }}>
|
||||
<h2 id="parent-modal-title">Text in a modal</h2>
|
||||
<p id="parent-modal-description">Duis mollis, est non commodo luctus, nisi erat porttitor ligula.</p>
|
||||
<ChildModal />
|
||||
<Button onClick={handleClose}>Close Modal</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue