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