Skip to content

Commit

Permalink
add tooltips
Browse files Browse the repository at this point in the history
  • Loading branch information
ordinalspractice committed Jan 20, 2025
1 parent 4c0734c commit 89d6edd
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 57 deletions.
54 changes: 32 additions & 22 deletions www/src/components/ConnectWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SUPPORTED_WALLETS, WalletIcon } from "@omnisat/lasereyes";
import FormWrapper from "@/components/FormWrapper";
import { BaseButton } from "@/components/ui/base-button";
import { useWalletConnection } from "@/hooks/useWalletConnection";
import { TooltipWrapper } from "@/components/TooltipWrapper";

interface ConnectWalletFormProps {
onBack: () => void;
Expand All @@ -21,30 +22,39 @@ const ConnectWalletForm = ({ onBack }: ConnectWalletFormProps) => {
const isMissingWallet = !walletState.hasWallet[wallet.name];
return (
<div key={wallet.name} className="w-full">
{isMissingWallet ? (
<BaseButton
variant="icon"
asChild
className="bg-transparent/5 backdrop-blur-sm"
>
<a
href={wallet.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center"
<TooltipWrapper
showTooltip
tooltipLabel={
isMissingWallet
? `install ${wallet.name}`
: `connect ${wallet.name}`
}
>
{isMissingWallet ? (
<BaseButton
variant="icon"
asChild
className="bg-transparent/5 backdrop-blur-sm"
>
<a
href={wallet.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center"
>
<WalletIcon walletName={wallet.name} size={32} />
</a>
</BaseButton>
) : (
<BaseButton
variant="icon"
className="wallet-button-available"
onClick={() => walletActions.handleConnect(wallet.name)}
>
<WalletIcon walletName={wallet.name} size={32} />
</a>
</BaseButton>
) : (
<BaseButton
variant="icon"
className="wallet-button-available"
onClick={() => walletActions.handleConnect(wallet.name)}
>
<WalletIcon walletName={wallet.name} size={32} />
</BaseButton>
)}
</BaseButton>
)}
</TooltipWrapper>
</div>
);
})}
Expand Down
5 changes: 4 additions & 1 deletion www/src/components/SignMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const SignMessageForm = ({
e.preventDefault();
onSign();
}}
className="flex flex-col gap-6"
className="flex flex-col gap-[calc(var(--size)*0.06)]"
>
<BaseInput
type="text"
Expand All @@ -70,20 +70,23 @@ const SignMessageForm = ({
: walletState.address ?? ""
}
disabled
tooltipLabel="address"
/>
<BaseTextarea
placeholder="message"
value={signedData ? signedData.message : message}
onChange={(e) => !signedData && onMessageChange(e.target.value)}
required
disabled={signedData !== null}
tooltipLabel="message"
/>
{signedData ? (
<BaseInput
type="text"
placeholder="signature"
value={signedData.signature}
disabled
tooltipLabel="signature"
/>
) : (
<BaseButton variant="default" type="submit">
Expand Down
88 changes: 88 additions & 0 deletions www/src/components/TooltipWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useState } from "react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";

interface TooltipWrapperProps {
children: React.ReactNode;
value?: string | number | readonly string[];
showTooltip?: boolean;
tooltipLabel?: string;
clickToCopy?: boolean;
}

export const TooltipWrapper = ({
children,
value,
showTooltip = false,
tooltipLabel,
clickToCopy,
}: TooltipWrapperProps) => {
const [showCopied, setShowCopied] = useState(false);
const [isTooltipOpen, setIsTooltipOpen] = useState(false);

if (!showTooltip) {
return <>{children}</>;
}

const handleCopy = async () => {
if (!clickToCopy || !value) return;

try {
await navigator.clipboard.writeText(value.toString());
setShowCopied(true);
setIsTooltipOpen(true);

setTimeout(() => {
setShowCopied(false);
setIsTooltipOpen(false);
}, 2000);
} catch (error) {
console.error("Failed to copy:", error);
}
};

const getTooltipText = () => {
if (showCopied) return tooltipLabel ? `${tooltipLabel} copied!` : "copied!";
if (clickToCopy) {
return tooltipLabel ? `copy ${tooltipLabel}` : "click to copy";
}
return tooltipLabel;
};

return (
<TooltipProvider>
<Tooltip open={isTooltipOpen} onOpenChange={setIsTooltipOpen}>
<div className="relative w-full">
<TooltipTrigger
className="w-full"
onClick={(e) => {
e.preventDefault();
if (clickToCopy) handleCopy();
}}
>
<div
className={`tooltip-wrapper ${
clickToCopy ? "cursor-pointer" : ""
}`}
>
<div
className={`tooltip-wrapper-inner ${
clickToCopy ? "copy-enabled" : ""
}`}
>
{children}
</div>
</div>
</TooltipTrigger>
<TooltipContent sideOffset={5}>
<p className="text-xs">{getTooltipText()}</p>
</TooltipContent>
</div>
</Tooltip>
</TooltipProvider>
);
};
3 changes: 3 additions & 0 deletions www/src/components/VerifyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const VerifyForm = ({
data-default={formData.address}
required
disabled={verificationResult !== null}
tooltipLabel="address"
/>

<BaseInput
Expand All @@ -61,6 +62,7 @@ const VerifyForm = ({
data-default={formData.message}
required
disabled={verificationResult !== null}
tooltipLabel="message"
/>

<BaseInput
Expand All @@ -72,6 +74,7 @@ const VerifyForm = ({
data-default={formData.signature}
required
disabled={verificationResult !== null}
tooltipLabel="signature"
/>

{verificationResult === null ? (
Expand Down
44 changes: 32 additions & 12 deletions www/src/components/ui/base-input.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from "react";
import { cn } from "@/lib/utils";
import { Input } from "./input";
import { Input } from "@/components/ui/input";
import { TooltipWrapper } from "@/components/TooltipWrapper";

type BaseInputVariant = "default" | "verification";

Expand All @@ -9,6 +10,7 @@ interface BaseInputProps
className?: string;
variant?: BaseInputVariant;
verificationResult?: boolean;
tooltipLabel?: string;
}

const variantStyles = {
Expand All @@ -17,29 +19,47 @@ const variantStyles = {
};

const BaseInput = React.forwardRef<HTMLInputElement, BaseInputProps>(
({ className, variant = "default", verificationResult, ...props }, ref) => {
(
{
className,
variant = "default",
verificationResult,
tooltipLabel,
disabled,
...props
},
ref
) => {
const getVerificationStyles = () => {
if (
variant !== "verification" ||
typeof verificationResult === "undefined"
)
return "";

return verificationResult
? "verification-input-success"
: "verification-input-error";
};

return (
<Input
className={cn(
variantStyles[variant],
getVerificationStyles(),
className
)}
ref={ref}
{...props}
/>
<TooltipWrapper
value={props.value}
showTooltip={disabled}
tooltipLabel={tooltipLabel}
clickToCopy={disabled}
>
<Input
ref={ref}
className={cn(
variantStyles[variant],
getVerificationStyles(),
disabled && "pointer-events-none cursor-pointer",
className
)}
disabled={disabled}
{...props}
/>
</TooltipWrapper>
);
}
);
Expand Down
45 changes: 32 additions & 13 deletions www/src/components/ui/base-textarea.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
import * as React from "react";
import { cn } from "@/lib/utils";
import { Textarea } from "./textarea";
import { Textarea } from "@/components/ui/textarea";
import { TooltipWrapper } from "@/components/TooltipWrapper";

const BaseTextarea = React.forwardRef<
HTMLTextAreaElement,
React.ComponentPropsWithoutRef<typeof Textarea>
>(({ className, ...props }, ref) => {
return (
<Textarea
className={cn("common-component", "common-textarea", className)}
ref={ref}
{...props}
/>
);
});
interface BaseTextareaProps
extends React.ComponentPropsWithoutRef<typeof Textarea> {
tooltipLabel?: string;
}

const BaseTextarea = React.forwardRef<HTMLTextAreaElement, BaseTextareaProps>(
({ className, tooltipLabel, disabled, ...props }, ref) => {
return (
<TooltipWrapper
value={props.value}
showTooltip={disabled}
tooltipLabel={tooltipLabel}
clickToCopy={disabled}
>
<Textarea
className={cn(
"common-component",
"common-textarea",
disabled && "pointer-events-none cursor-pointer",
className
)}
ref={ref}
disabled={disabled}
{...props}
/>
</TooltipWrapper>
);
}
);

BaseTextarea.displayName = "BaseTextarea";

export { BaseTextarea };
export type { BaseTextareaProps };
19 changes: 10 additions & 9 deletions www/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,19 @@ body {
}
}

.tooltip-wrapper {
width: 100%;
}

.common-input:disabled,
.common-textarea:disabled {
background-color: hsl(var(--dark-1) / 0.1);
color: hsl(var(--primary));
border-color: hsl(var(--primary));
text-shadow: var(--glow-sm);
.tooltip-wrapper-inner {
width: 100%;
}

.common-input:disabled:hover,
.common-textarea:disabled:hover {
cursor: pointer;
.tooltip-wrapper-inner.copy-enabled > * {
pointer-events: none;
}

.tooltip-wrapper.cursor-pointer:hover .common-component {
box-shadow: var(--glow-lg);
}

Expand Down

0 comments on commit 89d6edd

Please sign in to comment.