/* ============================================================================
* 서명전에 — TDS Button 컴포넌트
* · TDS Mobile 2026-03 Button 스펙. 기존 `.btn` (HIG 잔재) 을 대체하는 신규
* 공식 버튼. 레거시 `.btn` 은 deprecated 로 유지 (§4.7 마이그레이션 참고).
*
* · props:
* as "button" (기본) | "a"
* color "primary" (기본) | "danger" | "light" | "dark"
* variant "fill" (기본) | "weak"
* display "inline" (기본) | "block" | "full"
* size "small" | "medium" | "large" | "xlarge" (기본)
* loading boolean — 3-dot 인디케이터 + 딤 레이어. 너비는 유지.
* disabled boolean
* type "button" | "submit" | "reset" (as="button" 에서만)
* href string (as="a" 에서만)
* htmlStyle React.CSSProperties — HTML 의 `style` 속성에 매핑.
* `style` prop 은 CSS 변수 오버라이드용으로 쓰는 것이 권장이지만
* 래퍼/HoC 가 `style` 을 가로채는 환경을 위해 별도 경로로 제공.
* className, aria-* 등 HTMLElement 속성 전부 패스스루.
*
* · 색상 커스터마이즈는 style 로 CSS 변수 덮어쓰기:
*
* 지원 변수는 §4.7 참조.
*
* · 로딩 상태는 aria-busy="true", 비활성은 disabled/aria-disabled 로 접근성 지원.
* ========================================================================== */
function Button({
as = "button",
color = "primary",
variant = "fill",
display = "inline",
size = "xlarge",
loading = false,
disabled = false,
type,
href,
htmlStyle,
style,
className = "",
children,
onClick,
...rest
}) {
const Tag = as === "a" ? "a" : "button";
const cls = [
"tds-button",
`tds-button--${color}`,
`tds-button--${variant}`,
`tds-button--${display}`,
`tds-button--${size}`,
loading && "is-loading",
disabled && "is-disabled",
className,
]
.filter(Boolean)
.join(" ");
// style (CSS 변수 오버라이드 용) + htmlStyle (일반 인라인 스타일) 병합.
const mergedStyle = { ...(style || {}), ...(htmlStyle || {}) };
// 로딩/비활성 시 클릭 가로채기. button 태그는 disabled 속성으로도 막히지만
// a 태그는 disabled 가 없어서 가로채고 aria-disabled 만 달아준다.
const handleClick = (e) => {
if (loading || disabled) {
e.preventDefault();
e.stopPropagation();
return;
}
onClick?.(e);
};
const tagProps = {
className: cls,
style: mergedStyle,
onClick: handleClick,
"aria-busy": loading ? "true" : undefined,
...rest,
};
if (Tag === "button") {
tagProps.type = type || "button";
// loading 중에도 실제 클릭은 못하게 disabled 처리 (포커스는 유지).
tagProps.disabled = disabled || loading;
} else {
if (href) tagProps.href = href;
if (disabled || loading) {
tagProps["aria-disabled"] = "true";
tagProps.tabIndex = -1;
}
}
return (
{children}
{loading && (
)}
);
}
window.Button = Button;
Object.assign(window, { Button });