diff --git a/web/src/components/ActivityCalendar/CalendarCell.tsx b/web/src/components/ActivityCalendar/CalendarCell.tsx index e3d1d426..33549d94 100644 --- a/web/src/components/ActivityCalendar/CalendarCell.tsx +++ b/web/src/components/ActivityCalendar/CalendarCell.tsx @@ -71,7 +71,7 @@ export const CalendarCell = memo((props: CalendarCellProps) => { ); - const shouldShowTooltip = tooltipText && !disableTooltip; + const shouldShowTooltip = day.count > 0 && tooltipText && !disableTooltip; if (!shouldShowTooltip) { return button; diff --git a/web/src/components/AppSidebar/TagsSection.tsx b/web/src/components/AppSidebar/TagsSection.tsx index 49cc8238..f8b95d14 100644 --- a/web/src/components/AppSidebar/TagsSection.tsx +++ b/web/src/components/AppSidebar/TagsSection.tsx @@ -1,7 +1,7 @@ -import { ListIcon, ListTreeIcon } from "lucide-react"; -import { useEffect, useMemo, useState } from "react"; +import { HashIcon, ListIcon, ListTreeIcon } from "lucide-react"; +import { forwardRef, useMemo } from "react"; import { useMemoFilterContext } from "@/contexts/MemoFilterContext"; -import { useLocalStorage } from "@/hooks"; +import { useLocalStorage, useOverflowTitle } from "@/hooks"; import { cn } from "@/lib/utils"; import { useTranslate } from "@/utils/i18n"; import TagTree from "../TagTree"; @@ -12,66 +12,59 @@ interface Props { onSelect?: () => void; } -const TagPath = ({ tag }: { tag: string }) => { +const TagPath = forwardRef(({ tag }, ref) => { const segments = tag.split("/"); return ( - + {segments.map((segment, index) => ( - {index > 0 && /} + {index > 0 && /} {segment} ))} ); -}; +}); +TagPath.displayName = "TagPath"; -const getExpandableTagPaths = (tags: [tag: string, amount: number][]) => { - const paths = new Set(); +interface FlatTagRowProps { + tag: string; + amount: number; + active: boolean; + onClick: () => void; +} - for (const [tag] of tags) { - const segments = tag.split("/"); - for (let index = 1; index < segments.length; index++) { - paths.add(segments.slice(0, index).join("/")); - } - } +const FlatTagRow = ({ tag, amount, active, onClick }: FlatTagRowProps) => { + const { ref, title } = useOverflowTitle(`#${tag}`); - return paths; -}; - -const getParentTagPaths = (tag: string) => { - const segments = tag.split("/"); - return segments.slice(0, -1).map((_, index) => segments.slice(0, index + 1).join("/")); + return ( + + ); }; const TagsSection = ({ tagCount, onSelect }: Props) => { const t = useTranslate(); const { getFiltersByFactor, addFilter, removeFilter } = useMemoFilterContext(); const [treeMode, setTreeMode] = useLocalStorage("tag-view-as-tree", false); - const [expandedTagPaths, setExpandedTagPaths] = useState>(() => new Set()); const activeTags = new Set(getFiltersByFactor("tagSearch").map((filter) => filter.value)); const activeTag = activeTags.values().next().value as string | undefined; const tags = useMemo(() => Object.entries(tagCount).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])), [tagCount]); - const expandableTagPaths = useMemo(() => getExpandableTagPaths(tags), [tags]); - - useEffect(() => { - setExpandedTagPaths((current) => { - const next = new Set(Array.from(current).filter((path) => expandableTagPaths.has(path))); - return next.size === current.size ? current : next; - }); - }, [expandableTagPaths]); - - useEffect(() => { - if (!treeMode || !activeTag) { - return; - } - - setExpandedTagPaths((current) => { - const next = new Set([...current, ...getParentTagPaths(activeTag)]); - return next.size === current.size ? current : next; - }); - }, [activeTag, treeMode]); if (tags.length === 0) { return null; @@ -88,94 +81,50 @@ const TagsSection = ({ tagCount, onSelect }: Props) => { onSelect?.(); }; - const handleToggleBranch = (tag: string) => { - setExpandedTagPaths((current) => { - const next = new Set(current); - if (next.has(tag)) { - next.delete(tag); - } else { - next.add(tag); - } - return next; - }); - }; - return (
+
} > - + {t("common.tags")} - {tags.length} + {tags.length}
{treeMode ? ( - + ) : (
- {tags.map(([tag, amount]) => { - const active = activeTags.has(tag); - return ( - - ); - })} + {tags.map(([tag, amount]) => ( + handleTagClick(tag)} /> + ))}
)}
diff --git a/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx b/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx index be299939..c3517124 100644 --- a/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx +++ b/web/src/components/MemoDetailSidebar/MemoDetailSidebar.tsx @@ -9,6 +9,7 @@ import { getRelationBuckets, getRelationMemo } from "@/components/MemoMetadata/R import { useResolvedRelationMemos } from "@/components/MemoMetadata/Relation/useResolvedRelationMemos"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { useInstance } from "@/contexts/InstanceContext"; +import { useOverflowTitle } from "@/hooks"; import useCurrentUser from "@/hooks/useCurrentUser"; import { useUpdateMemo } from "@/hooks/useMemoQueries"; import { cn } from "@/lib/utils"; @@ -35,6 +36,7 @@ const Section = ({ label, children }: { label: string; children: React.ReactNode ); const BacklinkRow = ({ relation, snippet }: { relation: MemoRelation; snippet: string }) => { + const { ref, title } = useOverflowTitle(snippet); const relatedMemo = getRelationMemo(relation, "referenced"); if (!relatedMemo) { return null; @@ -44,11 +46,13 @@ const BacklinkRow = ({ relation, snippet }: { relation: MemoRelation; snippet: s - {snippet} + + {snippet} + ); }; diff --git a/web/src/components/TagTree.tsx b/web/src/components/TagTree.tsx index a0dff7d3..c1579ba6 100644 --- a/web/src/components/TagTree.tsx +++ b/web/src/components/TagTree.tsx @@ -1,5 +1,6 @@ -import { ChevronRightIcon } from "lucide-react"; +import { HashIcon } from "lucide-react"; import { useMemo } from "react"; +import { useOverflowTitle } from "@/hooks"; import { cn } from "@/lib/utils"; import { useTranslate } from "@/utils/i18n"; @@ -13,9 +14,7 @@ interface TagTreeNode { interface Props { tagAmounts: [tag: string, amount: number][]; activeTag?: string; - expandedTagPaths: ReadonlySet; onTagClick: (tag: string) => void; - onToggleBranch: (tag: string) => void; } export const buildTagTree = (tagAmounts: [tag: string, amount: number][]) => { @@ -53,98 +52,65 @@ export const buildTagTree = (tagAmounts: [tag: string, amount: number][]) => { return root.subTags; }; -const TagMark = () => ( - -); +const TagMark = () =>