init
This commit is contained in:
151
src/components/common/button/Button.js
Normal file
151
src/components/common/button/Button.js
Normal file
@@ -0,0 +1,151 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import styled, { css } from 'styled-components';
|
||||
import ResetIcon from '../../../assets/img/icon/icon-reset.png';
|
||||
/**
|
||||
* @param {string} text 버튼 내부 텍스트
|
||||
* @param {string} type 버튼 타입 (button, submit)
|
||||
* @param {string} errorMessage 표시할 에러 메시지
|
||||
* @param {() => void} handleClick 버튼 클릭시 실행할 이벤트, Route 기능의 경우 history.push와 같은 함수를 이용합니다.
|
||||
* @param {string} buttonColor 버튼 배경 색상
|
||||
* @param {string} hoverColor 버튼 호버 배경 색상
|
||||
* @param {object} props 폰트 관련 속성
|
||||
*/
|
||||
|
||||
const Button = ({ text, type = 'button', errorMessage, handleClick, theme, size, width, height, bordercolor, disabled, name }) => {
|
||||
const [isShowErrorMessage, setIsShowErrorMessage] = useState(false);
|
||||
|
||||
const handleButtonClick = () => {
|
||||
if (errorMessage) {
|
||||
setIsShowErrorMessage(true);
|
||||
} else {
|
||||
handleClick?.();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
errorMessage || setIsShowErrorMessage(false);
|
||||
}, [errorMessage]);
|
||||
|
||||
return (
|
||||
<Wrapper width={width}>
|
||||
<ButtonStyle
|
||||
onSubmit={e => e.preventDefault()}
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
theme={theme}
|
||||
size={size}
|
||||
bordercolor={bordercolor}
|
||||
width={width}
|
||||
height={height}
|
||||
name={name}>
|
||||
{text}
|
||||
</ButtonStyle>
|
||||
{isShowErrorMessage && <ErrorText>{errorMessage}</ErrorText>}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: ${props => props.display || 'inline-block'};
|
||||
width: ${props => props.width};
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const sizes = {
|
||||
large: {
|
||||
width: '100%',
|
||||
fontSize: '17px',
|
||||
padding: '16px 20px',
|
||||
fontWeight: '600',
|
||||
height: '50px',
|
||||
},
|
||||
};
|
||||
|
||||
const sizeStyles = css`
|
||||
${({ size }) => css`
|
||||
width: ${sizes[size].width};
|
||||
font-size: ${sizes[size].fontSize};
|
||||
border-radius: ${sizes[size].borderRadius};
|
||||
padding: ${sizes[size].$padding};
|
||||
font-weight: ${sizes[size].fontWeight};
|
||||
height: ${sizes[size].height};
|
||||
`}
|
||||
`;
|
||||
|
||||
const ButtonStyle = styled.button`
|
||||
border-radius: 5px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: ${props => props.width || '80px'};
|
||||
height: ${props => props.height || '30px'};
|
||||
min-width: fit-content;
|
||||
color: #2c2c2c;
|
||||
font-size: 14px;
|
||||
${props => props.size && sizeStyles}
|
||||
|
||||
${props =>
|
||||
props.theme === 'line' &&
|
||||
css`
|
||||
border: 1px solid #2c2c2c;
|
||||
background: transparent;
|
||||
`}
|
||||
${props =>
|
||||
props.theme === 'primary' &&
|
||||
css`
|
||||
background: #2c2c2c;
|
||||
color: #fff;
|
||||
`}
|
||||
${props =>
|
||||
props.theme === 'disable' &&
|
||||
css`
|
||||
background: #d9d9d9;
|
||||
color: #fff;
|
||||
`}
|
||||
${props =>
|
||||
props.theme === 'reset' &&
|
||||
css`
|
||||
border: 1px solid ${props.bordercolor};
|
||||
background: url(${ResetIcon}) 50% 50% no-repeat;
|
||||
border: 1px solid #d9d9d9;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
`}
|
||||
${props =>
|
||||
props.theme === 'gray' &&
|
||||
css`
|
||||
background: #b8b8b8;
|
||||
color: #fff;
|
||||
width: 100px;
|
||||
height: 35px;
|
||||
`}
|
||||
${props =>
|
||||
props.theme === 'search' &&
|
||||
css`
|
||||
background: #2c2c2c;
|
||||
color: #fff;
|
||||
width: 100px;
|
||||
height: 35px;
|
||||
`}
|
||||
${props =>
|
||||
props.theme === 'find' &&
|
||||
css`
|
||||
background: #8c8c8c;
|
||||
color: #fff;
|
||||
width: 100px;
|
||||
height: 35px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const ErrorText = styled.p`
|
||||
color: red;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
line-height: 15px;
|
||||
`;
|
||||
|
||||
export default Button;
|
||||
113
src/components/common/button/ExcelDownButton.js
Normal file
113
src/components/common/button/ExcelDownButton.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import * as XLSX from 'xlsx-js-style';
|
||||
import { ExcelDownButton } from '../../../styles/ModuleComponents';
|
||||
|
||||
const ExcelDownloadButton = ({ tableRef, fileName = 'download.xlsx', sheetName = 'Sheet1' }) => {
|
||||
const isNumeric = (value) => {
|
||||
// 숫자 또는 숫자 문자열인지 확인
|
||||
return !isNaN(value) && !isNaN(parseFloat(value));
|
||||
};
|
||||
|
||||
const downloadExcel = () => {
|
||||
try {
|
||||
if (!tableRef.current) return;
|
||||
|
||||
const tableElement = tableRef.current;
|
||||
const headerRows = tableElement.getElementsByTagName('thead')[0].getElementsByTagName('tr');
|
||||
const bodyRows = tableElement.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
||||
|
||||
// 헤더 데이터 추출
|
||||
const headers = Array.from(headerRows[0].cells).map(cell => cell.textContent);
|
||||
|
||||
// 바디 데이터 추출 및 숫자 타입 처리
|
||||
const bodyData = Array.from(bodyRows).map(row =>
|
||||
Array.from(row.cells).map(cell => {
|
||||
const value = cell.textContent;
|
||||
return isNumeric(value) ? parseFloat(value) : value;
|
||||
})
|
||||
);
|
||||
|
||||
// 워크북 생성
|
||||
const wb = XLSX.utils.book_new();
|
||||
|
||||
// 테두리 스타일 정의
|
||||
const borderStyle = {
|
||||
style: "thin",
|
||||
color: { rgb: "000000" }
|
||||
};
|
||||
|
||||
// 스타일 정의
|
||||
const centerStyle = {
|
||||
font: {
|
||||
name: "맑은 고딕",
|
||||
sz: 11
|
||||
},
|
||||
alignment: {
|
||||
horizontal: 'right',
|
||||
vertical: 'right'
|
||||
},
|
||||
border: {
|
||||
top: borderStyle,
|
||||
bottom: borderStyle,
|
||||
left: borderStyle,
|
||||
right: borderStyle
|
||||
}
|
||||
};
|
||||
|
||||
const headerStyle = {
|
||||
alignment: {
|
||||
horizontal: 'center',
|
||||
vertical: 'center'
|
||||
},
|
||||
fill: {
|
||||
fgColor: { rgb: "d9e1f2" },
|
||||
patternType: "solid"
|
||||
}
|
||||
};
|
||||
|
||||
// 데이터에 스타일 적용
|
||||
const wsData = [
|
||||
// 헤더 행
|
||||
headers.map(h => ({
|
||||
v: h,
|
||||
s: headerStyle
|
||||
})),
|
||||
// 데이터 행들
|
||||
...bodyData.map(row =>
|
||||
row.map(cell => ({
|
||||
v: cell,
|
||||
s: centerStyle
|
||||
}))
|
||||
)
|
||||
];
|
||||
|
||||
// 워크시트 생성
|
||||
const ws = XLSX.utils.aoa_to_sheet(wsData);
|
||||
|
||||
// 열 너비 설정 (최소 8, 최대 50)
|
||||
ws['!cols'] = headers.map((_, index) => {
|
||||
const maxLength = Math.max(
|
||||
headers[index].length * 2,
|
||||
...bodyData.map(row => String(row[index] || '').length * 1.2)
|
||||
);
|
||||
return { wch: Math.max(8, Math.min(50, maxLength)) };
|
||||
});
|
||||
|
||||
// 워크시트를 워크북에 추가
|
||||
XLSX.utils.book_append_sheet(wb, ws, sheetName);
|
||||
|
||||
// 엑셀 파일 다운로드
|
||||
XLSX.writeFile(wb, fileName);
|
||||
} catch (error) {
|
||||
console.error('Excel download failed:', error);
|
||||
alert('엑셀 다운로드 중 오류가 발생했습니다.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ExcelDownButton onClick={downloadExcel}>
|
||||
엑셀 다운로드
|
||||
</ExcelDownButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExcelDownloadButton;
|
||||
Reference in New Issue
Block a user