mirror of
https://github.com/linkwarden/linkwarden.git
synced 2026-03-03 03:47:02 +00:00
small improvement
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user