feat: implement memo detail sidebar

This commit is contained in:
Steven 2024-07-01 23:06:10 +08:00
parent 05c6edfe2f
commit 291b815653
8 changed files with 170 additions and 62 deletions

View File

@ -26,7 +26,7 @@ const ExploreSidebarDrawer = () => {
<Icon.Search className="w-5 h-auto dark:text-gray-400" /> <Icon.Search className="w-5 h-auto dark:text-gray-400" />
</IconButton> </IconButton>
<Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}> <Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}>
<div className="w-full h-full px-5 bg-zinc-100 dark:bg-zinc-900"> <div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900">
<ExploreSidebar className="py-4" /> <ExploreSidebar className="py-4" />
</div> </div>
</Drawer> </Drawer>

View File

@ -26,7 +26,7 @@ const HomeSidebarDrawer = () => {
<Icon.Search className="w-5 h-auto dark:text-gray-400" /> <Icon.Search className="w-5 h-auto dark:text-gray-400" />
</IconButton> </IconButton>
<Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}> <Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}>
<div className="w-full h-full px-5 bg-zinc-100 dark:bg-zinc-900"> <div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900">
<HomeSidebar className="py-4" /> <HomeSidebar className="py-4" />
</div> </div>
</Drawer> </Drawer>

View File

@ -0,0 +1,48 @@
import clsx from "clsx";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { useTranslate } from "@/utils/i18n";
import Icon from "../Icon";
interface Props {
memo: Memo;
className?: string;
}
const MemoDetailSidebar = ({ memo, className }: Props) => {
const t = useTranslate();
if (!memo.property) {
return;
}
return (
<aside
className={clsx(
"relative w-full h-auto max-h-screen overflow-auto hide-scrollbar flex flex-col justify-start items-start",
className,
)}
>
<div className="flex flex-col justify-start items-start w-full mt-1 px-1 h-auto shrink-0 flex-nowrap hide-scrollbar">
<div className="flex flex-row justify-start items-center w-full gap-1 mb-1 text-sm leading-6 text-gray-400 select-none">
<span>{t("common.tags")}</span>
{memo.property.tags.length > 0 && <span className="shrink-0">({memo.property.tags.length})</span>}
</div>
<div className="w-full flex flex-row justify-start items-center relative flex-wrap gap-x-2 gap-y-1">
{memo.property.tags.map((tag) => (
<div
key={tag}
className="shrink-0 w-auto max-w-full text-sm rounded-md leading-6 flex flex-row justify-start items-center select-none hover:opacity-80 text-gray-600 dark:text-gray-400 dark:border-zinc-800"
>
<Icon.Hash className="group-hover:hidden w-4 h-auto shrink-0 opacity-40" />
<div className={clsx("inline-flex flex-nowrap ml-0.5 gap-0.5 cursor-pointer max-w-[calc(100%-16px)]")}>
<span className="truncate dark:opacity-80">{tag}</span>
</div>
</div>
))}
</div>
</div>
</aside>
);
};
export default MemoDetailSidebar;

View File

@ -0,0 +1,41 @@
import { Drawer, IconButton } from "@mui/joy";
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import { Memo } from "@/types/proto/api/v1/memo_service";
import Icon from "../Icon";
import MemoDetailSidebar from "./MemoDetailSidebar";
interface Props {
memo: Memo;
}
const MemoDetailSidebarDrawer = ({ memo }: Props) => {
const location = useLocation();
const [open, setOpen] = useState(false);
useEffect(() => {
setOpen(false);
}, [location.pathname]);
const toggleDrawer = (inOpen: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => {
if (event.type === "keydown" && ((event as React.KeyboardEvent).key === "Tab" || (event as React.KeyboardEvent).key === "Shift")) {
return;
}
setOpen(inOpen);
};
return (
<>
<IconButton onClick={toggleDrawer(true)}>
<Icon.GanttChart className="w-5 h-auto dark:text-gray-400" />
</IconButton>
<Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}>
<div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900">
<MemoDetailSidebar className="py-4" memo={memo} />
</div>
</Drawer>
</>
);
};
export default MemoDetailSidebarDrawer;

View File

@ -0,0 +1,4 @@
import MemoDetailSidebar from "./MemoDetailSidebar";
import MemoDetailSidebarDrawer from "./MemoDetailSidebarDrawer";
export { MemoDetailSidebar, MemoDetailSidebarDrawer };

View File

