Skip to content

Commit 321749b

Browse files
authored
Merge pull request #62 from FE9-2/design/button
�Design: 드롭다운 버튼 2종류 (Sort, Filter) 버튼 UI 구현 완료
2 parents 01fcb2e + c41e6c2 commit 321749b

18 files changed

+290
-17
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"use client";
2+
import React from "react";
3+
4+
interface ChipProps {
5+
label: string; // Chip에 표시될 텍스트
6+
onDelete?: () => void; // 삭제 버튼 클릭 시 실행할 함수
7+
onClick?: () => void; // Chip 클릭 시 실행할 함수
8+
className?: string; // 사용자 정의 스타일 클래스
9+
deletable?: boolean; // 삭제 버튼 표시 여부
10+
selected?: boolean; // 선택된 상태 표시 여부
11+
}
12+
13+
const Chip: React.FC<ChipProps> = ({
14+
label,
15+
onDelete,
16+
onClick,
17+
className = "",
18+
deletable = false,
19+
selected = false,
20+
}) => {
21+
return (
22+
<div
23+
className={`inline-flex cursor-pointer items-center rounded-full border px-4 py-1 text-sm font-medium transition ${
24+
selected ? "border-blue-300 bg-blue-100 text-blue-600" : "border-gray-300 bg-gray-100 text-gray-700"
25+
} ${className}`}
26+
onClick={onClick}
27+
>
28+
<span className="mr-2">{label}</span>
29+
{deletable && (
30+
<button
31+
className="ml-2 flex h-4 w-4 items-center justify-center rounded-full bg-red-100 text-red-600 hover:bg-red-200"
32+
onClick={(e) => {
33+
e.stopPropagation();
34+
if (onDelete) onDelete();
35+
}}
36+
>
37+
×
38+
</button>
39+
)}
40+
</div>
41+
);
42+
};
43+
44+
export default Chip;

