mirror of
https://github.com/moghtech/komodo.git
synced 2026-04-28 11:49:39 -05:00
use Tooltip component instead of HoverCard for mobile compatibility
This commit is contained in:
@@ -19,7 +19,6 @@ import { ResourceComponents } from "..";
|
||||
import { Types } from "komodo_client";
|
||||
import { DashboardPieChart } from "@pages/home/dashboard";
|
||||
import { ResourcePageHeader, StatusBadge } from "@components/util";
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@ui/hover-card";
|
||||
import { Card } from "@ui/card";
|
||||
import { Badge } from "@ui/badge";
|
||||
import { useToast } from "@ui/use-toast";
|
||||
@@ -27,6 +26,7 @@ import { Button } from "@ui/button";
|
||||
import { useBuilder } from "../builder";
|
||||
import { RenameResource } from "@components/config/util";
|
||||
import { GroupActions } from "@components/group-actions";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip";
|
||||
|
||||
export const useBuild = (id?: string) =>
|
||||
useRead("ListBuilds", {}, { refetchInterval: 10_000 }).data?.find(
|
||||
@@ -153,8 +153,8 @@ export const BuildComponents: RequiredResourceComponents = {
|
||||
const out_of_date =
|
||||
info.built_hash && info.built_hash !== info.latest_hash;
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card
|
||||
className={cn(
|
||||
"px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer",
|
||||
@@ -166,8 +166,8 @@ export const BuildComponents: RequiredResourceComponents = {
|
||||
{info.built_hash || info.latest_hash}
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="grid gap-2">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
@@ -196,8 +196,8 @@ export const BuildComponents: RequiredResourceComponents = {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
Refresh: ({ id }) => {
|
||||
|
||||
@@ -29,8 +29,8 @@ import {
|
||||
StatusBadge,
|
||||
} from "@components/util";
|
||||
import { RenameResource } from "@components/config/util";
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@ui/hover-card";
|
||||
import { GroupActions } from "@components/group-actions";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip";
|
||||
|
||||
// const configOrLog = atomWithStorage("config-or-log-v1", "Config");
|
||||
|
||||
@@ -319,8 +319,8 @@ export const UpdateAvailable = ({
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className={cn(
|
||||
"px-2 py-1 border rounded-md border-blue-400 hover:border-blue-500 opacity-50 hover:opacity-70 transition-colors cursor-pointer flex items-center gap-2",
|
||||
@@ -334,10 +334,10 @@ export const UpdateAvailable = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start" className="w-fit text-sm">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="w-fit text-sm">
|
||||
There is a newer image available
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,7 +48,6 @@ import {
|
||||
} from "@ui/command";
|
||||
import { Switch } from "@ui/switch";
|
||||
import { DataTable } from "@ui/data-table";
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@ui/hover-card";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -61,6 +60,7 @@ import { filterBySplit } from "@lib/utils";
|
||||
import { useToast } from "@ui/use-toast";
|
||||
import { fmt_upper_camelcase } from "@lib/formatting";
|
||||
import { TextUpdateMenuMonaco } from "@components/util";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip";
|
||||
|
||||
export const ProcedureConfig = ({ id }: { id: string }) => {
|
||||
const procedure = useRead("GetProcedure", { procedure: id }).data;
|
||||
@@ -109,19 +109,19 @@ const ProcedureConfigInner = ({
|
||||
<Settings className="w-4 h-4" />
|
||||
<h2 className="text-xl">Config</h2>
|
||||
</div>
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline">
|
||||
<Info className="w-4 h-4" />
|
||||
</Button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div>
|
||||
The executions in a stage are all run in parallel. The stages
|
||||
themselves are run sequentially.
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
}
|
||||
disabled={disabled}
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
stroke_color_class_by_intention,
|
||||
} from "@lib/color";
|
||||
import { cn } from "@lib/utils";
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@ui/hover-card";
|
||||
import { useServer } from "../server";
|
||||
import { Types } from "komodo_client";
|
||||
import { DashboardPieChart } from "@pages/home/dashboard";
|
||||
@@ -22,6 +21,7 @@ import { Button } from "@ui/button";
|
||||
import { useBuilder } from "../builder";
|
||||
import { RenameResource } from "@components/config/util";
|
||||
import { GroupActions } from "@components/group-actions";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip";
|
||||
|
||||
export const useRepo = (id?: string) =>
|
||||
useRead("ListRepos", {}, { refetchInterval: 10_000 }).data?.find(
|
||||
@@ -97,21 +97,21 @@ export const RepoComponents: RequiredResourceComponents = {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card className="px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer">
|
||||
<div className="text-muted-foreground text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
||||
cloned: {info.cloned_hash}
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="grid">
|
||||
<div className="text-muted-foreground">commit message:</div>
|
||||
{info.cloned_message}
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
Built: ({ id }) => {
|
||||
@@ -121,15 +121,15 @@ export const RepoComponents: RequiredResourceComponents = {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card className="px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer">
|
||||
<div className="text-muted-foreground text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
||||
built: {info.built_hash}
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="grid gap-2">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
@@ -139,8 +139,8 @@ export const RepoComponents: RequiredResourceComponents = {
|
||||
</Badge>
|
||||
{fullInfo?.built_message}
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
Latest: ({ id }) => {
|
||||
@@ -150,15 +150,15 @@ export const RepoComponents: RequiredResourceComponents = {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card className="px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer">
|
||||
<div className="text-muted-foreground text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
||||
latest: {info.latest_hash}
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="grid gap-2">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
@@ -168,8 +168,8 @@ export const RepoComponents: RequiredResourceComponents = {
|
||||
</Badge>
|
||||
{fullInfo?.latest_message}
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
Refresh: ({ id }) => {
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
stroke_color_class_by_intention,
|
||||
} from "@lib/color";
|
||||
import { cn, sync_no_changes } from "@lib/utils";
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@ui/hover-card";
|
||||
import { fmt_date } from "@lib/formatting";
|
||||
import { DashboardPieChart } from "@pages/home/dashboard";
|
||||
import { ResourcePageHeader, StatusBadge } from "@components/util";
|
||||
@@ -24,6 +23,7 @@ import { Badge } from "@ui/badge";
|
||||
import { RenameResource } from "@components/config/util";
|
||||
import { GroupActions } from "@components/group-actions";
|
||||
import { useAtom } from "jotai";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip";
|
||||
|
||||
export const useResourceSync = (id?: string) =>
|
||||
useRead("ListResourceSyncs", {}, { refetchInterval: 10_000 }).data?.find(
|
||||
@@ -184,8 +184,8 @@ export const ResourceSyncComponents: RequiredResourceComponents = {
|
||||
const out_of_date =
|
||||
info.last_sync_hash && info.last_sync_hash !== info.pending_hash;
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card
|
||||
className={cn(
|
||||
"px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer",
|
||||
@@ -197,8 +197,8 @@ export const ResourceSyncComponents: RequiredResourceComponents = {
|
||||
{info.last_sync_hash || info.pending_hash}
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="grid gap-2">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
@@ -227,8 +227,8 @@ export const ResourceSyncComponents: RequiredResourceComponents = {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
stroke_color_class_by_intention,
|
||||
} from "@lib/color";
|
||||
import { cn } from "@lib/utils";
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@ui/hover-card";
|
||||
import { useServer } from "../server";
|
||||
import { Types } from "komodo_client";
|
||||
import {
|
||||
@@ -41,6 +40,7 @@ import { StackConfig } from "./config";
|
||||
import { RenameResource } from "@components/config/util";
|
||||
import { GroupActions } from "@components/group-actions";
|
||||
import { StackLogs } from "./log";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "@ui/tooltip";
|
||||
|
||||
export const useStack = (id?: string) =>
|
||||
useRead("ListStacks", {}, { refetchInterval: 10_000 }).data?.find(
|
||||
@@ -210,22 +210,22 @@ export const StackComponents: RequiredResourceComponents = {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card className="px-3 py-2 bg-destructive/75 hover:bg-destructive transition-colors cursor-pointer">
|
||||
<div className="text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
||||
Config Missing
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="grid gap-2">
|
||||
No configuration provided for stack. Cannot get stack state.
|
||||
Either paste the compose file contents into the UI, or configure a
|
||||
git repo containing your files.
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
ProjectMissing: ({ id }) => {
|
||||
@@ -239,22 +239,22 @@ export const StackComponents: RequiredResourceComponents = {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card className="px-3 py-2 bg-destructive/75 hover:bg-destructive transition-colors cursor-pointer">
|
||||
<div className="text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
||||
Project Missing
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="grid gap-2">
|
||||
The compose project is not on the host. If the compose stack is
|
||||
running, the 'Project Name' needs to be set. This can be found
|
||||
with 'docker compose ls'.
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
RemoteErrors: ({ id }) => {
|
||||
@@ -264,21 +264,21 @@ export const StackComponents: RequiredResourceComponents = {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card className="px-3 py-2 bg-destructive/75 hover:bg-destructive transition-colors cursor-pointer">
|
||||
<div className="text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
||||
Remote Error
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div>
|
||||
There are errors reading the remote file contents. See{" "}
|
||||
<span className="font-bold">Info</span> tab for details.
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
UpdateAvailable: ({ id }) => <UpdateAvailable id={id} />,
|
||||
@@ -301,8 +301,8 @@ export const StackComponents: RequiredResourceComponents = {
|
||||
const out_of_date =
|
||||
info.deployed_hash && info.deployed_hash !== info.latest_hash;
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card
|
||||
className={cn(
|
||||
"px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer",
|
||||
@@ -314,8 +314,8 @@ export const StackComponents: RequiredResourceComponents = {
|
||||
{info.deployed_hash || info.latest_hash}
|
||||
</div>
|
||||
</Card>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<div className="grid gap-2">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
@@ -344,8 +344,8 @@ export const StackComponents: RequiredResourceComponents = {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
Refresh: ({ id }) => {
|
||||
@@ -475,8 +475,8 @@ export const UpdateAvailable = ({
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<HoverCard openDelay={200}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className={cn(
|
||||
"px-2 py-1 border rounded-md border-blue-400 hover:border-blue-500 opacity-50 hover:opacity-70 transition-colors cursor-pointer flex items-center gap-2",
|
||||
@@ -495,8 +495,8 @@ export const UpdateAvailable = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start" className="flex flex-col gap-2 w-fit">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="flex flex-col gap-2 w-fit">
|
||||
{info?.services
|
||||
.filter((service) => service.update_available)
|
||||
.map((s) => (
|
||||
@@ -506,7 +506,7 @@ export const UpdateAvailable = ({
|
||||
<div>{s.image}</div>
|
||||
</div>
|
||||
))}
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -32,11 +32,15 @@ export const Resources = () => {
|
||||
const [search, set] = useState("");
|
||||
const [filter_update_available, toggle_filter_update_available] =
|
||||
useFilterByUpdateAvailable();
|
||||
const resources = useRead(`List${type}s`, {
|
||||
query: {
|
||||
specific: { update_available: filter_update_available },
|
||||
},
|
||||
}).data;
|
||||
const query =
|
||||
type === "Stack" || type === "Deployment"
|
||||
? {
|
||||
query: {
|
||||
specific: { update_available: filter_update_available },
|
||||
},
|
||||
}
|
||||
: {};
|
||||
const resources = useRead(`List${type}s`, query).data;
|
||||
const filtered = useFilterResources(resources as any, search);
|
||||
|
||||
const Components = ResourceComponents[type];
|
||||
|
||||
166
frontend/src/ui/tooltip.tsx
Normal file
166
frontend/src/ui/tooltip.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
import * as React from "react";
|
||||
import {
|
||||
useFloating,
|
||||
autoUpdate,
|
||||
offset,
|
||||
flip,
|
||||
shift,
|
||||
useHover,
|
||||
useFocus,
|
||||
useDismiss,
|
||||
useRole,
|
||||
useInteractions,
|
||||
useMergeRefs,
|
||||
FloatingPortal,
|
||||
Placement,
|
||||
} from "@floating-ui/react";
|
||||
import { cn } from "@lib/utils";
|
||||
|
||||
interface TooltipOptions {
|
||||
initialOpen?: boolean;
|
||||
placement?: Placement;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export function useTooltip({
|
||||
initialOpen = false,
|
||||
placement = "bottom-start",
|
||||
open: controlledOpen,
|
||||
onOpenChange: setControlledOpen,
|
||||
}: TooltipOptions = {}) {
|
||||
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen);
|
||||
|
||||
const open = controlledOpen ?? uncontrolledOpen;
|
||||
const setOpen = setControlledOpen ?? setUncontrolledOpen;
|
||||
|
||||
const data = useFloating({
|
||||
placement,
|
||||
open,
|
||||
onOpenChange: setOpen,
|
||||
whileElementsMounted: autoUpdate,
|
||||
middleware: [
|
||||
offset(5),
|
||||
flip({
|
||||
crossAxis: placement.includes("-"),
|
||||
fallbackAxisSideDirection: "start",
|
||||
padding: 5,
|
||||
}),
|
||||
shift({ padding: 5 }),
|
||||
],
|
||||
});
|
||||
|
||||
const context = data.context;
|
||||
|
||||
const hover = useHover(context, {
|
||||
move: false,
|
||||
enabled: controlledOpen == null,
|
||||
});
|
||||
const focus = useFocus(context, {
|
||||
enabled: controlledOpen == null,
|
||||
});
|
||||
const dismiss = useDismiss(context);
|
||||
const role = useRole(context, { role: "tooltip" });
|
||||
|
||||
const interactions = useInteractions([hover, focus, dismiss, role]);
|
||||
|
||||
return React.useMemo(
|
||||
() => ({
|
||||
open,
|
||||
setOpen,
|
||||
...interactions,
|
||||
...data,
|
||||
}),
|
||||
[open, setOpen, interactions, data]
|
||||
);
|
||||
}
|
||||
|
||||
type ContextType = ReturnType<typeof useTooltip> | null;
|
||||
|
||||
const TooltipContext = React.createContext<ContextType>(null);
|
||||
|
||||
export const useTooltipContext = () => {
|
||||
const context = React.useContext(TooltipContext);
|
||||
|
||||
if (context == null) {
|
||||
throw new Error("Tooltip components must be wrapped in <Tooltip />");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export function Tooltip({
|
||||
children,
|
||||
...options
|
||||
}: { children: React.ReactNode } & TooltipOptions) {
|
||||
// This can accept any props as options, e.g. `placement`,
|
||||
// or other positioning options.
|
||||
const tooltip = useTooltip(options);
|
||||
return (
|
||||
<TooltipContext.Provider value={tooltip}>
|
||||
{children}
|
||||
</TooltipContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const TooltipTrigger = React.forwardRef<
|
||||
HTMLElement,
|
||||
React.HTMLProps<HTMLElement> & { asChild?: boolean }
|
||||
>(({ children, asChild = false, ...props }, propRef) => {
|
||||
const context = useTooltipContext();
|
||||
const childrenRef = (children as any).ref;
|
||||
const ref = useMergeRefs([context.refs.setReference, propRef, childrenRef]);
|
||||
|
||||
// `asChild` allows the user to pass any element as the anchor
|
||||
if (asChild && React.isValidElement(children)) {
|
||||
return React.cloneElement(
|
||||
children,
|
||||
context.getReferenceProps({
|
||||
ref,
|
||||
...props,
|
||||
...(children.props as any),
|
||||
"data-state": context.open ? "open" : "closed",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
// The user can style the trigger based on the state
|
||||
data-state={context.open ? "open" : "closed"}
|
||||
{...context.getReferenceProps(props)}
|
||||
type="button"
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
TooltipTrigger.displayName = "TooltipTrigger";
|
||||
|
||||
export const TooltipContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLProps<HTMLDivElement>
|
||||
>(({ className, ...props }, propRef) => {
|
||||
const context = useTooltipContext();
|
||||
const ref = useMergeRefs([context.refs.setFloating, propRef]);
|
||||
|
||||
if (!context.open) return null;
|
||||
|
||||
return (
|
||||
<FloatingPortal>
|
||||
<div
|
||||
ref={ref}
|
||||
style={{
|
||||
...context.floatingStyles,
|
||||
}}
|
||||
{...context.getFloatingProps(props)}
|
||||
className={cn(
|
||||
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
</FloatingPortal>
|
||||
);
|
||||
});
|
||||
TooltipContent.displayName = "TooltipContent";
|
||||
Reference in New Issue
Block a user