From c57e0218feee9d0cd57ed540f5c81f0bf8b2dfd1 Mon Sep 17 00:00:00 2001 From: kjs Date: Thu, 20 Nov 2025 18:17:08 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20select-basic=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=20=EB=8B=A4=EC=A4=91=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기능: - 설정 패널에 '다중 선택' 체크박스 추가 - multiple 옵션 활성화 시 다중선택 UI 렌더링 - 선택된 항목을 태그 형식으로 표시 - 각 태그에 X 버튼으로 개별 제거 가능 - 드롭다운에 체크박스 표시 - 콤마(,) 구분자로 값 저장/파싱 수정사항: - SelectBasicConfigPanel: 다중 선택 체크박스 추가 - SelectBasicConfigPanel: config 병합 방식으로 변경 (다른 속성 보호) - SelectBasicComponent: 초기값 콤마 구분자로 파싱 - SelectBasicComponent: 외부 value 변경 시 다중선택 배열 동기화 - SelectBasicComponent: 다중선택 UI 렌더링 로직 추가 사용법: 1. 설정 패널에서 '다중 선택' 체크 2. 드롭다운에서 여러 항목 선택 3. 선택된 항목이 태그로 표시되며 X로 제거 가능 4. 저장 시 '값1,값2,값3' 형식으로 저장 --- .../select-basic/SelectBasicComponent.tsx | 105 +++++++++++++++++- .../select-basic/SelectBasicConfigPanel.tsx | 13 ++- 2 files changed, 114 insertions(+), 4 deletions(-) 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)} + /> +
); };