diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts deleted file mode 100644 index e1856bb..0000000 --- a/src/shared/ui/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./select"; diff --git a/src/shared/ui/select/README.md b/src/shared/ui/select/README.md new file mode 100644 index 0000000..5c30a6a --- /dev/null +++ b/src/shared/ui/select/README.md @@ -0,0 +1,164 @@ +# Select(드롭다운) 사용 가이드 + +이 문서는 `A:SSU` 프로젝트의 공용 `Select`(드롭다운) 컴포넌트 사용법을 정리합니다. + +## 구현 세부사항 + +- **내부 구현**: `react-native-element-dropdown`라이브러리 기반 래퍼(Wrapper) +- **스타일링**: + - NativeWind `className` + RN `style` 혼합 + - 색상은 `global.styles.css` 디자인 토큰과 **동일 값**을 갖는 TS 토큰(`src/shared/styles/tokens.ts`)을 통해 사용 + +## 설치 의존성 + +이 컴포넌트는 내부적으로 `react-native-element-dropdown`를 사용합니다. + +```bash +yarn add react-native-element-dropdown +``` + +## import 방법 +- `import { Select } from "@/shared/ui/select";` + + + +## 빠른 사용 예시 (가장 기본) + +`Select`는 **controlled 컴포넌트**입니다. 즉, `items/value/onChange`를 항상 함께 사용합니다. + +```tsx +import { useMemo, useState } from "react"; +import { View, Text } from "react-native"; +import { Select } from "@/shared/ui/select"; + +export default function Example() { + const items = useMemo( + () => [ + { label: "총학생회", value: "university" }, + { label: "단과대학 학생회", value: "college" }, + { label: "학과/부 학생회", value: "department" }, + ], + [] + ); + + const [value, setValue] = useState(null); + + return ( + + ; +``` + +### 2) Zustand(전역 상태) + +상태를 store에서 꺼내 props로 그대로 연결합니다. + +```tsx +const value = useLoginStore((s) => s.councilType); +const setValue = useLoginStore((s) => s.setCouncilType); + + +``` + +### 2) “특정 옵션만” disabled (`items[].disabled`) + +이건 **목록은 보여주되, 일부 항목만 선택 불가**로 만드는 옵션입니다. + +- **언제 쓰나** + - “준비중/마감/권한 없음” 같은 상태를 옵션으로 노출해야 할 때 + - 리스트에서 존재는 알려야 하지만, 선택은 막아야 할 때 + +```tsx +const items = [ + { label: "총학생회", value: "university" }, + { label: "단과대학 학생회(준비중)", value: "college", disabled: true }, + { label: "학과/부 학생회", value: "department" }, +]; +``` + diff --git a/src/shared/ui/select/Select.tsx b/src/shared/ui/select/Select.tsx index 1cc0ae7..8833919 100644 --- a/src/shared/ui/select/Select.tsx +++ b/src/shared/ui/select/Select.tsx @@ -1,5 +1,5 @@ import { Ionicons } from "@expo/vector-icons"; -import { useMemo, useState } from "react"; +import { useState } from "react"; import { Text, View } from "react-native"; import { Dropdown } from "react-native-element-dropdown"; import { shadows } from "@/shared/styles/shadows"; @@ -33,23 +33,22 @@ export function Select({ }: SelectProps) { const sizeToken = SIZES[size]; const [isOpen, setIsOpen] = useState(false); - - const dropdownData = useMemo(() => { - // react-native-element-dropdown은 disabled key가 없어서 - // item 렌더링/선택 로직에서 직접 처리한다. - return items; - }, [items]); + const [disabledTapNonce, setDisabledTapNonce] = useState(0); return ( {!!label && ( - + {label} )} setIsOpen(false)} onChange={(item: SelectItem) => { // disabled 항목은 선택 무시 - if (item?.disabled) return; + if (item?.disabled) { + setDisabledTapNonce((n) => n + 1); + return; + } onChange(item?.value ?? null); }} // NOTE: Dropdown의 `style`은 내부에서 width를 측정하는 컨테이너(View)에 적용됩니다. @@ -115,28 +117,39 @@ export function Select({ }} renderItem={(item: SelectItem) => { const isSelected = item.value === value; + const isDisabled = Boolean(item.disabled); return ( - + {item.label} - {isSelected && ( + {isDisabled ? ( + + ) : isSelected ? ( - )} + ) : null} ); }}