mirror of
https://github.com/jhbruhn/respira.git
synced 2026-01-27 10:23:41 +00:00
feature: Enhance PatternInfo with Tooltip and improve card layouts
Added shadcn Tooltip component for interactive pattern information. Wrapped all PatternInfo stat boxes and color swatches in tooltips with detailed metadata and explanations. Migrated PatternSummaryCard to use CardHeader/CardTitle/CardDescription for better semantic structure. Fixed Card component spacing issues across all cards. Changes: - Installed and added shadcn Tooltip component - Added tooltips to Size, Stitches, and Colors stat boxes with explanatory text - Wrapped color swatches in Tooltips with detailed thread information - Added Separator between pattern stats and colors sections - Migrated PatternSummaryCard to use CardHeader with semantic title/description - Fixed Card gap-0 on all cards (FileUpload, PatternSummaryCard, ConnectionPrompt) - Added explicit padding to PatternSummaryCard: CardHeader (p-4 pb-3) and CardContent (px-4 pt-0 pb-4) - Updated components.json to use src/ paths instead of @/ aliases to fix shadcn install location 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2544504933
commit
054524cb5e
8 changed files with 266 additions and 65 deletions
|
|
@ -11,11 +11,11 @@
|
||||||
"prefix": ""
|
"prefix": ""
|
||||||
},
|
},
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"components": "@/components",
|
"components": "src/components",
|
||||||
"utils": "@/lib/utils",
|
"utils": "src/lib/utils",
|
||||||
"ui": "@/components/ui",
|
"ui": "src/components/ui",
|
||||||
"lib": "@/lib",
|
"lib": "src/lib",
|
||||||
"hooks": "@/hooks"
|
"hooks": "src/hooks"
|
||||||
},
|
},
|
||||||
"iconLibrary": "lucide"
|
"iconLibrary": "lucide"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
76
package-lock.json
generated
76
package-lock.json
generated
|
|
@ -16,6 +16,7 @@
|
||||||
"@radix-ui/react-progress": "^1.1.8",
|
"@radix-ui/react-progress": "^1.1.8",
|
||||||
"@radix-ui/react-separator": "^1.1.8",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@types/web-bluetooth": "^0.0.21",
|
"@types/web-bluetooth": "^0.0.21",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
|
|
@ -4158,6 +4159,58 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-tooltip": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.3",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.1.11",
|
||||||
|
"@radix-ui/react-id": "1.1.1",
|
||||||
|
"@radix-ui/react-popper": "1.2.8",
|
||||||
|
"@radix-ui/react-portal": "1.1.9",
|
||||||
|
"@radix-ui/react-presence": "1.1.5",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-slot": "1.2.3",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||||
|
"@radix-ui/react-visually-hidden": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||||
|
|
@ -4279,6 +4332,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-visually-hidden": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.1.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/rect": {
|
"node_modules/@radix-ui/rect": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
"@radix-ui/react-progress": "^1.1.8",
|
"@radix-ui/react-progress": "^1.1.8",
|
||||||
"@radix-ui/react-separator": "^1.1.8",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@types/web-bluetooth": "^0.0.21",
|
"@types/web-bluetooth": "^0.0.21",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export function ConnectionPrompt() {
|
||||||
|
|
||||||
if (isBluetoothSupported()) {
|
if (isBluetoothSupported()) {
|
||||||
return (
|
return (
|
||||||
<Card className="p-0 border-l-4 border-gray-400 dark:border-gray-600">
|
<Card className="p-0 gap-0 border-l-4 border-gray-400 dark:border-gray-600">
|
||||||
<CardContent className="p-4 rounded-lg">
|
<CardContent className="p-4 rounded-lg">
|
||||||
<div className="flex items-start gap-3 mb-3">
|
<div className="flex items-start gap-3 mb-3">
|
||||||
<div className="w-6 h-6 text-gray-600 dark:text-gray-400 flex-shrink-0 mt-0.5">
|
<div className="w-6 h-6 text-gray-600 dark:text-gray-400 flex-shrink-0 mt-0.5">
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ export function FileUpload() {
|
||||||
: "text-gray-600 dark:text-gray-400";
|
: "text-gray-600 dark:text-gray-400";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={cn("p-0 border-l-4", borderColor)}>
|
<Card className={cn("p-0 gap-0 border-l-4", borderColor)}>
|
||||||
<CardContent className="p-4 rounded-lg">
|
<CardContent className="p-4 rounded-lg">
|
||||||
<div className="flex items-start gap-3 mb-3">
|
<div className="flex items-start gap-3 mb-3">
|
||||||
<DocumentTextIcon
|
<DocumentTextIcon
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
import type { PesPatternData } from "../formats/import/pesImporter";
|
import type { PesPatternData } from "../formats/import/pesImporter";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
|
||||||
interface PatternInfoProps {
|
interface PatternInfoProps {
|
||||||
pesData: PesPatternData;
|
pesData: PesPatternData;
|
||||||
|
|
@ -11,50 +18,87 @@ export function PatternInfo({
|
||||||
}: PatternInfoProps) {
|
}: PatternInfoProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-3 gap-2 text-xs mb-2">
|
<TooltipProvider>
|
||||||
<div className="bg-gray-200 dark:bg-gray-700/50 p-2 rounded">
|
<div className="grid grid-cols-3 gap-2 text-xs mb-2">
|
||||||
<span className="text-gray-600 dark:text-gray-400 block">Size</span>
|
<Tooltip>
|
||||||
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
<TooltipTrigger asChild>
|
||||||
{((pesData.bounds.maxX - pesData.bounds.minX) / 10).toFixed(1)} x{" "}
|
<div className="bg-gray-200 dark:bg-gray-700/50 p-2 rounded cursor-help">
|
||||||
{((pesData.bounds.maxY - pesData.bounds.minY) / 10).toFixed(1)} mm
|
<span className="text-gray-600 dark:text-gray-400 block">Size</span>
|
||||||
</span>
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
</div>
|
{((pesData.bounds.maxX - pesData.bounds.minX) / 10).toFixed(1)} x{" "}
|
||||||
<div className="bg-gray-200 dark:bg-gray-700/50 p-2 rounded">
|
{((pesData.bounds.maxY - pesData.bounds.minY) / 10).toFixed(1)} mm
|
||||||
<span className="text-gray-600 dark:text-gray-400 block">
|
|
||||||
Stitches
|
|
||||||
</span>
|
|
||||||
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
|
||||||
{pesData.penStitches?.stitches.length.toLocaleString() ||
|
|
||||||
pesData.stitchCount.toLocaleString()}
|
|
||||||
{pesData.penStitches &&
|
|
||||||
pesData.penStitches.stitches.length !== pesData.stitchCount && (
|
|
||||||
<span
|
|
||||||
className="text-gray-500 dark:text-gray-500 font-normal ml-1"
|
|
||||||
title="Input stitch count from PES file (lock stitches were added for machine compatibility)"
|
|
||||||
>
|
|
||||||
({pesData.stitchCount.toLocaleString()})
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
</div>
|
||||||
</span>
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p className="text-xs">Pattern dimensions (width × height)</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div className="bg-gray-200 dark:bg-gray-700/50 p-2 rounded cursor-help">
|
||||||
|
<span className="text-gray-600 dark:text-gray-400 block">
|
||||||
|
Stitches
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{pesData.penStitches?.stitches.length.toLocaleString() ||
|
||||||
|
pesData.stitchCount.toLocaleString()}
|
||||||
|
{pesData.penStitches &&
|
||||||
|
pesData.penStitches.stitches.length !== pesData.stitchCount && (
|
||||||
|
<span
|
||||||
|
className="text-gray-500 dark:text-gray-500 font-normal ml-1"
|
||||||
|
title="Input stitch count from PES file (lock stitches were added for machine compatibility)"
|
||||||
|
>
|
||||||
|
({pesData.stitchCount.toLocaleString()})
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<p className="text-xs">
|
||||||
|
{pesData.penStitches &&
|
||||||
|
pesData.penStitches.stitches.length !== pesData.stitchCount
|
||||||
|
? `Total stitches including lock stitches. Original file had ${pesData.stitchCount.toLocaleString()} stitches.`
|
||||||
|
: "Total number of stitches in the pattern"}
|
||||||
|
</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div className="bg-gray-200 dark:bg-gray-700/50 p-2 rounded cursor-help">
|
||||||
|
<span className="text-gray-600 dark:text-gray-400 block">
|
||||||
|
{showThreadBlocks ? "Colors / Blocks" : "Colors"}
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{showThreadBlocks
|
||||||
|
? `${pesData.uniqueColors.length} / ${pesData.threads.length}`
|
||||||
|
: pesData.uniqueColors.length}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p className="text-xs">
|
||||||
|
{showThreadBlocks
|
||||||
|
? `${pesData.uniqueColors.length} unique ${pesData.uniqueColors.length === 1 ? "color" : "colors"} across ${pesData.threads.length} thread ${pesData.threads.length === 1 ? "block" : "blocks"}`
|
||||||
|
: `${pesData.uniqueColors.length} unique ${pesData.uniqueColors.length === 1 ? "color" : "colors"} in the pattern`}
|
||||||
|
</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-200 dark:bg-gray-700/50 p-2 rounded">
|
</TooltipProvider>
|
||||||
<span className="text-gray-600 dark:text-gray-400 block">
|
|
||||||
{showThreadBlocks ? "Colors / Blocks" : "Colors"}
|
<Separator className="mb-3" />
|
||||||
</span>
|
|
||||||
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
|
||||||
{showThreadBlocks
|
|
||||||
? `${pesData.uniqueColors.length} / ${pesData.threads.length}`
|
|
||||||
: pesData.uniqueColors.length}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<span className="text-xs text-gray-600 dark:text-gray-400">
|
<span className="text-xs text-gray-600 dark:text-gray-400">
|
||||||
Colors:
|
Colors:
|
||||||
</span>
|
</span>
|
||||||
<div className="flex gap-1">
|
<TooltipProvider>
|
||||||
{pesData.uniqueColors.slice(0, 8).map((color, idx) => {
|
<div className="flex gap-1">
|
||||||
|
{pesData.uniqueColors.slice(0, 8).map((color, idx) => {
|
||||||
// Primary metadata: brand and catalog number
|
// Primary metadata: brand and catalog number
|
||||||
const primaryMetadata = [
|
const primaryMetadata = [
|
||||||
color.brand,
|
color.brand,
|
||||||
|
|
@ -85,20 +129,36 @@ export function PatternInfo({
|
||||||
: `Color ${idx + 1}: ${color.hex}\nUsed in thread blocks: ${threadNumbers}`;
|
: `Color ${idx + 1}: ${color.hex}\nUsed in thread blocks: ${threadNumbers}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Tooltip key={idx}>
|
||||||
key={idx}
|
<TooltipTrigger asChild>
|
||||||
className="w-3 h-3 rounded-full border border-gray-300 dark:border-gray-600"
|
<div
|
||||||
style={{ backgroundColor: color.hex }}
|
className="w-3 h-3 rounded-full border border-gray-300 dark:border-gray-600 cursor-help"
|
||||||
title={tooltipText}
|
style={{ backgroundColor: color.hex }}
|
||||||
/>
|
/>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-xs">
|
||||||
|
<p className="text-xs whitespace-pre-line">{tooltipText}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{pesData.uniqueColors.length > 8 && (
|
{pesData.uniqueColors.length > 8 && (
|
||||||
<div className="w-3 h-3 rounded-full bg-gray-300 dark:bg-gray-600 border border-gray-400 dark:border-gray-500 flex items-center justify-center text-xs font-bold text-gray-600 dark:text-gray-300 leading-none">
|
<Tooltip>
|
||||||
+{pesData.uniqueColors.length - 8}
|
<TooltipTrigger asChild>
|
||||||
</div>
|
<div className="w-3 h-3 rounded-full bg-gray-300 dark:bg-gray-600 border border-gray-400 dark:border-gray-500 flex items-center justify-center text-xs font-bold text-gray-600 dark:text-gray-300 leading-none cursor-help">
|
||||||
|
+{pesData.uniqueColors.length - 8}
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p className="text-xs">
|
||||||
|
{pesData.uniqueColors.length - 8} more{" "}
|
||||||
|
{pesData.uniqueColors.length - 8 === 1 ? "color" : "colors"}
|
||||||
|
</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,13 @@ import { canDeletePattern } from "../utils/machineStateHelpers";
|
||||||
import { PatternInfo } from "./PatternInfo";
|
import { PatternInfo } from "./PatternInfo";
|
||||||
import { DocumentTextIcon, TrashIcon } from "@heroicons/react/24/solid";
|
import { DocumentTextIcon, TrashIcon } from "@heroicons/react/24/solid";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
} from "@/components/ui/card";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
|
|
||||||
export function PatternSummaryCard() {
|
export function PatternSummaryCard() {
|
||||||
|
|
@ -30,23 +36,22 @@ export function PatternSummaryCard() {
|
||||||
|
|
||||||
const canDelete = canDeletePattern(machineStatus);
|
const canDelete = canDeletePattern(machineStatus);
|
||||||
return (
|
return (
|
||||||
<Card className="p-0 border-l-4 border-primary-600 dark:border-primary-500">
|
<Card className="p-0 gap-0 border-l-4 border-primary-600 dark:border-primary-500">
|
||||||
<CardContent className="p-4 rounded-lg">
|
<CardHeader className="p-4 pb-3">
|
||||||
<div className="flex items-start gap-3 mb-3">
|
<div className="flex items-start gap-3">
|
||||||
<DocumentTextIcon className="w-6 h-6 text-primary-600 dark:text-primary-400 flex-shrink-0 mt-0.5" />
|
<DocumentTextIcon className="w-6 h-6 text-primary-600 dark:text-primary-400 flex-shrink-0 mt-0.5" />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-1">
|
<CardTitle className="text-sm">Active Pattern</CardTitle>
|
||||||
Active Pattern
|
<CardDescription
|
||||||
</h3>
|
className="text-xs truncate"
|
||||||
<p
|
|
||||||
className="text-xs text-gray-600 dark:text-gray-400 truncate"
|
|
||||||
title={currentFileName}
|
title={currentFileName}
|
||||||
>
|
>
|
||||||
{currentFileName}
|
{currentFileName}
|
||||||
</p>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="px-4 pt-0 pb-4">
|
||||||
<PatternInfo pesData={pesData} />
|
<PatternInfo pesData={pesData} />
|
||||||
|
|
||||||
{canDelete && (
|
{canDelete && (
|
||||||
|
|
|
||||||
59
src/components/ui/tooltip.tsx
Normal file
59
src/components/ui/tooltip.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function TooltipProvider({
|
||||||
|
delayDuration = 0,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
||||||
|
return (
|
||||||
|
<TooltipPrimitive.Provider
|
||||||
|
data-slot="tooltip-provider"
|
||||||
|
delayDuration={delayDuration}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Tooltip({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<TooltipProvider>
|
||||||
|
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
||||||
|
</TooltipProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TooltipTrigger({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
||||||
|
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function TooltipContent({
|
||||||
|
className,
|
||||||
|
sideOffset = 0,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<TooltipPrimitive.Portal>
|
||||||
|
<TooltipPrimitive.Content
|
||||||
|
data-slot="tooltip-content"
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
||||||
|
</TooltipPrimitive.Content>
|
||||||
|
</TooltipPrimitive.Portal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||||
Loading…
Reference in a new issue