Sidebar: its hover hints are the site's tooltip
Soon items, the workspace name, the workspace menu's names and the project's Overview link use Hint. The reorder hint for pinned projects moves from the whole row to the grip, so it no longer stacks on the pin button's own tooltip.
1 file+24−200/1 viewed
| 8 | 8 | import { AgentButton, InboxBell } from "./inbox"; | |
| 9 | 9 | import { PinButton } from "./pin-button"; | |
| 10 | 10 | import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; | |
| 11 | + | import { Hint } from "./ui/hint"; | |
| 11 | 12 | import { Sheet, SheetContent, SheetTitle, SheetTrigger } from "./ui/sheet"; | |
| 12 | 13 | import { StatusDot, useSiteStatus } from "./footer"; | |
| 13 | 14 | import { Logo, Mark } from "./logo"; | |
| ⋯ | |||
| 136 | 137 | */ | |
| 137 | 138 | function SidebarSoon({ icon, children, about }: { icon: ReactNode; children: ReactNode; about: string }) { | |
| 138 | 139 | return ( | |
| 139 | − | <div | |
| 140 | − | title={about} | |
| 141 | − | aria-disabled="true" | |
| 142 | − | className="flex h-8 cursor-default items-center gap-2.5 rounded-md px-2 text-[0.8125rem] text-faint" | |
| 143 | − | > | |
| 144 | − | <span className="shrink-0 opacity-70">{icon}</span> | |
| 145 | − | <span className="grow truncate">{children}</span> | |
| 146 | − | <SoonPill /> | |
| 147 | − | </div> | |
| 140 | + | <Hint label={about} side="right"> | |
| 141 | + | <div aria-disabled="true" className="flex h-8 cursor-default items-center gap-2.5 rounded-md px-2 text-[0.8125rem] text-faint"> | |
| 142 | + | <span className="shrink-0 opacity-70">{icon}</span> | |
| 143 | + | <span className="grow truncate">{children}</span> | |
| 144 | + | <span className="sr-only">{about}</span> | |
| 145 | + | <SoonPill /> | |
| 146 | + | </div> | |
| 147 | + | </Hint> | |
| 148 | 148 | ); | |
| 149 | 149 | } | |
| 150 | 150 | ||
| ⋯ | |||
| 167 | 167 | }) { | |
| 168 | 168 | const { pathname } = useLocation(); | |
| 169 | 169 | return ( | |
| 170 | + | <Hint label={about} side="right"> | |
| 170 | 171 | <NavLink | |
| 171 | 172 | to={to} | |
| 172 | − | title={about} | |
| 173 | 173 | prefetch="intent" | |
| 174 | 174 | className={({ isActive }) => { | |
| 175 | 175 | const current = isActive || (also ?? []).some((prefix) => pathname === prefix || pathname.startsWith(prefix + "/")); | |
| ⋯ | |||
| 182 | 182 | <span className="min-w-0 grow truncate">{children}</span> | |
| 183 | 183 | <SoonPill /> | |
| 184 | 184 | </NavLink> | |
| 185 | + | </Hint> | |
| 185 | 186 | ); | |
| 186 | 187 | } | |
| 187 | 188 | ||
| ⋯ | |||
| 234 | 235 | <DropdownMenu> | |
| 235 | 236 | {/* The name goes to the workspace; only the arrows switch it. The | |
| 236 | 237 | name gets all the room there is, and the whole of it on hover. */} | |
| 238 | + | <Hint label={workspace ? `${label} · g1t.sh/${workspace.slug}` : undefined}> | |
| 237 | 239 | <Link | |
| 238 | 240 | to={current ? `/${current}` : "/workspaces/new"} | |
| 239 | 241 | prefetch="intent" | |
| 240 | − | title={workspace ? `${label} · g1t.sh/${workspace.slug}` : undefined} | |
| 241 | 242 | className="flex h-9 min-w-0 grow items-center gap-2 rounded-md px-2 transition-colors hover:bg-raised" | |
| 242 | 243 | > | |
| 243 | 244 | {workspace ? ( | |
| ⋯ | |||
| 247 | 248 | )} | |
| 248 | 249 | <span className="min-w-0 truncate text-[0.8125rem] font-medium">{label}</span> | |
| 249 | 250 | </Link> | |
| 251 | + | </Hint> | |
| 250 | 252 | <DropdownMenuTrigger | |
| 251 | 253 | aria-label="Switch workspace" | |
| 252 | 254 | className="flex h-9 w-7 shrink-0 items-center justify-center rounded-md text-faint outline-none transition-colors hover:bg-raised hover:text-fg focus-visible:ring-2 focus-visible:ring-accent data-[state=open]:bg-raised data-[state=open]:text-fg" | |
| ⋯ | |||
| 274 | 276 | )} | |
| 275 | 277 | <DropdownMenuLabel>Workspaces</DropdownMenuLabel> | |
| 276 | 278 | {(user.workspaces ?? []).map((membership) => ( | |
| 277 | − | <DropdownMenuItem asChild key={membership.slug}> | |
| 278 | − | <Link to={`/${membership.slug}`} title={displayName(membership)}> | |
| 279 | + | <Hint key={membership.slug} label={displayName(membership)} side="right"> | |
| 280 | + | <DropdownMenuItem asChild> | |
| 281 | + | <Link to={`/${membership.slug}`}> | |
| 279 | 282 | <Avatar name={membership.slug} image={membership.avatar} size={24} square /> | |
| 280 | 283 | <span className="flex min-w-0 grow flex-col leading-tight"> | |
| 281 | 284 | <span className="truncate">{displayName(membership)}</span> | |
| ⋯ | |||
| 289 | 292 | )} | |
| 290 | 293 | </Link> | |
| 291 | 294 | </DropdownMenuItem> | |
| 295 | + | </Hint> | |
| 292 | 296 | ))} | |
| 293 | 297 | <DropdownMenuSeparator /> | |
| 294 | 298 | <DropdownMenuItem asChild> | |
| ⋯ | |||
| 655 | 659 | event.preventDefault(); | |
| 656 | 660 | move(index, event.key === "ArrowUp" ? -1 : 1); | |
| 657 | 661 | }} | |
| 658 | − | title={pinned.length > 1 ? "Drag, or Alt and an arrow key, to reorder" : undefined} | |
| 659 | 662 | className={`group/pin relative ${dragging === index ? "opacity-50" : ""}`} | |
| 660 | 663 | > | |
| 661 | 664 | {row(project, true)} | |
| 662 | 665 | {pinned.length > 1 && ( | |
| 663 | − | <GripVertical | |
| 664 | − | size={12} | |
| 665 | − | aria-hidden="true" | |
| 666 | − | className="pointer-events-none absolute top-1/2 -left-1.5 -translate-y-1/2 text-faint opacity-0 transition-opacity group-hover/pin:opacity-100" | |
| 667 | − | /> | |
| 666 | + | <Hint label="Drag, or Alt and an arrow key, to reorder" side="right"> | |
| 667 | + | <span className="absolute top-1/2 -left-1.5 -translate-y-1/2 cursor-grab text-faint opacity-0 transition-opacity group-hover/pin:opacity-100"> | |
| 668 | + | <GripVertical size={12} aria-hidden="true" /> | |
| 669 | + | </span> | |
| 670 | + | </Hint> | |
| 668 | 671 | )} | |
| 669 | 672 | </li> | |
| 670 | 673 | ))} | |
| ⋯ | |||
| 903 | 906 | return ( | |
| 904 | 907 | <nav aria-label={`${repo.namespace}/${repo.name}`} className={PANEL}> | |
| 905 | 908 | <BackRow to={back.to} label={back.label} /> | |
| 909 | + | <Hint label="Overview" side="right"> | |
| 906 | 910 | <NavLink | |
| 907 | 911 | to={base} | |
| 908 | 912 | end | |
| 909 | 913 | prefetch="intent" | |
| 910 | − | title="Overview" | |
| 911 | 914 | className={({ isActive }) => | |
| 912 | 915 | `mt-2 flex items-center gap-2 rounded-md px-2 py-1.5 transition-colors ${isActive ? "bg-raised" : "hover:bg-raised/60"}` | |
| 913 | 916 | } | |
| ⋯ | |||
| 920 | 923 | <span className="font-semibold text-fg">{repo.name}</span> | |
| 921 | 924 | </span> | |
| 922 | 925 | </NavLink> | |
| 926 | + | </Hint> | |
| 923 | 927 | {/* A project's pages, in the order people use them. A page with more | |
| 924 | 928 | than one view shows them as tabs across its top. */} | |
| 925 | 929 | <Rule /> | |