Skip to content

Commit

Permalink
fix(Select): defaultValue type Option<T>로 수정 (#120)
Browse files Browse the repository at this point in the history
* fix: Select defaultValue  타입 Option<T>로 수정

* cs
  • Loading branch information
Brokyeom authored Aug 31, 2024
1 parent bca7aab commit 251acba
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 56 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-roses-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sopt-makers/ui": patch
---

Select 컴포넌트 defaultValue type Option<T>로 수정
85 changes: 29 additions & 56 deletions packages/ui/Input/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState, useRef, useEffect, useCallback } from "react";
import { IconChevronDown, IconUser } from "@sopt-makers/icons";
import * as S from "./style.css";
import { useState, useRef, useEffect, useCallback } from 'react';
import { IconChevronDown, IconUser } from '@sopt-makers/icons';
import * as S from './style.css';

export interface Option<T> {
label: string;
Expand All @@ -13,28 +13,20 @@ export interface Option<T> {
interface SelectProps<T> {
className?: string;
placeholder?: string;
type: "text" | "textDesc" | "textIcon" | "userList" | "userListDesc";
type: 'text' | 'textDesc' | 'textIcon' | 'userList' | 'userListDesc';
options: Option<T>[];
visibleOptions?: number;
defaultValue?: T;
defaultValue?: Option<T>;
onChange: (value: T) => void;
}

function Select<T extends string | number | boolean>(props: SelectProps<T>) {
const {
className,
placeholder,
type,
options,
visibleOptions = 5,
defaultValue,
onChange,
} = props;
const { className, placeholder, type, options, visibleOptions = 5, defaultValue, onChange } = props;

const optionsRef = useRef<HTMLUListElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);

const [selected, setSelected] = useState<T | null>(defaultValue ?? null);
const [selected, setSelected] = useState<Option<T> | null>(defaultValue ?? null);
const [open, setOpen] = useState(false);

const handleToggleOpen = useCallback(() => {
Expand All @@ -48,24 +40,20 @@ function Select<T extends string | number | boolean>(props: SelectProps<T>) {
const calcMaxHeight = useCallback(() => {
const getOptionHeight = () => {
switch (type) {
case "text":
case "textIcon":
case 'text':
case 'textIcon':
return 42;
case "textDesc":
case "userListDesc":
case 'textDesc':
case 'userListDesc':
return 62;
case "userList":
case 'userList':
return 48;
}
};
const gapHeight = 6;
const paddingHeight = 8;

return (
getOptionHeight() * visibleOptions +
gapHeight * (visibleOptions - 1) +
paddingHeight * 2
);
return getOptionHeight() * visibleOptions + gapHeight * (visibleOptions - 1) + paddingHeight * 2;
}, [visibleOptions, type]);

useEffect(() => {
Expand All @@ -79,65 +67,50 @@ function Select<T extends string | number | boolean>(props: SelectProps<T>) {
handleToggleClose();
}
};
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener('mousedown', handleClickOutside);

return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener('mousedown', handleClickOutside);
};
}, [handleToggleClose]);

const handleOptionClick = (value: T) => {
const handleOptionClick = (value: Option<T>) => {
setSelected(value);
onChange(value);
onChange(value.value); // value 자체가 아닌 value.value를 onChange에 전달
handleToggleClose();
};

const selectedLabel = selected
? options.find((option) => option.value === selected)?.label
: placeholder;
const selectedLabel = selected ? options.find((option) => option.value === selected.value)?.label : placeholder;

return (
<div className={`${S.selectWrap} ${className}`}>
<button
className={S.select}
onClick={handleToggleOpen}
ref={buttonRef}
type="button"
>
<p className={!selected ? S.selectPlaceholder : ""}>{selectedLabel}</p>
<button className={S.select} onClick={handleToggleOpen} ref={buttonRef} type='button'>
<p className={!selected ? S.selectPlaceholder : ''}>{selectedLabel}</p>
<IconChevronDown
style={{
width: 20,
height: 20,
transform: open ? "rotate(-180deg)" : "",
transition: "all 0.5s",
transform: open ? 'rotate(-180deg)' : '',
transition: 'all 0.5s',
}}
/>
</button>

{open ? (
<ul
className={S.optionList}
ref={optionsRef}
style={{ maxHeight: calcMaxHeight() }}
>
<ul className={S.optionList} ref={optionsRef} style={{ maxHeight: calcMaxHeight() }}>
{options.map((option) => (
<li key={option.label}>
<button
className={S.option}
onClick={() => {
handleOptionClick(option.value);
handleOptionClick(option); // Option<T> 타입으로 전달
}}
type="button"
type='button'
>
{type === "textIcon" && option.icon}
{(type === "userList" || type === "userListDesc") &&
{type === 'textIcon' && option.icon}
{(type === 'userList' || type === 'userListDesc') &&
(option.profileUrl ? (
<img
alt={option.label}
className={S.optionProfileImg}
src={option.profileUrl}
/>
<img alt={option.label} className={S.optionProfileImg} src={option.profileUrl} />
) : (
<div className={S.optionProfileEmpty}>
<IconUser />
Expand All @@ -146,7 +119,7 @@ function Select<T extends string | number | boolean>(props: SelectProps<T>) {

<div>
<p>{option.label}</p>
{(type === "textDesc" || type === "userListDesc") && (
{(type === 'textDesc' || type === 'userListDesc') && (
<p className={S.optionDesc}>{option.description}</p>
)}
</div>
Expand Down

0 comments on commit 251acba

Please sign in to comment.