기획:
1. figma 작성
- 기본 틀만 그림
- 기본 글자만 적음
- 1차적으로 모바일만
- 데스크탑은 모바일에서 조금 변형해서 구현 예정 (responsive)
2. 이미지 설명:
- title이 있음
- 각 각 input field
- 결과 보기는 button
- 전용 면적은 select bar. drag 시 평으로 자동 변환해서 옆에 적어줌 (modal)
- 층수, 건축년도는 select box (modal)
- 결과 보기 누르면 아래에 결과가 뜸
구현 설계:
- 구현 과정 나열:
1. create-react-app
2. title mui
3. textfield mui
4. react-daum-postcode 적용: 팝업 띄워서 주소 textfield 로 동까지 결과 넣어줄 예정
5. 결과 보기 button
6. 결과 보기 버튼 누를 시 현상 (part 2): part 2 에서 자세히 이어보겠습니다
구현 결과:
- 화면 스크린샷:
- 소스 코드:
import './App.css';
import React, {useState} from 'react';
import Grid from '@mui/material/Grid';
import TextField from '@mui/material/TextField';
import Box from '@mui/material/Box';
import Slider from '@mui/material/Slider';
import Button from '@mui/material/Button';
import Select from '@mui/material/Select';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { Typography, Container } from '@mui/material';
import Modal from '@mui/material/Modal';
import { useDaumPostcodePopup } from 'react-daum-postcode';
const MyTitle = () => {
return (
<Typography
variant="h3"
color="secondary"
align="center"
>
내 빌라의 적정 월세는?
</Typography>
);
};
const Footer = () => {
return (
<Box sx={{
position: 'fixed',
bottom: 0,
width: '100%',
backgroundColor: '#f0f0f0',
padding: '1rem 0',
}}>
<Container maxWidth="lg">
<Typography variant="body1" align="center">
Copyright © {new Date().getFullYear()} 엘앤에스프로퍼티
</Typography>
</Container>
</Box>
);
};
const SelectBox = (props) => {
const handleChange = (event) => {
props.setValue(event.target.value);
};
return (
<>
<FormControl sx={{ width: "100%"}}>
<InputLabel id="demo-simple-select-label">{props.use}</InputLabel>
<Select
variant="outlined"
labelId="demo-simple-select-label"
label={props.use}
value={props.value}
onChange={handleChange}
>
{props.myArray.map((item, index) => (
<MenuItem value={item.value}>{item.label}</MenuItem>
))}
</Select>
</FormControl>
</>
);
};
const DepositSliderModal = (props) => {
const handleChange = (event, newValue) => {
props.setValue(newValue);
};
const handleClose = () => {
props.setOpen(false);
};
return (
<Modal open={props.open} onClose={handleClose}>
<Box
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '80%',
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
}}
>
<p id="slider-in-modal">{props.use}</p>
<Slider
defaultValue={props.value}
step={500}
marks
min={0}
max={10000}
value={props.value}
onChange={handleChange}
aria-label={props.use}
color="secondary"
valueLabelDisplay="on"
/>
<Button onClick={handleClose}>닫기</Button>
</Box>
</Modal>
);
};
const CustomSliderModal = (props) => {
const [peong, setPeong] = useState(0);
const handleChange = (event, newValue) => {
props.setValue(newValue);
setPeong((newValue / 3.305785).toFixed(1));
};
const handleClose = () => {
props.setOpen(false);
};
return (
<Modal open={props.open} onClose={handleClose}>
<Box
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '80%',
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 25,
p: 4,
}}
>
<Typography>
{props.use}
</Typography>
<Slider
defaultValue={props.value}
min={0}
max={100}
value={props.value}
onChange={handleChange}
aria-label={props.use}
color="secondary"
valueLabelDisplay="on"
/>
<Typography align="center">
{peong} 평
</Typography>
<Button onClick={handleClose}>닫기</Button>
</Box>
</Modal>
);
};
const MyInputField = (props) => {
const postcodeOpen = useDaumPostcodePopup();
const [open, setOpen] = useState(false);
const [depositOpen, setDepositOpen] = useState(false);
const handleComplete = (data) => {
let fullAddress = data.address;
let extraAddress = '';
if (data.addressType === 'R') {
if (data.bname !== '') {
extraAddress += data.bname;
}
if (data.buildingName !== '') {
extraAddress += extraAddress !== '' ? `, ${data.buildingName}` : data.buildingName;
}
fullAddress += extraAddress !== '' ? ` (${extraAddress})` : '';
}
props.setValue(fullAddress);
};
const handleClick = () => {
if (props.use === '주소')
postcodeOpen({ onComplete: handleComplete });
else if (props.use === '전용면적 (㎡)') {
setOpen(true);
} else if (props.use === '보증금 (만원)') {
setDepositOpen(true);
}
};
const handleChange = (event) => {
};
let readOnly = false;
if (props.use === '주소' || props.use === '전용면적 (㎡)' || props.use === '보증금 (만원)') readOnly = true;
let defaultValue = "";
return (
<>
<TextField
label={props.use}
variant="outlined"
value={props.value}
fullWidth
aria-label={props.use}
size="small"
color="secondary"
onClick={handleClick}
onChange={handleChange}
InputProps={{
readOnly: readOnly,
}}
/>
<CustomSliderModal use={props.use} open={open} setOpen={setOpen} value={props.value} setValue={props.setValue} />
<DepositSliderModal use={props.use} open={depositOpen} setOpen={setDepositOpen} value={props.value} setValue={props.setValue} />
</>
);
};
const theme = createTheme({
palette: {
secondary: {
main: '#1976D2',
},
secondary: {
main: '#F57C00',
},
},
});
function App() {
const [addr, setAddr] = useState();
const [size, setSize] = useState();
const [deposit, setDeposit] = useState();
const [floor, setFloor] = useState();
const [year, setYear] = useState();
const floorArray = [ {"label" : "지하1층", "value" : "지하1층"}, {"label" : "1층", "value" : "1층"}, {"label" : "2층", "value" : "2층"}, {"label" : "3층", "value" : "3층"}, {"label" : "4층", "value" : "4층"}, {"label" : "5층", "value" : "5층"}, {"label" : "6층", "value" : "6층"} ];
const currentYear = new Date().getFullYear();
const yearArray = Array.from({length: 100}, (_, i) => ({
label: currentYear - i,
value: currentYear - i,
}))
const handleSubmit = () => {
//
};
return (
<ThemeProvider theme={theme}>
<Grid container spacing={3}>
<Grid item xs={12} sm={12} md={12}>
<Box m={2} mb={0}>
<MyTitle />
</Box>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<Box m={1} mb={0} maxWidth={600} sx={{ display: 'block', margin: '0 auto' }}>
<MyInputField use="주소" value={addr} setValue={setAddr} />
</Box>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<Box m={1} mb={0} maxWidth={600} sx={{ display: 'block', margin: '0 auto' }}>
<MyInputField use="보증금 (만원)" value={deposit} setValue={setDeposit} />
</Box>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<Box m={1} mb={0} maxWidth={600} sx={{ display: 'block', margin: '0 auto' }}>
<MyInputField use="전용면적 (㎡)" value={size} setValue={setSize} />
</Box>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<Box m={1} mb={0} maxWidth={600} sx={{ display: 'block', margin: '0 auto' }}>
<SelectBox use="층수" value={floor} setValue={setFloor} myArray={floorArray} />
</Box>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<Box m={1} mb={0} maxWidth={600} sx={{ display: 'block', margin: '0 auto' }}>
<SelectBox use="건축년도" value={year} setValue={setYear} myArray={yearArray} />
</Box>
</Grid>
<Grid item xs={12} sm={12} md={12}>
<Box m={0.5}>
<Button sx={{ display: 'block', margin: '0 auto' }} variant="contained" color="secondary" onClick={handleSubmit}>계산하기</Button>
</Box>
</Grid>
</Grid>
<Footer />
</ThemeProvider>
);
}
export default App;