Skip to content

Commit

Permalink
Feat: 다중택일(라디오) 컴포넌트 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
joanShim committed Dec 29, 2023
1 parent 290760d commit 80c35a9
Show file tree
Hide file tree
Showing 11 changed files with 306 additions and 9 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@pnpm-monorepo/shared": "^1.0.0",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-radio-group": "^1.1.3",
"@svgr/rollup": "^8.1.0",
"@tanstack/react-query": "^5.14.6",
"@tanstack/react-query-devtools": "^5.14.6",
Expand Down
129 changes: 129 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 85 additions & 0 deletions src/components/common/radio/Radio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as RadioGroup from '@radix-ui/react-radio-group';
import { useState } from 'react';

interface RadioSelectProps {
items: (string | number)[] | Record<string, string | number>;
ariaLabel: string;
onSelectionChange: (selectedValue: string | number) => void;
gridCols?: number;
}

type gridColumnsType = {
[key: number]: string;
};

const gridColumns: gridColumnsType = {
2: 'grid grid-cols-2',
3: 'grid grid-cols-3',
4: 'grid grid-cols-4',
};

export const RadioSelect: React.FC<RadioSelectProps> = ({
items,
ariaLabel,
onSelectionChange,
gridCols = 2,
}) => {
const [selectedValue, setSelectedValue] = useState<string | number | null>(
null,
);

console.log('selected Radio value:', selectedValue);

const handleRadioChange = (value: string | number) => {
setSelectedValue(value);
onSelectionChange(value);
};

const gridClassName = gridColumns[gridCols] || gridColumns[2];
return (
<>
<form>
<RadioGroup.Root
className={`RadioGroupRoot grid w-full gap-x-4 ${gridClassName}`}
onValueChange={handleRadioChange}
aria-label={ariaLabel}>
{Array.isArray(items)
? items.map((value) => (
<div key={value}>
<RadioGroup.Item
className="RadioGroupItem"
value={value as string}
id={`r${value}`}>
<RadioGroup.Indicator className="RadioGroupIndicator absolute" />
</RadioGroup.Item>
<label
className={`Label block flex h-10 w-full cursor-pointer items-center justify-center rounded-lg bg-gray1 transition-colors ${
selectedValue === value ? 'bg-gray3' : ''
}`}
htmlFor={`r${value}`}>
{String(value)}
</label>
</div>
))
: Object.entries(items).map(([label, value]) => (
<div key={String(value)}>
<RadioGroup.Item
className="RadioGroupItem"
value={String(value)}
id={`r${value}`}>
<RadioGroup.Indicator className="RadioGroupIndicator absolute" />
</RadioGroup.Item>
<label
className={`Label block flex h-10 w-full cursor-pointer items-center justify-center rounded-lg bg-gray1 transition-colors ${
selectedValue === String(value) ? 'bg-gray3' : ''
}`}
htmlFor={`r${value}`}>
{label}
</label>
</div>
))}
</RadioGroup.Root>
</form>
</>
);
};
19 changes: 19 additions & 0 deletions src/components/search/RegionSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { AREA_CODE } from '@/constants';
import { RadioSelect } from '@components/common/radio/Radio';

interface RegionSelectProps {
onRegionChange: (selectedRegion: string | number) => void;
}

export const RegionSelect: React.FC<RegionSelectProps> = ({
onRegionChange,
}) => {
return (
<RadioSelect
items={AREA_CODE}
ariaLabel="지역선택"
onSelectionChange={onRegionChange}
gridCols={3}
/>
);
};
14 changes: 14 additions & 0 deletions src/components/search/SearchInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const SearchInput = () => {
return (
<>
<div className="relative flex items-center ">
<button className=" backIcon bg-gray5 mr-2.5 h-[24px] w-[24px]" />
<div className="absolute left-[52px] top-1/2 h-[12px] w-[12px] -translate-y-1/2 transform bg-black" />
<input
className="bg-gray1 body1 ml-2.5 h-[40px] w-full items-center rounded-lg pl-[32px] pr-2.5 focus:outline-none"
onClick={() => {}}
placeholder="어디로 떠나세요?"></input>
</div>
</>
);
};
18 changes: 18 additions & 0 deletions src/components/search/StartSearchBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useNavigate } from 'react-router-dom';

export const StartSearchButton = () => {
const navigate = useNavigate();

const goToSearch = () => {
navigate('/search');
};

return (
<button
className="flex h-[40px] w-full items-center border-b-2 border-solid px-1"
onClick={goToSearch}>
<div className=" searchIcon mr-2.5 h-[12px] w-[12px]" />
<span className="text-gray4 body1">어디로 떠나세요?</span>
</button>
);
};
19 changes: 19 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const AREA_CODE = {
서울: '서울',
부산: '부산',
대구: '대구',
인천: '인천',
광주: '광주',
대전: '대전',
울산: '울산',
세종: '세종',
제주: '제주',
경기: '경기',
강원: '강원',
충북: '충북',
충남: '충남',
경북: '경북',
경남: '경남',
전북: '전북',
전남: '전남',
};
2 changes: 1 addition & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import './index.css';
// }
if (import.meta.env.DEV) {
const { worker } = await import('./mocks/browser.ts');
await worker.start();
await worker.stop();
}

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
9 changes: 3 additions & 6 deletions src/pages/main/main.page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { ButtonPrimary, ButtonWhite } from '@components/common/button/Button';

import { getPopularTours } from '@api/region';
import { getMember } from '@api/member';
// import { getMember } from '@api/member';
import { useEffect } from 'react';
import { StartSearchButton } from '@components/search/StartSearchBtn';

const Main = () => {
useEffect(() => {
Expand All @@ -20,9 +19,7 @@ const Main = () => {

return (
<>
<h1 className="title2">지금 인기여행지</h1>
<ButtonWhite onClick={() => {}}>더보기</ButtonWhite>
<ButtonPrimary onClick={() => {}}>완료</ButtonPrimary>
<StartSearchButton />
</>
);
};
Expand Down
15 changes: 15 additions & 0 deletions src/pages/search/search.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { RegionSelect } from '@components/search/RegionSelect';
import { SearchInput } from '@components/search/SearchInput';

export const Search = () => {
const handleRegionChange = (selectedRegion: string | number) => {
console.log('선택한 지역:', selectedRegion);
};

return (
<>
<SearchInput />
<RegionSelect onRegionChange={handleRegionChange} />
</>
);
};
4 changes: 2 additions & 2 deletions src/router/mainRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Outlet, Route, Routes } from 'react-router-dom';
import styled from 'styled-components';
import { Header } from '@components/common/header';
import { Footer } from '@components/common/footer';
import ABC from '@pages/abc/abc.page';
import Main from '@pages/main/main.page';
import { Search } from '@pages/search/search.page';

export function MainLayout() {
return (
Expand All @@ -23,7 +23,7 @@ const MainRouter = () => {
<Routes>
<Route path="/" element={<MainLayout />}>
<Route index element={<Main />} />
<Route path="/abc" element={<ABC />} />
<Route path="/search" element={<Search />} />
</Route>
</Routes>
</>
Expand Down

0 comments on commit 80c35a9

Please sign in to comment.