diff --git a/frontend/lib/registry/components/select-basic/SelectBasicComponent.tsx b/frontend/lib/registry/components/select-basic/SelectBasicComponent.tsx index 2597a143..c2d4bcb3 100644 --- a/frontend/lib/registry/components/select-basic/SelectBasicComponent.tsx +++ b/frontend/lib/registry/components/select-basic/SelectBasicComponent.tsx @@ -62,8 +62,14 @@ const SelectBasicComponent: React.FC = ({ const [selectedValue, setSelectedValue] = useState(externalValue || config?.value || ""); const [selectedLabel, setSelectedLabel] = useState(""); - // multiselect의 경우 배열로 관리 - const [selectedValues, setSelectedValues] = useState([]); + // multiselect의 경우 배열로 관리 (콤마 구분자로 파싱) + const [selectedValues, setSelectedValues] = useState(() => { + const initialValue = externalValue || config?.value || ""; + if (config?.multiple && typeof initialValue === "string" && initialValue) { + return initialValue.split(",").map(v => v.trim()).filter(v => v); + } + return []; + }); // autocomplete의 경우 검색어 관리 const [searchQuery, setSearchQuery] = useState(""); @@ -116,8 +122,14 @@ const SelectBasicComponent: React.FC = ({ // 값이 실제로 다른 경우에만 업데이트 (빈 문자열도 유효한 값으로 처리) if (newValue !== selectedValue) { setSelectedValue(newValue); + + // 다중선택 모드인 경우 selectedValues도 업데이트 + if (config?.multiple && typeof newValue === "string" && newValue) { + const values = newValue.split(",").map(v => v.trim()).filter(v => v); + setSelectedValues(values); + } } - }, [externalValue, config?.value]); + }, [externalValue, config?.value, config?.multiple]); // ✅ React Query가 자동으로 처리하므로 복잡한 전역 상태 관리 제거 // - 캐싱: React Query가 자동 관리 (10분 staleTime, 30분 gcTime) @@ -500,6 +512,93 @@ const SelectBasicComponent: React.FC = ({ } // select (기본 선택박스) + // 다중선택 모드인 경우 + if (config?.multiple) { + return ( +
+
!isDesignMode && setIsOpen(true)} + style={{ pointerEvents: isDesignMode ? "none" : "auto" }} + > + {selectedValues.map((val, idx) => { + const opt = allOptions.find((o) => o.value === val); + return ( + + {opt?.label || val} + + + ); + })} + {selectedValues.length === 0 && ( + {placeholder} + )} +
+ {isOpen && !isDesignMode && ( +
+ {isLoadingCodes ? ( +
로딩 중...
+ ) : allOptions.length > 0 ? ( + allOptions.map((option, index) => { + const isSelected = selectedValues.includes(option.value); + return ( +
{ + const newVals = isSelected + ? selectedValues.filter((v) => v !== option.value) + : [...selectedValues, option.value]; + setSelectedValues(newVals); + const newValue = newVals.join(","); + if (isInteractive && onFormDataChange && component.columnName) { + onFormDataChange(component.columnName, newValue); + } + }} + > +
+ {}} + className="h-4 w-4" + /> + {option.label || option.value} +
+
+ ); + }) + ) : ( +
옵션이 없습니다
+ )} +
+ )} +
+ ); + } + + // 단일선택 모드 return (
= ({ onChange, }) => { const handleChange = (key: keyof SelectBasicConfig, value: any) => { - onChange({ [key]: value }); + // 기존 config와 병합하여 전체 객체 전달 (다른 속성 보호) + const newConfig = { ...config, [key]: value }; + onChange(newConfig); }; return ( @@ -67,6 +69,15 @@ export const SelectBasicConfigPanel: React.FC = ({ onCheckedChange={(checked) => handleChange("readonly", checked)} />
+ +
+ + handleChange("multiple", checked)} + /> +
); };