src/app/components/button/FloatingBtn.tsx renamed to src/app/components/button/default/FloatingBtn.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const FloatingBtn: React.FC<FloatingBtnProps> = ({ variant = "orange", icon, chi
1414
white: "bg-white text-primary-orange-300 border border-primary-orange-300 hover:bg-gray-100",
1515
};
1616

17-
const widthStyles = children ? "w-auto px-4" : "w-12";
17+
const widthStyles = children ? "w-auto px-3" : "w-12";
1818

1919
return (
2020
<button className={`${baseStyles} ${variants[variant]} ${widthStyles} ${className}`.trim()} {...props}>

src/app/components/button/FrameRadioBtn.tsx renamed to src/app/components/button/default/FrameRadioBtn.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const FrameRadioBtn: React.FC<FrameRadioBtnProps> = ({ width = "sm", checked = f
1919
md: "w-[360px]",
2020
};
2121

22-
const bgColor = disabled ? "bg-gray-200" : checked ? "bg-primary-orange-50" : "bg-white";
22+
const bgColor = disabled ? "" : checked ? "bg-primary-orange-50" : "bg-white";
2323
const borderColor = disabled ? "border-gray-200" : "border-primary-orange-300";
2424
const textColor = disabled ? "text-gray-400" : "text-black";
2525
const cursorStyle = disabled ? "cursor-not-allowed" : "cursor-pointer";
File renamed without changes.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"use client";
2+
import React, { useState } from "react";
3+
import { IoIosArrowDown } from "react-icons/io";
4+
5+
interface DropdownProps {
6+
label: string;
7+
options: string[];
8+
className?: string;
9+
}
10+
11+
const Dropdown: React.FC<DropdownProps> = ({ label, options, className = "" }) => {
12+
const [isOpen, setIsOpen] = useState(false);
13+
const [selectedLabel, setSelectedLabel] = useState(label);
14+
15+
const toggleDropdown = () => {
16+
setIsOpen((prev) => !prev);
17+
};
18+
19+
const handleSelect = (option: string) => {
20+
setSelectedLabel(option);
21+
setIsOpen(false);
22+
};
23+
24+
return (
25+
<div className={`relative inline-block w-20 text-left text-xs ${className}`}>
26+
<div>
27+
<button
28+
type="button"
29+
className="flex w-full items-center justify-between rounded-md border border-orange-300 bg-white p-2 font-medium text-gray-700 shadow-sm hover:bg-orange-100"
30+
onClick={toggleDropdown}
31+
>
32+
<span>{selectedLabel}</span>
33+
<span className={`text-orange-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`}>
34+
<IoIosArrowDown />
35+
</span>
36+
</button>
37+
</div>
38+
39+
{isOpen && (
40+
<div className="ring-black absolute right-0 z-10 mt-2 h-40 w-20 overflow-y-auto rounded-md bg-white ring-1 ring-gray-200">
41+
<div className="py-1" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
42+
<ul>
43+
{options.map((option) => (
44+
<li
45+
key={option}
46+
className="cursor-pointer px-4 py-2 hover:bg-gray-50"
47+
onClick={() => handleSelect(option)}
48+
>
49+
{option}
50+
</li>
51+
))}
52+
</ul>
53+
</div>
54+
</div>
55+
)}
56+
</div>
57+
);
58+
};
59+
60+
export default Dropdown;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"use client";
2+
import React, { useState } from "react";
3+
import { IoIosArrowDown } from "react-icons/io";
4+
5+
interface SortProps {
6+
label: string;
7+
options: string[];
8+
className?: string;
9+
onSortChange: (option: string) => void;
10+
}
11+
12+
const SortBtn: React.FC<SortProps> = ({ label, options, className = "", onSortChange }) => {
13+
const [isOpen, setIsOpen] = useState(false);
14+
const [selectedLabel, setSelectedLabel] = useState(label);
15+
16+
const toggleDropdown = () => {
17+
setIsOpen((prev) => !prev);
18+
};
19+
20+
const handleSelect = (option: string) => {
21+
setSelectedLabel(option);
22+
onSortChange(option);
23+
setIsOpen(false);
24+
};
25+
26+
return (
27+
<div className={`relative inline-block w-20 text-left text-xs ${className}`}>
28+
<div>
29+
<button
30+
type="button"
31+
className="bg-whitefont-medium flex w-full items-center justify-between rounded-md text-gray-700"
32+
onClick={toggleDropdown}
33+
>
34+
<span>{selectedLabel}</span>
35+
<span
36+
className={`transition-transform duration-200 hover:text-gray-200 ${isOpen ? "rotate-180 text-gray-200" : ""}`}
37+
>
38+
<IoIosArrowDown />
39+
</span>
40+
</button>
41+
</div>
42+
43+
{isOpen && (
44+
<div className="ring-black h-30 absolute right-0 z-10 mt-2 w-20 overflow-y-auto rounded-md bg-white p-1">
45+
<div role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
46+
<ul>
47+
{options.map((option) => (
48+
<li
49+
key={option}
50+
className="cursor-pointer rounded-md py-2 text-center text-gray-200 hover:bg-orange-100 hover:text-gray-700"
51+
onClick={() => handleSelect(option)}
52+
>
53+
{option}
54+
</li>
55+
))}
56+
</ul>
57+
</div>
58+
</div>
59+
)}
60+
</div>
61+
);
62+
};
63+
64+
export default SortBtn;

src/app/stories/design-system/components/buttons/BookmarkBtn.stories.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import BookmarkBtn from "@/app/components/button/BookmarkBtn";
1+
import BookmarkBtn from "@/app/components/button/default/BookmarkBtn";
22
import type { Meta, StoryObj } from "@storybook/react";
33

44
const meta: Meta<typeof BookmarkBtn> = {
5-
title: "Design System/Components/BookmarkBtn",
5+
title: "Design System/Components/Button/BookmarkBtn",
66
component: BookmarkBtn,
77
parameters: {
88
layout: "centered",
@@ -17,7 +17,6 @@ export const Bookmarked: Story = {
1717
render: (args) => {
1818
return (
1919
<div>
20-
<p className="mb-2 text-sm">click !</p>
2120
<BookmarkBtn {...args} />
2221
</div>
2322
);

0 commit comments

Comments
 (0)