@ -26,7 +26,7 @@ const TimelineSidebarDrawer = () => {
<Icon.Search className="w-5 h-auto dark:text-gray-400" /> <Icon.Search className="w-5 h-auto dark:text-gray-400" />
</IconButton> </IconButton>
<Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}> <Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}>
<div className="w-full h-full px-5 bg-zinc-100 dark:bg-zinc-900"> <div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900">
<TimelineSidebar className="py-4" /> <TimelineSidebar className="py-4" />
</div> </div>
</Drawer> </Drawer>

View File

@ -62,10 +62,10 @@ const UserStatisticsView = () => {
}; };
return ( return (
<div className="w-full border mt-2 py-2 px-3 rounded-lg space-y-0.5 text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 dark:border-zinc-800"> <div className="group w-full border mt-2 py-2 px-3 rounded-lg space-y-0.5 text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 dark:border-zinc-800">
<div className="w-full mb-1 flex flex-row justify-between items-center"> <div className="w-full mb-1 flex flex-row justify-between items-center">
<p className="text-sm font-medium leading-6 dark:text-gray-500">{t("common.statistics")}</p> <p className="text-sm font-medium leading-6 dark:text-gray-500">{t("common.statistics")}</p>
<div className=""> <div className="group-hover:block hidden">
<Tooltip title={"Refresh"} placement="top"> <Tooltip title={"Refresh"} placement="top">
<Icon.RefreshCcw <Icon.RefreshCcw
className="text-gray-400 w-4 h-auto cursor-pointer opacity-60 hover:opacity-100" className="text-gray-400 w-4 h-auto cursor-pointer opacity-60 hover:opacity-100"

View File

@ -1,14 +1,17 @@
import { Button } from "@mui/joy"; import { Button } from "@mui/joy";
import clsx from "clsx";
import { ClientError } from "nice-grpc-web"; import { ClientError } from "nice-grpc-web";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import Icon from "@/components/Icon"; import Icon from "@/components/Icon";
import { MemoDetailSidebar, MemoDetailSidebarDrawer } from "@/components/MemoDetailSidebar";
import showMemoEditorDialog from "@/components/MemoEditor/MemoEditorDialog"; import showMemoEditorDialog from "@/components/MemoEditor/MemoEditorDialog";
import MemoView from "@/components/MemoView"; import MemoView from "@/components/MemoView";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import useNavigateTo from "@/hooks/useNavigateTo"; import useNavigateTo from "@/hooks/useNavigateTo";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { useMemoStore } from "@/store/v1"; import { useMemoStore } from "@/store/v1";
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service"; import { MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service";
import { Memo } from "@/types/proto/api/v1/memo_service"; import { Memo } from "@/types/proto/api/v1/memo_service";
@ -16,6 +19,7 @@ import { useTranslate } from "@/utils/i18n";
const MemoDetail = () => { const MemoDetail = () => {
const t = useTranslate(); const t = useTranslate();
const { md } = useResponsiveWidth();
const params = useParams(); const params = useParams();
const navigateTo = useNavigateTo(); const navigateTo = useNavigateTo();
const currentUser = useCurrentUser(); const currentUser = useCurrentUser();
@ -77,8 +81,13 @@ const MemoDetail = () => {
return ( return (
<section className="@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8"> <section className="@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8">
<MobileHeader /> {!md && (
<div className="w-full px-4 sm:px-6"> <MobileHeader>
<MemoDetailSidebarDrawer memo={memo} />
</MobileHeader>
)}
<div className={clsx("w-full flex flex-row justify-start items-start px-4 sm:px-6 gap-4")}>
<div className={clsx(md ? "w-[calc(100%-15rem)]" : "w-full")}>
{parentMemo && ( {parentMemo && (
<div className="w-auto inline-block mb-2"> <div className="w-auto inline-block mb-2">
<Link <Link
@ -93,7 +102,7 @@ const MemoDetail = () => {
)} )}
<MemoView <MemoView
key={`${memo.name}-${memo.displayTime}`} key={`${memo.name}-${memo.displayTime}`}
className="shadow hover:shadow-xl transition-all" className="shadow hover:shadow-md transition-all"
memo={memo} memo={memo}
compact={false} compact={false}
showCreator showCreator
@ -138,6 +147,12 @@ const MemoDetail = () => {
</div> </div>
</div> </div>
</div> </div>
{md && (
<div className="sticky top-0 left-0 shrink-0 -mt-6 w-56 h-full">
<MemoDetailSidebar className="py-6" memo={memo} />
</div>
)}
</div>
</section> </section>
); );
}; };