feat(web): useMediaQuery hook + Base UI tooltip wrapper (#44)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type Side = NonNullable<TooltipPrimitive.Positioner.Props["side"]>;
|
||||
|
||||
function TooltipProvider({ ...props }: TooltipPrimitive.Provider.Props) {
|
||||
return <TooltipPrimitive.Provider {...props} />;
|
||||
}
|
||||
|
||||
function TooltipRoot({ ...props }: TooltipPrimitive.Root.Props) {
|
||||
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
|
||||
}
|
||||
|
||||
function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
|
||||
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
||||
}
|
||||
|
||||
function TooltipPopup({ className, ...props }: TooltipPrimitive.Popup.Props) {
|
||||
return (
|
||||
<TooltipPrimitive.Popup
|
||||
data-slot="tooltip-popup"
|
||||
className={cn(
|
||||
"rounded border bg-white px-2 py-1 text-sm shadow-md",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function TooltipPositioner({ className, ...props }: TooltipPrimitive.Positioner.Props) {
|
||||
return (
|
||||
<TooltipPrimitive.Positioner
|
||||
data-slot="tooltip-positioner"
|
||||
className={cn("z-50", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export type TooltipProps = {
|
||||
/** Text shown in the tooltip popup. */
|
||||
content: ReactNode;
|
||||
/** The element the tooltip is attached to. Rendered as the trigger. */
|
||||
children: ReactElement;
|
||||
/** Which side of the trigger to place the popup. Defaults to `"right"`. */
|
||||
side?: Side;
|
||||
/** Pixel gap between the trigger and the popup. */
|
||||
sideOffset?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Standalone tooltip: wraps its own `Provider`, so it can be dropped in
|
||||
* anywhere without an ancestor provider. `children` is delegated to the
|
||||
* Base UI trigger via `render`, so the underlying element keeps its own
|
||||
* tag/handlers (e.g. a `NavLink` for the collapsed sidebar).
|
||||
*/
|
||||
function Tooltip({ content, children, side = "right", sideOffset = 6 }: TooltipProps) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<TooltipRoot>
|
||||
<TooltipTrigger render={children} />
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPositioner side={side} sideOffset={sideOffset}>
|
||||
<TooltipPopup>{content}</TooltipPopup>
|
||||
</TooltipPositioner>
|
||||
</TooltipPrimitive.Portal>
|
||||
</TooltipRoot>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Tooltip,
|
||||
TooltipProvider,
|
||||
TooltipRoot,
|
||||
TooltipTrigger,
|
||||
TooltipPositioner,
|
||||
TooltipPopup,
|
||||
};
|
||||
Reference in New Issue
Block a user