chore: refactor tooltip usage and remove unused components

This commit is contained in:
Johnny
2025-12-22 08:53:54 +08:00
parent ec5db24b2a
commit e62eebf906
8 changed files with 195 additions and 244 deletions
+3 -2
View File
@@ -1,6 +1,7 @@
import { useTheme } from "next-themes";
import { useEffect } from "react";
import { Outlet } from "react-router-dom";
import { TooltipProvider } from "@/components/ui/tooltip";
import { useWorkspaceStore } from "@/stores";
import useNavigateTo from "./hooks/useNavigateTo";
import { FeatureType } from "./stores/workspace";
@@ -63,9 +64,9 @@ function App() {
}, [colorScheme]);
return (
<>
<TooltipProvider>
<Outlet />
</>
</TooltipProvider>
);
}
+24 -28
View File
@@ -4,7 +4,7 @@ import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { toast } from "sonner";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { absolutifyLink } from "@/helpers/utils";
import useNavigateTo from "@/hooks/useNavigateTo";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
@@ -76,33 +76,29 @@ const CollectionView = (props: Props) => {
<p className="text-sm text-muted-foreground">{collection.description}</p>
</div>
<div className="flex flex-row justify-end items-center shrink-0 gap-2">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Link
className="w-auto text-muted-foreground cursor-pointer hover:text-foreground"
to={`/c/${collection.name}`}
target="_blank"
>
<Icon.Share className="w-4 h-auto" />
</Link>
</TooltipTrigger>
<TooltipContent>Share</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
className="w-auto text-muted-foreground cursor-pointer hover:text-foreground"
onClick={() => handleOpenAllShortcutsButtonClick()}
>
<Icon.ArrowUpRight className="w-5 h-auto" />
</button>
</TooltipTrigger>
<TooltipContent>Open all</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Link
className="w-auto text-muted-foreground cursor-pointer hover:text-foreground"
to={`/c/${collection.name}`}
target="_blank"
>
<Icon.Share className="w-4 h-auto" />
</Link>
</TooltipTrigger>
<TooltipContent>Share</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<button
className="w-auto text-muted-foreground cursor-pointer hover:text-foreground"
onClick={() => handleOpenAllShortcutsButtonClick()}
>
<Icon.ArrowUpRight className="w-5 h-auto" />
</button>
</TooltipTrigger>
<TooltipContent>Open all</TooltipContent>
</Tooltip>
{showAdminActions && (
<Dropdown
trigger={
+113 -117
View File
@@ -4,10 +4,10 @@ import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { toast } from "sonner";
import { AnimatedCard } from "@/components/ui/animated-card";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { Card } from "@/components/ui/card";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { absolutifyLink } from "@/helpers/utils";
import { useUserStore, useViewStore } from "@/stores";
import { Shortcut } from "@/types/proto/api/v1/shortcut_service";
@@ -38,125 +38,121 @@ const ShortcutCard = (props: Props) => {
};
return (
<TooltipProvider>
<AnimatedCard
className={classNames("group p-4 w-full flex flex-col justify-start items-start hover:shadow-md transition-shadow duration-200")}
>
<div className="w-full flex flex-row justify-between items-center">
<div className="w-[calc(100%-16px)] flex flex-row justify-start items-center mr-1 shrink-0">
<Card className={classNames("group p-4 w-full flex flex-col justify-start items-start hover:shadow-md transition-shadow duration-200")}>
<div className="w-full flex flex-row justify-between items-center">
<div className="w-[calc(100%-16px)] flex flex-row justify-start items-center mr-1 shrink-0">
<Link
className={classNames("w-8 h-8 flex justify-center items-center overflow-clip shrink-0 rounded")}
to={`/shortcut/${shortcut.id}`}
viewTransition
>
<LinkFavicon url={shortcut.link} />
</Link>
<div className="ml-3 w-[calc(100%-24px)] flex flex-col justify-start items-start">
<div className="w-full flex flex-row justify-start items-center leading-tight">
<a
className={classNames(
"max-w-[calc(100%-36px)] flex flex-row justify-start items-center mr-1 hover:opacity-80 hover:underline transition-all",
)}
target="_blank"
href={shortcutLink}
>
<div className="truncate">
<span className="text-foreground font-medium">{shortcut.title}</span>
{shortcut.title ? (
<span className="text-muted-foreground ml-1">({shortcut.name})</span>
) : (
<span className="truncate text-foreground font-medium">{shortcut.name}</span>
)}
</div>
<span className="hidden group-hover:block ml-1 shrink-0">
<Icon.ExternalLink className="w-4 h-auto text-muted-foreground" />
</span>
</a>
<Tooltip>
<TooltipTrigger asChild>
<button
className="hidden group-hover:block text-muted-foreground hover:text-foreground transition-colors"
onClick={() => handleCopyButtonClick()}
>
<Icon.Clipboard className="w-4 h-auto" />
</button>
</TooltipTrigger>
<TooltipContent>
<p>Copy</p>
</TooltipContent>
</Tooltip>
</div>
<a
className="pr-4 leading-tight w-full text-sm truncate text-muted-foreground hover:underline transition-all"
href={shortcut.link}
target="_blank"
>
{shortcut.link}
</a>
</div>
</div>
<div className="h-full pt-2 flex flex-row justify-end items-start">
<ShortcutActionsDropdown shortcut={shortcut} />
</div>
</div>
<div className="mt-3 w-full flex flex-row justify-start items-start gap-2 truncate">
{shortcut.tags.map((tag) => {
return (
<Badge
key={tag}
variant="secondary"
className="max-w-[8rem] truncate cursor-pointer hover:bg-secondary/80 transition-colors"
onClick={() => viewStore.setFilter({ tag: tag })}
>
#{tag}
</Badge>
);
})}
{shortcut.tags.length === 0 && <span className="text-muted-foreground text-sm italic">No tags</span>}
</div>
<div className="w-full mt-3 flex gap-3 overflow-x-auto">
<Tooltip>
<TooltipTrigger asChild>
<Avatar className="h-6 w-6">
<AvatarFallback className="text-xs">{creator.nickname.substring(0, 2).toUpperCase()}</AvatarFallback>
</Avatar>
</TooltipTrigger>
<TooltipContent>
<p>{creator.nickname}</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<div
className="flex flex-row justify-start items-center gap-1 text-muted-foreground text-sm cursor-pointer hover:text-foreground transition-colors"
onClick={() => viewStore.setFilter({ visibility: shortcut.visibility })}
>
<VisibilityIcon className="w-4 h-auto" visibility={shortcut.visibility} />
{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.self`)}
</div>
</TooltipTrigger>
<TooltipContent>
<p>{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.description`)}</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
className={classNames("w-8 h-8 flex justify-center items-center overflow-clip shrink-0 rounded")}
to={`/shortcut/${shortcut.id}`}
className="flex flex-row justify-start items-center gap-1 text-muted-foreground text-sm hover:text-foreground transition-colors"
to={`/shortcut/${shortcut.id}#analytics`}
viewTransition
>
<LinkFavicon url={shortcut.link} />
<Icon.BarChart2 className="w-4 h-auto" />
{t("shortcut.visits", { count: shortcut.viewCount })}
</Link>
<div className="ml-3 w-[calc(100%-24px)] flex flex-col justify-start items-start">
<div className="w-full flex flex-row justify-start items-center leading-tight">
<a
className={classNames(
"max-w-[calc(100%-36px)] flex flex-row justify-start items-center mr-1 hover:opacity-80 hover:underline transition-all",
)}
target="_blank"
href={shortcutLink}
>
<div className="truncate">
<span className="text-foreground font-medium">{shortcut.title}</span>
{shortcut.title ? (
<span className="text-muted-foreground ml-1">({shortcut.name})</span>
) : (
<span className="truncate text-foreground font-medium">{shortcut.name}</span>
)}
</div>
<span className="hidden group-hover:block ml-1 shrink-0">
<Icon.ExternalLink className="w-4 h-auto text-muted-foreground" />
</span>
</a>
<Tooltip>
<TooltipTrigger asChild>
<button
className="hidden group-hover:block text-muted-foreground hover:text-foreground transition-colors"
onClick={() => handleCopyButtonClick()}
>
<Icon.Clipboard className="w-4 h-auto" />
</button>
</TooltipTrigger>
<TooltipContent>
<p>Copy</p>
</TooltipContent>
</Tooltip>
</div>
<a
className="pr-4 leading-tight w-full text-sm truncate text-muted-foreground hover:underline transition-all"
href={shortcut.link}
target="_blank"
>
{shortcut.link}
</a>
</div>
</div>
<div className="h-full pt-2 flex flex-row justify-end items-start">
<ShortcutActionsDropdown shortcut={shortcut} />
</div>
</div>
<div className="mt-3 w-full flex flex-row justify-start items-start gap-2 truncate">
{shortcut.tags.map((tag) => {
return (
<Badge
key={tag}
variant="secondary"
className="max-w-[8rem] truncate cursor-pointer hover:bg-secondary/80 transition-colors"
onClick={() => viewStore.setFilter({ tag: tag })}
>
#{tag}
</Badge>
);
})}
{shortcut.tags.length === 0 && <span className="text-muted-foreground text-sm italic">No tags</span>}
</div>
<div className="w-full mt-3 flex gap-3 overflow-x-auto">
<Tooltip>
<TooltipTrigger asChild>
<Avatar className="h-6 w-6">
<AvatarFallback className="text-xs">{creator.nickname.substring(0, 2).toUpperCase()}</AvatarFallback>
</Avatar>
</TooltipTrigger>
<TooltipContent>
<p>{creator.nickname}</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<div
className="flex flex-row justify-start items-center gap-1 text-muted-foreground text-sm cursor-pointer hover:text-foreground transition-colors"
onClick={() => viewStore.setFilter({ visibility: shortcut.visibility })}
>
<VisibilityIcon className="w-4 h-auto" visibility={shortcut.visibility} />
{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.self`)}
</div>
</TooltipTrigger>
<TooltipContent>
<p>{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.description`)}</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link
className="flex flex-row justify-start items-center gap-1 text-muted-foreground text-sm hover:text-foreground transition-colors"
to={`/shortcut/${shortcut.id}#analytics`}
viewTransition
>
<Icon.BarChart2 className="w-4 h-auto" />
{t("shortcut.visits", { count: shortcut.viewCount })}
</Link>
</TooltipTrigger>
<TooltipContent>
<p>View count</p>
</TooltipContent>
</Tooltip>
</div>
</AnimatedCard>
</TooltipProvider>
</TooltipTrigger>
<TooltipContent>
<p>View count</p>
</TooltipContent>
</Tooltip>
</div>
</Card>
);
};
@@ -1,22 +0,0 @@
import { motion } from "framer-motion";
import { forwardRef } from "react";
import { Card, CardProps } from "@/components/ui/card";
const AnimatedCard = forwardRef<HTMLDivElement, CardProps>(({ className, children, ...props }, ref) => {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2, ease: "easeOut" }}
whileHover={{ y: -2 }}
>
<Card ref={ref} className={className} {...props}>
{children}
</Card>
</motion.div>
);
});
AnimatedCard.displayName = "AnimatedCard";
export { AnimatedCard };
+1 -1
View File
@@ -6,7 +6,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"flex h-9 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className,
)}
ref={ref}
+2 -2
View File
@@ -49,10 +49,10 @@ const ShortcutDashboard: React.FC = () => {
<>
<div className="mx-auto max-w-8xl w-full px-4 sm:px-6 md:px-12 pt-4 pb-6 flex flex-col justify-start items-start">
<ShortcutsNavigator />
<div className="w-full flex flex-row justify-between items-center mb-4">
<div className="w-full flex flex-row justify-between items-center mb-4 gap-2">
<div className="flex flex-row justify-start items-center">
<Input
className="w-32 mr-2"
className="w-32"
type="text"
placeholder={t("common.search")}
value={filter.search}
+50 -60
View File
@@ -12,7 +12,7 @@ import Icon from "@/components/Icon";
import LinkFavicon from "@/components/LinkFavicon";
import VisibilityIcon from "@/components/VisibilityIcon";
import Dropdown from "@/components/common/Dropdown";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { absolutifyLink } from "@/helpers/utils";
import useLoading from "@/hooks/useLoading";
import useNavigateTo from "@/hooks/useNavigateTo";
@@ -104,32 +104,28 @@ const ShortcutDetail = () => {
</span>
</a>
<div className="mt-2 w-full flex flex-row justify-normal items-center space-x-2">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
className="w-8 h-8 cursor-pointer border border-border rounded-full text-muted-foreground hover:bg-accent hover:shadow"
onClick={() => handleCopyButtonClick()}
>
<Icon.Clipboard className="w-4 h-auto mx-auto" />
</button>
</TooltipTrigger>
<TooltipContent>Copy</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
className="w-8 h-8 cursor-pointer border border-border rounded-full text-muted-foreground hover:bg-accent hover:shadow"
onClick={() => setShowQRCodeDialog(true)}
>
<Icon.QrCode className="w-4 h-auto mx-auto" />
</button>
</TooltipTrigger>
<TooltipContent>QR Code</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
className="w-8 h-8 cursor-pointer border border-border rounded-full text-muted-foreground hover:bg-accent hover:shadow"
onClick={() => handleCopyButtonClick()}
>
<Icon.Clipboard className="w-4 h-auto mx-auto" />
</button>
</TooltipTrigger>
<TooltipContent>Copy</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<button
className="w-8 h-8 cursor-pointer border border-border rounded-full text-muted-foreground hover:bg-accent hover:shadow"
onClick={() => setShowQRCodeDialog(true)}
>
<Icon.QrCode className="w-4 h-auto mx-auto" />
</button>
</TooltipTrigger>
<TooltipContent>QR Code</TooltipContent>
</Tooltip>
{havePermission && (
<Dropdown
className="w-8 h-8 flex justify-center items-center border border-border cursor-pointer rounded-full hover:bg-accent hover:shadow"
@@ -172,39 +168,33 @@ const ShortcutDetail = () => {
{shortcut.tags.length === 0 && <span className="text-muted-foreground text-sm leading-4 italic">No tags</span>}
</div>
<div className="w-full flex mt-4 gap-2">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="w-auto px-2 leading-6 flex flex-row justify-start items-center border border-border rounded-full text-muted-foreground text-sm">
<Icon.User className="w-4 h-auto mr-1" />
<span className="max-w-[4rem] sm:max-w-[6rem] truncate">{creator.nickname}</span>
</div>
</TooltipTrigger>
<TooltipContent>Creator</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="w-auto px-2 leading-6 flex flex-row justify-start items-center border border-border rounded-full text-muted-foreground text-sm">
<VisibilityIcon className="w-4 h-auto mr-1" visibility={shortcut.visibility} />
{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.self`)}
</div>
</TooltipTrigger>
<TooltipContent>{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.description`)}</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="w-auto px-2 leading-6 flex flex-row justify-start items-center border border-border rounded-full text-muted-foreground text-sm">
<Icon.BarChart2 className="w-4 h-auto mr-1" />
{shortcut.viewCount} visits
</div>
</TooltipTrigger>
<TooltipContent>View count</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="w-auto px-2 leading-6 flex flex-row justify-start items-center border border-border rounded-full text-muted-foreground text-sm">
<Icon.User className="w-4 h-auto mr-1" />
<span className="max-w-[4rem] sm:max-w-[6rem] truncate">{creator.nickname}</span>
</div>
</TooltipTrigger>
<TooltipContent>Creator</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<div className="w-auto px-2 leading-6 flex flex-row justify-start items-center border border-border rounded-full text-muted-foreground text-sm">
<VisibilityIcon className="w-4 h-auto mr-1" visibility={shortcut.visibility} />
{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.self`)}
</div>
</TooltipTrigger>
<TooltipContent>{t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.description`)}</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<div className="w-auto px-2 leading-6 flex flex-row justify-start items-center border border-border rounded-full text-muted-foreground text-sm">
<Icon.BarChart2 className="w-4 h-auto mr-1" />
{shortcut.viewCount} visits
</div>
</TooltipTrigger>
<TooltipContent>View count</TooltipContent>
</Tooltip>
</div>
<div className="w-full flex flex-col mt-8">
+2 -12
View File
@@ -1,10 +1,9 @@
import { useEffect } from "react";
import { Link } from "react-router-dom";
import { toast } from "sonner";
import Icon from "@/components/Icon";
import WorkspaceGeneralSettingSection from "@/components/setting/WorkspaceGeneralSettingSection";
import WorkspaceMembersSection from "@/components/setting/WorkspaceMembersSection";
import WorkspaceSecuritySection from "@/components/setting/WorkspaceSecuritySection";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { useUserStore, useWorkspaceStore } from "@/stores";
@@ -16,22 +15,13 @@ const WorkspaceSetting = () => {
const currentUser = useUserStore().getCurrentUser();
const isAdmin = currentUser.role === Role.ADMIN;
useEffect(() => {
if (!isAdmin) {
window.location.href = "/";
}
}, []);
if (!isAdmin) {
toast.error("Only workspace admins can access workspace settings.");
return null;
}
return (
<div className="mx-auto max-w-8xl w-full px-4 sm:px-6 md:px-12 py-6 flex flex-col justify-start items-start gap-y-12">
<Alert variant="default" className="border-primary bg-muted">
<Icon.Info className="h-4 w-4" />
<AlertDescription>You can see the settings items below because you are an Admin.</AlertDescription>
</Alert>
<div className="w-full flex flex-col">
<p className="text-2xl shrink-0 font-semibold text-foreground">Subscription</p>
<div className="mt-2">