-
Pool: {market.poolAmount.toLocaleString()} USDC
-
Ends in {market.endsIn}
+
+
+ Pool: {market.poolAmount.toLocaleString()} USDC
+
+
+ Ends in {market.endsIn}
+
);
-}
\ No newline at end of file
+}
diff --git a/app/components/Tooltip.tsx b/app/components/Tooltip.tsx
new file mode 100644
index 00000000..2247aeba
--- /dev/null
+++ b/app/components/Tooltip.tsx
@@ -0,0 +1,168 @@
+"use client";
+
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+
+export interface TooltipProps
+ extends Omit
, "content"> {
+ /** Content announced and displayed inside the tooltip bubble. */
+ content: React.ReactNode;
+ /** Focusable or descriptive trigger content. */
+ children: React.ReactNode;
+ /** Delay before showing on hover, in milliseconds. */
+ hoverDelay?: number;
+ /** Delay before showing on touch or pen long-press, in milliseconds. */
+ longPressDelay?: number;
+ /** Optional id for the tooltip bubble. */
+ tooltipId?: string;
+ /** Extra classes for the tooltip bubble. */
+ tooltipClassName?: string;
+}
+
+export function Tooltip({
+ content,
+ children,
+ hoverDelay = 300,
+ longPressDelay = 600,
+ tooltipId,
+ tooltipClassName,
+ className,
+ tabIndex,
+ onBlur,
+ onFocus,
+ onKeyDown,
+ onMouseEnter,
+ onMouseLeave,
+ onPointerCancel,
+ onPointerDown,
+ onPointerLeave,
+ onPointerUp,
+ onTouchCancel,
+ onTouchEnd,
+ onTouchStart,
+ "aria-describedby": ariaDescribedBy,
+ ...triggerProps
+}: TooltipProps) {
+ const reactId = React.useId();
+ const id = tooltipId ?? `tooltip-${reactId.replace(/:/g, "")}`;
+ const [open, setOpen] = React.useState(false);
+ const hoverTimer = React.useRef | null>(null);
+ const longPressTimer = React.useRef | null>(null);
+
+ const clearHoverTimer = React.useCallback(() => {
+ if (hoverTimer.current) {
+ clearTimeout(hoverTimer.current);
+ hoverTimer.current = null;
+ }
+ }, []);
+
+ const clearLongPressTimer = React.useCallback(() => {
+ if (longPressTimer.current) {
+ clearTimeout(longPressTimer.current);
+ longPressTimer.current = null;
+ }
+ }, []);
+
+ const close = React.useCallback(() => {
+ clearHoverTimer();
+ clearLongPressTimer();
+ setOpen(false);
+ }, [clearHoverTimer, clearLongPressTimer]);
+
+ React.useEffect(() => {
+ return () => {
+ clearHoverTimer();
+ clearLongPressTimer();
+ };
+ }, [clearHoverTimer, clearLongPressTimer]);
+
+ const describedBy = [ariaDescribedBy, open ? id : undefined]
+ .filter(Boolean)
+ .join(" ") || undefined;
+
+ return (
+ {
+ onBlur?.(event);
+ close();
+ }}
+ onFocus={(event) => {
+ onFocus?.(event);
+ setOpen(true);
+ }}
+ onKeyDown={(event) => {
+ onKeyDown?.(event);
+ if (event.key === "Escape") {
+ close();
+ }
+ }}
+ onMouseEnter={(event) => {
+ onMouseEnter?.(event);
+ clearHoverTimer();
+ hoverTimer.current = setTimeout(() => setOpen(true), hoverDelay);
+ }}
+ onMouseLeave={(event) => {
+ onMouseLeave?.(event);
+ close();
+ }}
+ onPointerCancel={(event) => {
+ onPointerCancel?.(event);
+ close();
+ }}
+ onPointerDown={(event) => {
+ onPointerDown?.(event);
+ if (event.pointerType === "mouse" || event.button !== 0) return;
+ clearLongPressTimer();
+ longPressTimer.current = setTimeout(() => setOpen(true), longPressDelay);
+ }}
+ onPointerLeave={(event) => {
+ onPointerLeave?.(event);
+ close();
+ }}
+ onPointerUp={(event) => {
+ onPointerUp?.(event);
+ clearLongPressTimer();
+ if (event.pointerType !== "mouse") {
+ setOpen(false);
+ }
+ }}
+ onTouchCancel={(event) => {
+ onTouchCancel?.(event);
+ close();
+ }}
+ onTouchEnd={(event) => {
+ onTouchEnd?.(event);
+ close();
+ }}
+ onTouchStart={(event) => {
+ onTouchStart?.(event);
+ clearLongPressTimer();
+ longPressTimer.current = setTimeout(() => setOpen(true), longPressDelay);
+ }}
+ >
+ {children}
+ {open && (
+
+ {content}
+
+ )}
+
+ );
+}