refactor: 전체 프론트엔드 하드코딩 색상 → CSS 변수 일괄 치환
447+ 파일, 4500+ 줄 변경: - gray-* → border/bg-muted/text-foreground/text-muted-foreground - blue-* → primary/ring - red-* → destructive - green-* → emerald (일관성) - indigo-* → primary - yellow/orange → amber (통일) - dark mode 변형도 시맨틱 토큰으로 변환 Made-with: Cursor
This commit is contained in:
@@ -537,28 +537,28 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
onFocus={() => setIsOpen(true)}
|
||||
placeholder="코드 또는 코드명 입력..."
|
||||
className={cn(
|
||||
"h-10 w-full rounded-lg border border-gray-300 bg-white px-3 py-2 transition-all outline-none",
|
||||
"h-10 w-full rounded-lg border border-input bg-white px-3 py-2 transition-all outline-none",
|
||||
!isFieldDisabled && "hover:border-orange-400 focus:border-orange-500 focus:ring-2 focus:ring-orange-200",
|
||||
isSelected && "ring-2 ring-orange-500",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
|
||||
)}
|
||||
readOnly={isFieldDisabled}
|
||||
disabled={isFieldDisabled}
|
||||
/>
|
||||
{isOpen && !isFieldDisabled && filteredOptions.length > 0 && (
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
|
||||
{filteredOptions.map((option, index) => (
|
||||
<div
|
||||
key={`${option.value}-${index}`}
|
||||
className="cursor-pointer bg-white px-3 py-2 hover:bg-gray-100"
|
||||
className="cursor-pointer bg-white px-3 py-2 hover:bg-muted"
|
||||
onClick={() => {
|
||||
setSearchQuery("");
|
||||
handleOptionSelect(option.value, option.label);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-900">{option.label}</span>
|
||||
<span className="text-xs text-gray-500">{option.value}</span>
|
||||
<span className="text-sm font-medium text-foreground">{option.label}</span>
|
||||
<span className="text-xs text-muted-foreground">{option.value}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -574,16 +574,16 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
<div className="w-full">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-10 w-full items-center justify-between rounded-lg border border-gray-300 bg-white px-3 py-2",
|
||||
"flex h-10 w-full items-center justify-between rounded-lg border border-input bg-white px-3 py-2",
|
||||
!isFieldDisabled && "cursor-pointer hover:border-orange-400",
|
||||
isSelected && "ring-2 ring-orange-500",
|
||||
isOpen && "border-orange-500",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
|
||||
)}
|
||||
onClick={handleToggle}
|
||||
style={{ pointerEvents: isFieldDisabled ? "none" : "auto" }}
|
||||
>
|
||||
<span className={selectedLabel ? "text-gray-900" : "text-gray-500"}>{selectedLabel || placeholder}</span>
|
||||
<span className={selectedLabel ? "text-foreground" : "text-muted-foreground"}>{selectedLabel || placeholder}</span>
|
||||
<svg
|
||||
className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`}
|
||||
fill="none"
|
||||
@@ -594,21 +594,21 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
</svg>
|
||||
</div>
|
||||
{isOpen && !isFieldDisabled && (
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
|
||||
{isLoadingCodes ? (
|
||||
<div className="bg-white px-3 py-2 text-gray-900">로딩 중...</div>
|
||||
<div className="bg-white px-3 py-2 text-foreground">로딩 중...</div>
|
||||
) : allOptions.length > 0 ? (
|
||||
allOptions.map((option, index) => (
|
||||
<div
|
||||
key={`${option.value}-${index}`}
|
||||
className="cursor-pointer bg-white px-3 py-2 text-gray-900 hover:bg-gray-100"
|
||||
className="cursor-pointer bg-white px-3 py-2 text-foreground hover:bg-muted"
|
||||
onClick={() => handleOptionSelect(option.value, option.label)}
|
||||
>
|
||||
{option.label}
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="bg-white px-3 py-2 text-gray-900">옵션이 없습니다</div>
|
||||
<div className="bg-white px-3 py-2 text-foreground">옵션이 없습니다</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -622,16 +622,16 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
<div className="w-full">
|
||||
<div
|
||||
className={cn(
|
||||
"box-border flex h-full w-full flex-wrap gap-2 rounded-lg border border-gray-300 bg-white px-3 py-2",
|
||||
"box-border flex h-full w-full flex-wrap gap-2 rounded-lg border border-input bg-white px-3 py-2",
|
||||
!isFieldDisabled && "hover:border-orange-400",
|
||||
isSelected && "ring-2 ring-orange-500",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
|
||||
)}
|
||||
>
|
||||
{selectedValues.map((val, idx) => {
|
||||
const opt = allOptions.find((o) => o.value === val);
|
||||
return (
|
||||
<span key={idx} className="flex items-center gap-1 rounded bg-blue-100 px-2 py-1 text-sm text-blue-800">
|
||||
<span key={idx} className="flex items-center gap-1 rounded bg-primary/10 px-2 py-1 text-sm text-primary">
|
||||
{opt?.label || val}
|
||||
<button
|
||||
type="button"
|
||||
@@ -642,7 +642,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
onFormDataChange(component.columnName, newVals.join(","));
|
||||
}
|
||||
}}
|
||||
className="ml-1 text-blue-600 hover:text-blue-800"
|
||||
className="ml-1 text-primary hover:text-primary"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
@@ -683,20 +683,20 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
onFocus={() => !isFieldDisabled && setIsOpen(true)}
|
||||
placeholder={placeholder}
|
||||
className={cn(
|
||||
"h-10 w-full rounded-lg border border-gray-300 bg-white px-3 py-2 transition-all outline-none",
|
||||
"h-10 w-full rounded-lg border border-input bg-white px-3 py-2 transition-all outline-none",
|
||||
!isFieldDisabled && "hover:border-orange-400 focus:border-orange-500 focus:ring-2 focus:ring-orange-200",
|
||||
isSelected && "ring-2 ring-orange-500",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
|
||||
)}
|
||||
readOnly={isFieldDisabled}
|
||||
disabled={isFieldDisabled}
|
||||
/>
|
||||
{isOpen && !isFieldDisabled && filteredOptions.length > 0 && (
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
|
||||
{filteredOptions.map((option, index) => (
|
||||
<div
|
||||
key={`${option.value}-${index}`}
|
||||
className="cursor-pointer bg-white px-3 py-2 text-gray-900 hover:bg-gray-100"
|
||||
className="cursor-pointer bg-white px-3 py-2 text-foreground hover:bg-muted"
|
||||
onClick={() => {
|
||||
setSearchQuery(option.label);
|
||||
setSelectedValue(option.value);
|
||||
@@ -722,16 +722,16 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
<div className="w-full">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-10 w-full items-center justify-between rounded-lg border border-gray-300 bg-white px-3 py-2",
|
||||
"flex h-10 w-full items-center justify-between rounded-lg border border-input bg-white px-3 py-2",
|
||||
!isFieldDisabled && "cursor-pointer hover:border-orange-400",
|
||||
isSelected && "ring-2 ring-orange-500",
|
||||
isOpen && "border-orange-500",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
|
||||
)}
|
||||
onClick={handleToggle}
|
||||
style={{ pointerEvents: isFieldDisabled ? "none" : "auto" }}
|
||||
>
|
||||
<span className={selectedLabel ? "text-gray-900" : "text-gray-500"}>{selectedLabel || placeholder}</span>
|
||||
<span className={selectedLabel ? "text-foreground" : "text-muted-foreground"}>{selectedLabel || placeholder}</span>
|
||||
<svg
|
||||
className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`}
|
||||
fill="none"
|
||||
@@ -742,13 +742,13 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
</svg>
|
||||
</div>
|
||||
{isOpen && !isFieldDisabled && (
|
||||
<div className="absolute z-[99999] mt-1 w-full rounded-md border border-gray-300 bg-white shadow-lg">
|
||||
<div className="absolute z-[99999] mt-1 w-full rounded-md border border-input bg-white shadow-lg">
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="검색..."
|
||||
className="w-full border-b border-gray-300 px-3 py-2 outline-none"
|
||||
className="w-full border-b border-input px-3 py-2 outline-none"
|
||||
/>
|
||||
<div className="max-h-60 overflow-auto">
|
||||
{allOptions
|
||||
@@ -760,7 +760,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
.map((option, index) => (
|
||||
<div
|
||||
key={`${option.value}-${index}`}
|
||||
className="cursor-pointer bg-white px-3 py-2 text-gray-900 hover:bg-gray-100"
|
||||
className="cursor-pointer bg-white px-3 py-2 text-foreground hover:bg-muted"
|
||||
onClick={() => handleOptionSelect(option.value, option.label)}
|
||||
>
|
||||
{option.label || option.value}
|
||||
@@ -780,10 +780,10 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
<div className="w-full" style={{ height: "100%" }}>
|
||||
<div
|
||||
className={cn(
|
||||
"box-border flex w-full flex-wrap items-center gap-2 rounded-lg border border-gray-300 bg-white px-3 py-2",
|
||||
"box-border flex w-full flex-wrap items-center gap-2 rounded-lg border border-input bg-white px-3 py-2",
|
||||
!isFieldDisabled && "hover:border-orange-400",
|
||||
isSelected && "ring-2 ring-orange-500",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
|
||||
)}
|
||||
onClick={() => !isFieldDisabled && setIsOpen(true)}
|
||||
style={{
|
||||
@@ -794,7 +794,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
{selectedValues.map((val, idx) => {
|
||||
const opt = allOptions.find((o) => o.value === val);
|
||||
return (
|
||||
<span key={idx} className="flex items-center gap-1 rounded bg-blue-100 px-2 py-1 text-sm text-blue-800">
|
||||
<span key={idx} className="flex items-center gap-1 rounded bg-primary/10 px-2 py-1 text-sm text-primary">
|
||||
{opt?.label || val}
|
||||
<button
|
||||
type="button"
|
||||
@@ -807,19 +807,19 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
onFormDataChange(component.columnName, newValue);
|
||||
}
|
||||
}}
|
||||
className="ml-1 text-blue-600 hover:text-blue-800"
|
||||
className="ml-1 text-primary hover:text-primary"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
{selectedValues.length === 0 && <span className="text-gray-500">{placeholder}</span>}
|
||||
{selectedValues.length === 0 && <span className="text-muted-foreground">{placeholder}</span>}
|
||||
</div>
|
||||
{isOpen && !isFieldDisabled && (
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
|
||||
{isLoadingCodes || isLoadingCategories ? (
|
||||
<div className="bg-white px-3 py-2 text-gray-900">로딩 중...</div>
|
||||
<div className="bg-white px-3 py-2 text-foreground">로딩 중...</div>
|
||||
) : allOptions.length > 0 ? (
|
||||
(() => {
|
||||
// 부모별 그룹핑 (카테고리 연쇄관계인 경우)
|
||||
@@ -840,7 +840,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
return Object.entries(groupedOptions).map(([parentKey, group]) => (
|
||||
<div key={parentKey}>
|
||||
{/* 그룹 헤더 */}
|
||||
<div className="sticky top-0 border-b bg-gray-100 px-3 py-1.5 text-xs font-semibold text-gray-600">
|
||||
<div className="sticky top-0 border-b bg-muted px-3 py-1.5 text-xs font-semibold text-muted-foreground">
|
||||
{group.parentLabel}
|
||||
</div>
|
||||
{/* 그룹 옵션들 */}
|
||||
@@ -850,8 +850,8 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
<div
|
||||
key={`${option.value}-${index}`}
|
||||
className={cn(
|
||||
"cursor-pointer px-3 py-2 text-gray-900 hover:bg-gray-100",
|
||||
isOptionSelected && "bg-blue-50 font-medium",
|
||||
"cursor-pointer px-3 py-2 text-foreground hover:bg-muted",
|
||||
isOptionSelected && "bg-primary/10 font-medium",
|
||||
)}
|
||||
onClick={() => {
|
||||
const newVals = isOptionSelected
|
||||
@@ -898,8 +898,8 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
<div
|
||||
key={`${option.value}-${index}`}
|
||||
className={cn(
|
||||
"cursor-pointer px-3 py-2 text-gray-900 hover:bg-gray-100",
|
||||
isOptionSelected && "bg-blue-50 font-medium",
|
||||
"cursor-pointer px-3 py-2 text-foreground hover:bg-muted",
|
||||
isOptionSelected && "bg-primary/10 font-medium",
|
||||
)}
|
||||
onClick={() => {
|
||||
const newVals = isOptionSelected
|
||||
@@ -937,7 +937,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
});
|
||||
})()
|
||||
) : (
|
||||
<div className="bg-white px-3 py-2 text-gray-900">옵션이 없습니다</div>
|
||||
<div className="bg-white px-3 py-2 text-foreground">옵션이 없습니다</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -950,16 +950,16 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
<div className="w-full">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-10 w-full items-center justify-between rounded-lg border border-gray-300 bg-white px-3 py-2",
|
||||
"flex h-10 w-full items-center justify-between rounded-lg border border-input bg-white px-3 py-2",
|
||||
!isFieldDisabled && "cursor-pointer hover:border-orange-400",
|
||||
isSelected && "ring-2 ring-orange-500",
|
||||
isOpen && "border-orange-500",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
|
||||
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
|
||||
)}
|
||||
onClick={handleToggle}
|
||||
style={{ pointerEvents: isFieldDisabled ? "none" : "auto" }}
|
||||
>
|
||||
<span className={selectedLabel ? "text-gray-900" : "text-gray-500"}>{selectedLabel || placeholder}</span>
|
||||
<span className={selectedLabel ? "text-foreground" : "text-muted-foreground"}>{selectedLabel || placeholder}</span>
|
||||
<svg
|
||||
className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`}
|
||||
fill="none"
|
||||
@@ -970,21 +970,21 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
</svg>
|
||||
</div>
|
||||
{isOpen && !isFieldDisabled && (
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
|
||||
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
|
||||
{isLoadingCodes ? (
|
||||
<div className="bg-white px-3 py-2 text-gray-900">로딩 중...</div>
|
||||
<div className="bg-white px-3 py-2 text-foreground">로딩 중...</div>
|
||||
) : allOptions.length > 0 ? (
|
||||
allOptions.map((option, index) => (
|
||||
<div
|
||||
key={`${option.value}-${index}`}
|
||||
className="cursor-pointer bg-white px-3 py-2 text-gray-900 hover:bg-gray-100"
|
||||
className="cursor-pointer bg-white px-3 py-2 text-foreground hover:bg-muted"
|
||||
onClick={() => handleOptionSelect(option.value, option.label)}
|
||||
>
|
||||
{option.label || option.value}
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="bg-white px-3 py-2 text-gray-900">옵션이 없습니다</div>
|
||||
<div className="bg-white px-3 py-2 text-foreground">옵션이 없습니다</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -1006,7 +1006,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
|
||||
{component.label && (component.style?.labelDisplay ?? true) && (
|
||||
<label className="absolute -top-6 left-0 text-sm font-medium text-slate-600">
|
||||
{component.label}
|
||||
{component.required && <span className="text-red-500">*</span>}
|
||||
{component.required && <span className="text-destructive">*</span>}
|
||||
</label>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user