React : BreadCrumb + Tab

728x90
반응형

00. BreadCrumb.tsx

import { useLocation, Link } from 'react-router-dom';

function BreadCrumb() {
    const location = useLocation();
    const { pathname, search } = location;

    const pathSegments = pathname.split('/').filter((segment) => segment);
    const searchSegments = search
        .replace('?', '')
        .split('&')
        .filter((segment) => segment);

    return (
        <nav aria-label="breadcrumb" className="flex items-center gap-2 text-gray-600">
            <Link to="/" className="hover:text-blue-500">
                Home
            </Link>

            {/* 경로에 따른 BreadCrumb */}
            {pathSegments.map((segment, index) => {
                const pathToSegment = `/${pathSegments.slice(0, index + 1).join('/')}`;
                const isLastSegment = index === pathSegments.length - 1;

                return (
                    <span key={`path-${index}`} className="flex items-center">
                        <span className="mx-2">/</span>
                        {isLastSegment ? (
                            <span className="text-gray-500">{decodeURIComponent(segment)}</span>
                        ) : (
                            <Link to={pathToSegment} className="hover:text-blue-500">
                                {decodeURIComponent(segment)}
                            </Link>
                        )}
                    </span>
                );
            })}

            {/* 쿼리 파라미터에 따른 BreadCrumb */}
            {searchSegments.map((segment, index) => {
                const queryToSegment = `?${searchSegments.slice(0, index + 1).join('&')}`;
                const isLastQuery = index === searchSegments.length - 1;

                return (
                    <span key={`query-${index}`} className="flex items-center">
                        <span className="mx-2">/</span>
                        {isLastQuery ? (
                            <span className="text-gray-500"> {decodeURIComponent(segment)}</span>
                        ) : (
                            <Link to={queryToSegment} className="hover:text-blue-500">
                                {decodeURIComponent(segment)}
                            </Link>
                        )}
                    </span>
                );
            })}
        </nav>
    );
}

export default BreadCrumb;

 

 

 

 

01. Router 설정

import './App.css';
import './index.css';

import { Route, Routes } from 'react-router-dom';
import Home from './Home';
import Layout from './Layout';
import { RecoilRoot } from 'recoil';

function App() {
    return (
        <>
            <RecoilRoot>
                <Routes>
                    <Route path="/" element={<Home />} />

                    <Route path="/test" element={<Layout />} />
                    <Route path="/test/*" element={<Layout />} />
                </Routes>
            </RecoilRoot>
        </>
    );
}

export default App;

 

 

02.Layout.tsx

import Aside from './components/url/Aside';
import Content from './Content';

function Layout() {
    return (
        <div className="flex gap-2 m-2">
            <Aside />
            <Content />
        </div>
    );
}

export default Layout;

 

 

03.Aside.tsx

import { NavLink } from 'react-router-dom';
import { useRecoilState } from 'recoil';
import { selectedDataAtom } from '../../store/selectedData';

function Aside() {
    const [selectedData, setSelectedData] = useRecoilState(selectedDataAtom);

    const handleClick = (data: string) => {
        if (selectedData.includes(data)) return;
        setSelectedData((prev) => [...prev, data]);
    };

    const context = 'http://12345/test.tsx';
    const decode = encodeURIComponent(context);

    return (
        <div>
            <ul>
                <NavLink to={`/test/${decode}/1`} className=" [&.active]:text-indigo-600 text-gray-600">
                    <p onClick={() => handleClick('1')} className="w-[200px] text-center">
                        1
                    </p>
                </NavLink>
                <NavLink to={`/test/${decode}/2`} className=" [&.active]:text-indigo-600 text-gray-600">
                    <p onClick={() => handleClick('2')} className="w-[200px] text-center">
                        2
                    </p>
                </NavLink>
            </ul>
        </div>
    );
}

export default Aside;

 

 

04. Content.tsx

import { useLocation, useNavigate } from 'react-router-dom';
import BreadCrumb from './BreadCrumb';
import { useRecoilState } from 'recoil';
import { selectedDataAtom } from './store/selectedData';

function Content() {
    const [selectedData, setSelectedData] = useRecoilState(selectedDataAtom);
    const location = useLocation();
    const { pathname, search } = location;

    const navigate = useNavigate();
    const params = new URLSearchParams(search);

    const handleClick = (item: string) => {
        // 현재 pathname을 '/'로 분리하여 배열로 나눔
        const pathSegments = pathname.split('/');

        // 마지막 경로(예: "2")를 변경
        pathSegments[pathSegments.length - 1] = item;

        // 새로운 pathname을 생성
        const newPathname = pathSegments.join('/');

        // 기존 쿼리 문자열은 그대로 유지
        navigate(`${newPathname}${search}`);
    };

    return (
        <div>
            <BreadCrumb />

            <div className="flex gap-1">
                {selectedData.map((item) => {
                    return (
                        <ul key={item}>
                            <li className="bg-slate-300 text-center w-[50px] " onClick={() => handleClick(item)}>
                                {' '}
                                {item}
                            </li>
                        </ul>
                    );
                })}
            </div>
        </div>
    );
}

export default Content;

 

 

끝.

반응형