- cn() 유틸 신규 (clsx + tailwind-merge, 시맨틱 토큰 classGroup 등록) - theme.css @layer utilities로 직접 정의 (Tailwind v4 복합 이름 매핑 실패 대응): text-heading/label/hint/on-vivid/on-bright, bg-surface-raised/overlay - badgeVariants (CVA) 재구축: 8 intent x 4 size, rem 기반, !important 제거 - Badge 컴포넌트가 cn(badgeVariants, className)로 override 허용 - DataTable width 의미 변경: 고정 -> 선호 최소 너비 (minWidth), truncate + title 툴팁 - dateFormat.ts sv-SE 로케일로 YYYY-MM-DD HH:mm:ss 일관된 KST 출력 - ColorPicker 신규 (팔레트 + native color + hex 입력) - shared/constants/ 19개 카탈로그: violation/alert/event/enforcement/patrol/ engine/userRole/device/parentResolution/modelDeployment/gearGroup/darkVessel/ httpStatus/userAccount/loginResult/permission/vesselAnalysis/connection/trainingZone + kpiUiMap. 백엔드 enum/code_master 기반 SSOT - i18n ko/en common.json에 카테고리 섹션 추가 - adminApi.fetchRoles()가 updateRoleColorCache 자동 호출 - 공통 컴포넌트 (ExcelExport/NotificationBanner/Pagination/SaveButton) 시맨틱 토큰 적용
81 lines
2.9 KiB
TypeScript
81 lines
2.9 KiB
TypeScript
/**
|
|
* CVA (Class Variance Authority) 변형 모듈
|
|
* 프로젝트 전체에서 반복되는 Tailwind 패턴을 변형으로 통합
|
|
*/
|
|
import { cva } from 'class-variance-authority';
|
|
|
|
/** 카드 변형 — CSS 변수 기반으로 테마 반응 */
|
|
export const cardVariants = cva('rounded-xl border border-border', {
|
|
variants: {
|
|
variant: {
|
|
default: 'bg-surface-raised',
|
|
elevated: 'bg-card shadow-lg shadow-black/20',
|
|
inner: 'bg-surface-overlay',
|
|
transparent: 'bg-transparent border-transparent',
|
|
},
|
|
},
|
|
defaultVariants: { variant: 'default' },
|
|
});
|
|
|
|
/** 뱃지 변형 — 위험도/상태별 150회+ 반복 패턴 통합
|
|
*
|
|
* 가독성 정책:
|
|
* - 배경: 색상별 진한 솔리드(-400) — 명확한 분류 식별
|
|
* - 텍스트: 시맨틱 토큰 text-on-bright (theme.css = #0f172a) — 테마 무관 일관 가독성
|
|
* - 보더: 같은 색상 계열 -600 (배경 강조)
|
|
* - 가운데 정렬
|
|
* - 폰트 크기: rem 기반 (root font-size 대비 비율) — 화면 비율에 따라 자동 조정
|
|
*
|
|
* className override는 cn(tailwind-merge) 덕분에 같은 그룹(text-color/font-size/bg) 충돌 시
|
|
* 마지막 명시값이 적용 — !important 없이 의도된 override만 허용.
|
|
*/
|
|
export const badgeVariants = cva(
|
|
'inline-flex items-center justify-center whitespace-nowrap rounded-md border px-2 py-0.5 font-semibold transition-colors text-center text-on-bright',
|
|
{
|
|
variants: {
|
|
intent: {
|
|
critical: 'bg-red-400 border-red-600',
|
|
high: 'bg-orange-400 border-orange-600',
|
|
warning: 'bg-yellow-400 border-yellow-600',
|
|
info: 'bg-blue-400 border-blue-600',
|
|
success: 'bg-green-400 border-green-600',
|
|
muted: 'bg-slate-400 border-slate-600',
|
|
purple: 'bg-purple-400 border-purple-600',
|
|
cyan: 'bg-cyan-400 border-cyan-600',
|
|
},
|
|
// rem 기반 — root font-size 대비 비율, 화면 비율 변경 시 자동 조정
|
|
// xs ≈ 11px, sm ≈ 12px, md ≈ 13px, lg ≈ 14px (root 14px 기준)
|
|
size: {
|
|
xs: 'text-[0.6875rem] leading-tight',
|
|
sm: 'text-[0.75rem] leading-tight',
|
|
md: 'text-[0.8125rem] leading-tight',
|
|
lg: 'text-[0.875rem] leading-tight',
|
|
},
|
|
},
|
|
defaultVariants: { intent: 'info', size: 'sm' },
|
|
},
|
|
);
|
|
|
|
/** 상태 점(dot) 변형 */
|
|
export const statusDotVariants = cva('rounded-full', {
|
|
variants: {
|
|
status: {
|
|
online: 'bg-green-500',
|
|
warning: 'bg-yellow-500',
|
|
danger: 'bg-red-500',
|
|
offline: 'bg-slate-500',
|
|
},
|
|
size: {
|
|
sm: 'w-1.5 h-1.5',
|
|
md: 'w-2 h-2',
|
|
lg: 'w-2.5 h-2.5',
|
|
},
|
|
},
|
|
defaultVariants: { status: 'online', size: 'md' },
|
|
});
|
|
|
|
export type CardVariant = 'default' | 'elevated' | 'inner' | 'transparent';
|
|
export type BadgeIntent = 'critical' | 'high' | 'warning' | 'info' | 'success' | 'muted' | 'purple' | 'cyan';
|
|
export type BadgeSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
export type StatusDotStatus = 'online' | 'warning' | 'danger' | 'offline';
|