Skip to content

Commit 9062ad3

Browse files
Fix note folder breadcrumbs (#4810)
* fix: simplify note folder breadcrumbs Remove the redundant note title from the note header breadcrumb, show only folder ancestry, and use monospace styling for that path. * fix: add unassigned note folder placeholder Show a muted folder icon and Unassigned label for notes without a folder, with a Coming soon tooltip in the note header.
1 parent a6eef5b commit 9062ad3

1 file changed

Lines changed: 44 additions & 117 deletions

File tree

  • apps/desktop/src/session/components/outer-header/folder

apps/desktop/src/session/components/outer-header/folder/index.tsx

Lines changed: 44 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ import {
55
BreadcrumbItem,
66
BreadcrumbLink,
77
BreadcrumbList,
8-
BreadcrumbPage,
98
BreadcrumbSeparator,
109
} from "@hypr/ui/components/ui/breadcrumb";
1110
import { Button } from "@hypr/ui/components/ui/button";
12-
13-
import { SearchableFolderDropdown } from "./searchable-dropdown";
11+
import {
12+
Tooltip,
13+
TooltipContent,
14+
TooltipTrigger,
15+
} from "@hypr/ui/components/ui/tooltip";
1416

1517
import { useBillingAccess } from "~/auth/billing";
1618
import { FolderBreadcrumb } from "~/shared/ui/folder-breadcrumb";
1719
import * as main from "~/store/tinybase/store/main";
18-
import { useSessionTitle } from "~/store/zustand/live-title";
1920
import { useTabs } from "~/store/zustand/tabs";
2021

2122
export function FolderChain({ sessionId }: { sessionId: string }) {
@@ -26,78 +27,57 @@ export function FolderChain({ sessionId }: { sessionId: string }) {
2627
"folder_id",
2728
main.STORE_ID,
2829
);
29-
const storeTitle = main.UI.useCell(
30-
"sessions",
31-
sessionId,
32-
"title",
33-
main.STORE_ID,
34-
) as string | undefined;
35-
const title = useSessionTitle(sessionId, storeTitle);
3630

37-
const handleChangeTitle = main.UI.useSetPartialRowCallback(
38-
"sessions",
39-
sessionId,
40-
(title: string) => ({ title }),
41-
[],
42-
main.STORE_ID,
43-
);
44-
45-
if (!isPro) {
46-
return (
47-
<Breadcrumb className="ml-1.5 w-full min-w-0">
48-
<BreadcrumbList className="w-full flex-nowrap gap-0.5 overflow-hidden text-xs text-neutral-700">
49-
<BreadcrumbItem className="min-w-0 flex-1 overflow-hidden">
50-
<BreadcrumbPage className="block w-full min-w-0">
51-
<TitleInput title={title} handleChangeTitle={handleChangeTitle} />
52-
</BreadcrumbPage>
53-
</BreadcrumbItem>
54-
</BreadcrumbList>
55-
</Breadcrumb>
56-
);
31+
if (!folderId) {
32+
return <UnassignedFolderBreadcrumb />;
5733
}
5834

5935
return (
6036
<Breadcrumb className="ml-1.5 w-full min-w-0">
61-
<BreadcrumbList className="w-full flex-nowrap gap-0.5 overflow-hidden text-xs text-neutral-700">
62-
{folderId && <FolderIcon className="mr-1 h-3 w-3 shrink-0" />}
63-
{!folderId ? (
64-
<RenderIfRootNotExist
65-
title={title}
66-
handleChangeTitle={handleChangeTitle}
67-
sessionId={sessionId}
68-
/>
69-
) : (
70-
<RenderIfRootExist
71-
title={title}
72-
handleChangeTitle={handleChangeTitle}
73-
folderId={folderId}
74-
/>
75-
)}
37+
<BreadcrumbList className="w-full flex-nowrap gap-0.5 overflow-hidden font-mono text-xs text-neutral-700">
38+
<FolderIcon className="mr-1 h-3 w-3 shrink-0" />
39+
<RenderFolderBreadcrumb folderId={folderId} isPro={isPro} />
7640
</BreadcrumbList>
7741
</Breadcrumb>
7842
);
7943
}
8044

81-
function RenderIfRootExist({
45+
function UnassignedFolderBreadcrumb() {
46+
return (
47+
<Breadcrumb className="ml-1.5 w-full min-w-0">
48+
<BreadcrumbList className="w-full flex-nowrap gap-0.5 overflow-hidden font-mono text-xs text-neutral-700">
49+
<Tooltip delayDuration={0}>
50+
<TooltipTrigger asChild>
51+
<BreadcrumbItem className="flex items-center gap-1.5 text-neutral-400">
52+
<FolderIcon className="h-3 w-3 shrink-0" />
53+
<span className="cursor-default">Unassigned</span>
54+
</BreadcrumbItem>
55+
</TooltipTrigger>
56+
<TooltipContent side="bottom">Coming soon</TooltipContent>
57+
</Tooltip>
58+
</BreadcrumbList>
59+
</Breadcrumb>
60+
);
61+
}
62+
63+
function RenderFolderBreadcrumb({
8264
folderId,
83-
title,
84-
handleChangeTitle,
65+
isPro,
8566
}: {
8667
folderId: string;
87-
title: string;
88-
handleChangeTitle: (title: string) => void;
68+
isPro: boolean;
8969
}) {
9070
const openNew = useTabs((state) => state.openNew);
9171

9272
return (
93-
<>
94-
<FolderBreadcrumb
95-
folderId={folderId}
96-
renderSeparator={({ index }) =>
97-
index > 0 ? <BreadcrumbSeparator className="shrink-0" /> : null
98-
}
99-
renderCrumb={({ id, name }) => (
100-
<BreadcrumbItem className="overflow-hidden">
73+
<FolderBreadcrumb
74+
folderId={folderId}
75+
renderSeparator={({ index }) =>
76+
index > 0 ? <BreadcrumbSeparator className="shrink-0" /> : null
77+
}
78+
renderCrumb={({ id, name }) => (
79+
<BreadcrumbItem className="overflow-hidden">
80+
{isPro ? (
10181
<BreadcrumbLink asChild>
10282
<Button
10383
size="sm"
@@ -108,64 +88,11 @@ function RenderIfRootExist({
10888
{name}
10989
</Button>
11090
</BreadcrumbLink>
111-
</BreadcrumbItem>
112-
)}
113-
/>
114-
<BreadcrumbSeparator className="shrink-0" />
115-
<BreadcrumbItem className="min-w-0 flex-1 overflow-hidden">
116-
<BreadcrumbPage className="block w-full min-w-0">
117-
<TitleInput title={title} handleChangeTitle={handleChangeTitle} />
118-
</BreadcrumbPage>
119-
</BreadcrumbItem>
120-
</>
121-
);
122-
}
123-
124-
function RenderIfRootNotExist({
125-
title,
126-
handleChangeTitle,
127-
sessionId,
128-
}: {
129-
title: string;
130-
handleChangeTitle: (title: string) => void;
131-
sessionId: string;
132-
}) {
133-
return (
134-
<>
135-
<BreadcrumbItem className="shrink-0">
136-
<SearchableFolderDropdown
137-
sessionId={sessionId}
138-
trigger={
139-
<button className="text-neutral-500 outline-hidden transition-colors hover:text-neutral-700">
140-
Select folder
141-
</button>
142-
}
143-
/>
144-
</BreadcrumbItem>
145-
<BreadcrumbSeparator className="shrink-0" />
146-
<BreadcrumbItem className="min-w-0 flex-1 overflow-hidden">
147-
<BreadcrumbPage className="block w-full min-w-0">
148-
<TitleInput title={title} handleChangeTitle={handleChangeTitle} />
149-
</BreadcrumbPage>
150-
</BreadcrumbItem>
151-
</>
152-
);
153-
}
154-
155-
function TitleInput({
156-
title,
157-
handleChangeTitle,
158-
}: {
159-
title: string;
160-
handleChangeTitle: (title: string) => void;
161-
}) {
162-
return (
163-
<input
164-
type="text"
165-
placeholder="Untitled"
166-
className="block w-full min-w-0 truncate border-none bg-transparent text-neutral-700 focus:underline focus:outline-hidden"
167-
value={title ?? ""}
168-
onChange={(e) => handleChangeTitle(e.target.value)}
91+
) : (
92+
<span className="truncate text-neutral-600">{name}</span>
93+
)}
94+
</BreadcrumbItem>
95+
)}
16996
/>
17097
);
17198
}

0 commit comments

Comments
 (0)