small improvement

This commit is contained in:
daniel31x13
2025-09-24 15:32:36 -04:00
parent b336b04d71
commit 6a40a70e6d
6 changed files with 286 additions and 125 deletions

View File

@@ -34,8 +34,8 @@ export default function NoLinksFound({ text }: Props) {
}}
variant="accent"
>
<i className="bi-plus-lg text-3xl left-2 group-hover:ml-[4rem] absolute duration-100"></i>
<span className="group-hover:opacity-0 text-right w-full duration-100">
<i className="bi-plus-lg text-xl duration-100"></i>
<span className="group-hover:opacity-0 w-full duration-100">
{t("create_new_link")}
</span>
</Button>

View File

@@ -7,8 +7,26 @@ import { useTranslation } from "next-i18next";
import { useCollections } from "@linkwarden/router/collections";
import { useTags } from "@linkwarden/router/tags";
import { TagListing } from "./TagListing";
import { Button } from "./ui/button";
import { useUser } from "@linkwarden/router/user";
import Image from "next/image";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
export default function Sidebar({ className }: { className?: string }) {
export default function Sidebar({
className,
toggleSidebar,
sidebarIsCollapsed,
}: {
className?: string;
toggleSidebar?: () => void;
sidebarIsCollapsed?: boolean;
}) {
const { t } = useTranslation();
const [tagDisclosure, setTagDisclosure] = useState<boolean>(() => {
const storedValue = localStorage.getItem("tagDisclosure");
@@ -29,6 +47,8 @@ export default function Sidebar({ className }: { className?: string }) {
const router = useRouter();
const { data: user } = useUser();
useEffect(() => {
localStorage.setItem("tagDisclosure", tagDisclosure ? "true" : "false");
}, [tagDisclosure]);
@@ -47,105 +67,199 @@ export default function Sidebar({ className }: { className?: string }) {
return (
<div
id="sidebar"
className={`bg-base-200 h-full w-80 overflow-y-auto border-solid border border-base-200 border-r-neutral-content p-2 z-20 ${
className || ""
}`}
className={cn(
"bg-base-200 h-screen overflow-y-auto border-solid border border-base-200 border-r-neutral-content p-2 z-20",
className,
sidebarIsCollapsed ? "w-14" : "w-80"
)}
>
<div className="flex flex-col gap-1">
<SidebarHighlightLink
title={t("dashboard")}
href={`/dashboard`}
icon={"bi-house"}
active={active === `/dashboard`}
/>
<SidebarHighlightLink
title={t("links")}
href={`/links`}
icon={"bi-link-45deg"}
active={active === `/links`}
/>
<SidebarHighlightLink
title={t("pinned")}
href={`/links/pinned`}
icon={"bi-pin-angle"}
active={active === `/links/pinned`}
/>
<SidebarHighlightLink
title={t("collections")}
href={`/collections`}
icon={"bi-folder"}
active={active === `/collections`}
/>
<SidebarHighlightLink
title={t("tags")}
href={`/tags`}
icon={"bi-hash"}
active={active === `/tags`}
/>
<div
className={cn(
"flex flex-col",
sidebarIsCollapsed
? "my-auto h-full justify-between items-center gap-3"
: "gap-1"
)}
>
<div className="flex items-center justify-between mb-4">
{sidebarIsCollapsed ? (
<Image
src={"/icon.png"}
width={640}
height={136}
alt="Linkwarden Icon"
className="h-8 w-auto cursor-pointer"
onClick={() => router.push("/dashboard")}
/>
) : user?.theme === "light" ? (
<Image
src={"/linkwarden_light.png"}
width={640}
height={136}
alt="Linkwarden"
className="h-9 w-auto cursor-pointer"
onClick={() => router.push("/dashboard")}
/>
) : (
<Image
src={"/linkwarden_dark.png"}
width={640}
height={136}
alt="Linkwarden"
className="h-9 w-auto cursor-pointer"
onClick={() => router.push("/dashboard")}
/>
)}
{!sidebarIsCollapsed && (
<div className="hidden lg:block">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
onClick={toggleSidebar}
size={"icon"}
>
<i className={`bi-layout-sidebar`} />
</Button>
</TooltipTrigger>
<TooltipContent>
{sidebarIsCollapsed ? t("show_sidebar") : t("hide_sidebar")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
)}
</div>
<div
className={cn(
"flex flex-col",
sidebarIsCollapsed ? "my-auto justify-center gap-3" : "gap-1"
)}
>
<SidebarHighlightLink
title={t("dashboard")}
href={`/dashboard`}
icon={"bi-house"}
active={active === `/dashboard`}
sidebarIsCollapsed={sidebarIsCollapsed}
/>
<SidebarHighlightLink
title={t("links")}
href={`/links`}
icon={"bi-link-45deg"}
active={active === `/links`}
sidebarIsCollapsed={sidebarIsCollapsed}
/>
<SidebarHighlightLink
title={t("pinned")}
href={`/links/pinned`}
icon={"bi-pin-angle"}
active={active === `/links/pinned`}
sidebarIsCollapsed={sidebarIsCollapsed}
/>
<SidebarHighlightLink
title={t("collections")}
href={`/collections`}
icon={"bi-folder"}
active={active === `/collections`}
sidebarIsCollapsed={sidebarIsCollapsed}
/>
<SidebarHighlightLink
title={t("tags")}
href={`/tags`}
icon={"bi-hash"}
active={active === `/tags`}
sidebarIsCollapsed={sidebarIsCollapsed}
/>
</div>
{sidebarIsCollapsed && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" onClick={toggleSidebar} size={"icon"}>
<i className={`bi-layout-sidebar`} />
</Button>
</TooltipTrigger>
<TooltipContent side="right">
{sidebarIsCollapsed ? t("show_sidebar") : t("hide_sidebar")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
<Disclosure defaultOpen={collectionDisclosure}>
<Disclosure.Button
onClick={() => {
setCollectionDisclosure(!collectionDisclosure);
}}
className="flex items-center justify-between w-full text-left mb-2 pl-2 font-bold text-neutral mt-5"
>
<p className="text-sm">{t("collections")}</p>
<i
className={`bi-chevron-down ${
collectionDisclosure ? "rotate-reverse" : "rotate"
}`}
></i>
</Disclosure.Button>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform opacity-0 -translate-y-3"
enterTo="transform opacity-100 translate-y-0"
leave="transition duration-100 ease-out"
leaveFrom="transform opacity-100 translate-y-0"
leaveTo="transform opacity-0 -translate-y-3"
>
<Disclosure.Panel>
<CollectionListing />
</Disclosure.Panel>
</Transition>
</Disclosure>
<Disclosure defaultOpen={tagDisclosure}>
<Disclosure.Button
onClick={() => {
setTagDisclosure(!tagDisclosure);
}}
className="flex items-center justify-between w-full text-left mb-2 pl-2 font-bold text-neutral mt-5"
>
<p className="text-sm">{t("tags")}</p>
<i
className={`bi-chevron-down ${
tagDisclosure ? "rotate-reverse" : "rotate"
}`}
></i>
</Disclosure.Button>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform opacity-0 -translate-y-3"
enterTo="transform opacity-100 translate-y-0"
leave="transition duration-100 ease-out"
leaveFrom="transform opacity-100 translate-y-0"
leaveTo="transform opacity-0 -translate-y-3"
>
<Disclosure.Panel className="flex flex-col gap-1">
{isLoading ? (
<div className="flex flex-col gap-4">
<div className="skeleton h-4 w-full"></div>
<div className="skeleton h-4 w-full"></div>
<div className="skeleton h-4 w-full"></div>
</div>
) : (
<TagListing tags={tags} active={active} />
)}
</Disclosure.Panel>
</Transition>
</Disclosure>
{sidebarIsCollapsed ? (
<></>
) : (
<>
<Disclosure defaultOpen={collectionDisclosure}>
<Disclosure.Button
onClick={() => {
setCollectionDisclosure(!collectionDisclosure);
}}
className="flex items-center justify-between w-full text-left mb-2 pl-2 font-bold text-neutral mt-5"
>
<p className="text-sm">{t("collections")}</p>
<i
className={`bi-chevron-down ${
collectionDisclosure ? "rotate-reverse" : "rotate"
}`}
></i>
</Disclosure.Button>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform opacity-0 -translate-y-3"
enterTo="transform opacity-100 translate-y-0"
leave="transition duration-100 ease-out"
leaveFrom="transform opacity-100 translate-y-0"
leaveTo="transform opacity-0 -translate-y-3"
>
<Disclosure.Panel>
<CollectionListing />
</Disclosure.Panel>
</Transition>
</Disclosure>
<Disclosure defaultOpen={tagDisclosure}>
<Disclosure.Button
onClick={() => {
setTagDisclosure(!tagDisclosure);
}}
className="flex items-center justify-between w-full text-left mb-2 pl-2 font-bold text-neutral mt-5"
>
<p className="text-sm">{t("tags")}</p>
<i
className={`bi-chevron-down ${
tagDisclosure ? "rotate-reverse" : "rotate"
}`}
></i>
</Disclosure.Button>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform opacity-0 -translate-y-3"
enterTo="transform opacity-100 translate-y-0"
leave="transition duration-100 ease-out"
leaveFrom="transform opacity-100 translate-y-0"
leaveTo="transform opacity-0 -translate-y-3"
>
<Disclosure.Panel className="flex flex-col gap-1">
{isLoading ? (
<div className="flex flex-col gap-4">
<div className="skeleton h-4 w-full"></div>
<div className="skeleton h-4 w-full"></div>
<div className="skeleton h-4 w-full"></div>
</div>
) : (
<TagListing tags={tags} active={active} />
)}
</Disclosure.Panel>
</Transition>
</Disclosure>
</>
)}
</div>
);
}

View File

@@ -1,27 +1,56 @@
import Link from "next/link";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
export default function SidebarHighlightLink({
title,
href,
icon,
active,
sidebarIsCollapsed,
}: {
title: string;
href: string;
icon: string;
active?: boolean;
sidebarIsCollapsed?: boolean;
}) {
return (
<Link href={href}>
<div
title={title}
className={`${
active || false ? "bg-primary/20" : "hover:bg-neutral/20"
} duration-200 px-3 py-1 cursor-pointer flex items-center gap-2 w-full rounded-lg capitalize`}
>
<i className={`${icon} text-primary text-xl drop-shadow`}></i>
<p className="truncate w-full font-semibold text-sm">{title}</p>
</div>
</Link>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Link href={href} title={title}>
<div
className={cn(
active ? "bg-primary/20" : "hover:bg-neutral/20",
"duration-200 cursor-pointer flex items-center gap-2 capitalize",
sidebarIsCollapsed
? "rounded-md h-8 w-8"
: "rounded-lg px-3 py-1"
)}
>
<i
className={cn(
icon,
"text-primary text-xl drop-shadow",
sidebarIsCollapsed && "w-full text-center"
)}
></i>
{!sidebarIsCollapsed && (
<p className="truncate w-full font-semibold text-sm">{title}</p>
)}
</div>
</Link>
</TooltipTrigger>
{sidebarIsCollapsed && (
<TooltipContent side="right">{title}</TooltipContent>
)}
</Tooltip>
</TooltipProvider>
);
}

View File

@@ -30,7 +30,7 @@ export default function CenteredForm({
width={640}
height={136}
alt="Linkwarden"
className="h-12 w-fit mx-auto"
className="h-12 w-auto mx-auto"
/>
) : (
<Image
@@ -38,7 +38,7 @@ export default function CenteredForm({
width={640}
height={136}
alt="Linkwarden"
className="h-12 w-fit mx-auto"
className="h-12 w-auto mx-auto"
/>
)}
{text && (

View File

@@ -10,27 +10,35 @@ interface Props {
export default function MainLayout({ children }: Props) {
const showAnnouncementBar = localStorage.getItem("showAnnouncementBar");
const sidebarState = localStorage.getItem("sidebarIsCollapsed");
const [showAnnouncement, setShowAnnouncement] = useState(
showAnnouncementBar ? showAnnouncementBar === "true" : true
);
const [sidebarIsCollapsed, setSidebarIsCollapsed] = useState(
sidebarState ? sidebarState === "true" : false
);
useEffect(() => {
getLatestVersion(setShowAnnouncement);
}, []);
useEffect(() => {
if (showAnnouncement) {
localStorage.setItem("showAnnouncementBar", "true");
setShowAnnouncement(true);
} else if (!showAnnouncement) {
localStorage.setItem("showAnnouncementBar", "false");
setShowAnnouncement(false);
}
localStorage.setItem(
"showAnnouncementBar",
showAnnouncement ? "true" : "false"
);
}, [showAnnouncement]);
const toggleAnnouncementBar = () => {
setShowAnnouncement(!showAnnouncement);
};
useEffect(() => {
localStorage.setItem(
"sidebarIsCollapsed",
sidebarIsCollapsed ? "true" : "false"
);
}, [sidebarIsCollapsed]);
const toggleAnnouncementBar = () => setShowAnnouncement(!showAnnouncement);
const toggleSidebar = () => setSidebarIsCollapsed(!sidebarIsCollapsed);
return (
<div className="flex" data-testid="dashboard-wrapper">
@@ -38,11 +46,19 @@ export default function MainLayout({ children }: Props) {
<Announcement toggleAnnouncementBar={toggleAnnouncementBar} />
)}
<div className="hidden lg:block">
<Sidebar className={`fixed top-0`} />
<Sidebar
className={`${sidebarIsCollapsed ? "w-14" : "w-80"}`}
toggleSidebar={toggleSidebar}
sidebarIsCollapsed={sidebarIsCollapsed}
/>
</div>
<div
className={`lg:w-[calc(100%-320px)] w-full sm:pb-0 pb-20 flex flex-col min-h-screen lg:ml-80`}
className={`${
sidebarIsCollapsed
? "lg:w-[calc(100%-56px)]"
: "lg:w-[calc(100%-320px)]"
} w-full sm:pb-0 pb-20 flex flex-col min-h-screen`}
>
<Navbar />
{children}

View File

@@ -521,5 +521,7 @@
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Use this Tag while creating or editing Links!",
"accept_promotional_emails": "Get notified about new features and offers via email."
"accept_promotional_emails": "Get notified about new features and offers via email.",
"show_sidebar": "Show Sidebar",
"hide_sidebar": "Hide Sidebar"
}