mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 21:06:04 +00:00
public commit
This commit is contained in:
116
app/src/ui/layouts/AppShell/Breadcrumbs.tsx
Normal file
116
app/src/ui/layouts/AppShell/Breadcrumbs.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { ucFirstAllSnakeToPascalWithSpaces } from "core/utils";
|
||||
import { useMemo } from "react";
|
||||
import { TbArrowLeft, TbDots } from "react-icons/tb";
|
||||
import { Link, useLocation } from "wouter";
|
||||
import { IconButton } from "../../components/buttons/IconButton";
|
||||
import { Dropdown } from "../../components/overlay/Dropdown";
|
||||
import { useEvent } from "../../hooks/use-event";
|
||||
|
||||
export type BreadcrumbsProps = {
|
||||
path: string | string[];
|
||||
backTo?: string;
|
||||
onBack?: () => void;
|
||||
};
|
||||
|
||||
export const Breadcrumbs = ({ path: _path, backTo, onBack }: BreadcrumbsProps) => {
|
||||
const [_, navigate] = useLocation();
|
||||
const location = window.location.pathname;
|
||||
const path = Array.isArray(_path) ? _path : [_path];
|
||||
const loc = location.split("/").filter((v) => v !== "");
|
||||
const hasBack = path.length > 1;
|
||||
|
||||
const goBack = onBack
|
||||
? onBack
|
||||
: useEvent(() => {
|
||||
if (backTo) {
|
||||
navigate(backTo, { replace: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const href = loc.slice(0, path.length + 1).join("/");
|
||||
navigate(`~/${href}`, { replace: true });
|
||||
});
|
||||
|
||||
const crumbs = useMemo(
|
||||
() =>
|
||||
path.map((p, key) => {
|
||||
const last = key === path.length - 1;
|
||||
const index = loc.indexOf(p);
|
||||
const href = loc.slice(0, index + 1).join("/");
|
||||
const string = ucFirstAllSnakeToPascalWithSpaces(p);
|
||||
|
||||
return {
|
||||
last,
|
||||
href,
|
||||
string
|
||||
};
|
||||
}),
|
||||
[path, loc]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row flex-grow items-center gap-2 text-lg">
|
||||
{hasBack && (
|
||||
<IconButton
|
||||
onClick={goBack}
|
||||
Icon={TbArrowLeft}
|
||||
variant="default"
|
||||
size="lg"
|
||||
className="mr-1"
|
||||
/>
|
||||
)}
|
||||
<div className="hidden md:flex gap-2">
|
||||
<CrumbsDesktop crumbs={crumbs} />
|
||||
</div>
|
||||
<div className="flex md:hidden gap-2">{<CrumbsMobile crumbs={crumbs} />}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CrumbsDesktop = ({ crumbs }) => {
|
||||
return crumbs.map((crumb, key) => {
|
||||
return crumb.last ? <CrumbLast key={key} {...crumb} /> : <CrumbLink key={key} {...crumb} />;
|
||||
});
|
||||
};
|
||||
|
||||
const CrumbsMobile = ({ crumbs }) => {
|
||||
const [, navigate] = useLocation();
|
||||
if (crumbs.length <= 2) return <CrumbsDesktop crumbs={crumbs} />;
|
||||
const first = crumbs[0];
|
||||
const last = crumbs[crumbs.length - 1];
|
||||
const items = useMemo(
|
||||
() =>
|
||||
crumbs.slice(1, -1).map((c) => ({
|
||||
label: c.string,
|
||||
href: c.href
|
||||
})),
|
||||
[crumbs]
|
||||
);
|
||||
const onClick = useEvent((item) => navigate(`~/${item.href}`));
|
||||
|
||||
return (
|
||||
<>
|
||||
<CrumbLink {...first} />
|
||||
<Dropdown onClickItem={onClick} items={items}>
|
||||
<IconButton Icon={TbDots} variant="ghost" />
|
||||
</Dropdown>
|
||||
<span className="opacity-25 dark:font-bold font-semibold">/</span>
|
||||
<CrumbLast {...last} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CrumbLast = ({ string }) => {
|
||||
return <span className="text-nowrap dark:font-bold font-semibold">{string}</span>;
|
||||
};
|
||||
|
||||
const CrumbLink = ({ href, string }) => {
|
||||
return (
|
||||
<div className="opacity-50 flex flex-row gap-2 dark:font-bold font-semibold">
|
||||
<Link to={`~/${href}`} className="text-nowrap">
|
||||
{string}
|
||||
</Link>
|
||||
<span className="opacity-50">/</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user