Skip to content

Commit

Permalink
input field - responsiveness
Browse files Browse the repository at this point in the history
deploy tool - styling/tooltips

fee calculator - className fix/tooltips/responsive slider+toggle

deploy - tooltip timeout

mint - minor styling

transfer - minor styling/className update

stamping tool - responsiveness

tooltip fix

stamp tool - tooltip fix

stamp tool - tooltip styling

stamp tool - tooltip lock

stamp tool - image/uploadField responsiveness - CHECK CODE FUNCTIONALITY

stamp tool - consoleLog cleanup

image container styling

wallet receive modal - tooltip update

wallet send modal - input field responsiveness

wallet receive modal - responsiveness

transfer modal - squared input field

squared fix

receive - class update

tooltips update

receive - tooltip change
  • Loading branch information
babalicious-io authored and reinamora137 committed Dec 16, 2024
1 parent 5e763d9 commit 8285995
Show file tree
Hide file tree
Showing 11 changed files with 687 additions and 199 deletions.
118 changes: 83 additions & 35 deletions components/shared/fee/FeeCalculatorBase.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "preact/hooks";
import { useEffect, useRef, useState } from "preact/hooks";
import { useFeePolling } from "$client/hooks/useFeePolling.ts";
import { logger } from "$lib/utils/logger.ts";
import {
Expand Down Expand Up @@ -45,6 +45,9 @@ export function FeeCalculatorBase({
const { fees } = useFeePolling();
const [visible, setVisible] = useState(false);
const [coinType, setCoinType] = useState("BTC");
const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 });
const [isTooltipVisible, setIsTooltipVisible] = useState(false);
const tooltipTimeoutRef = useRef<number | null>(null);

const handleCoinToggle = () => {
logger.debug("stamps", {
Expand All @@ -57,6 +60,32 @@ export function FeeCalculatorBase({
setCoinType(coinType === "BTC" ? "USDT" : "BTC");
};

const handleMouseMove = (e: MouseEvent) => {
setTooltipPosition({
x: e.clientX,
y: e.clientY,
});
};

const handleMouseEnter = () => {
setIsTooltipVisible(true);

if (tooltipTimeoutRef.current) {
window.clearTimeout(tooltipTimeoutRef.current);
}

tooltipTimeoutRef.current = window.setTimeout(() => {
setIsTooltipVisible(false);
}, 1500);
};

const handleMouseLeave = () => {
if (tooltipTimeoutRef.current) {
window.clearTimeout(tooltipTimeoutRef.current);
}
setIsTooltipVisible(false);
};

// Fee selector component
const renderFeeSelector = () => (
<div className={`flex flex-col ${isModal ? "w-2/3" : "w-1/2"}`}>
Expand All @@ -70,7 +99,11 @@ export function FeeCalculatorBase({
<span className="font-medium">{fees.recommendedFee}</span> SAT/vB
</p>
)}
<div className="relative w-full">
<div
className="relative w-full group"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<input
type="range"
value={fee}
Expand All @@ -79,53 +112,62 @@ export function FeeCalculatorBase({
step="1"
onInput={(e) =>
handleChangeFee(parseInt((e.target as HTMLInputElement).value, 10))}
className="accent-stamp-purple-dark w-full h-[6px] rounded-lg appearance-none cursor-pointer bg-stamp-grey [&::-webkit-slider-thumb]:w-[22px] [&::-webkit-slider-thumb]:h-[22px] [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:bg-stamp-purple-dark [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:cursor-pointer [&::-moz-range-thumb]:w-[22px] [&::-moz-range-thumb]:h-[22px] [&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:bg-stamp-purple-dark [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:cursor-pointer"
onMouseMove={handleMouseMove}
className="accent-stamp-purple-dark w-full h-1 mobileLg:h-1.5 rounded-lg appearance-none cursor-pointer bg-stamp-grey [&::-webkit-slider-thumb]:w-[18px] [&::-webkit-slider-thumb]:h-[18px] [&::-webkit-slider-thumb]:mobileLg:w-[22px] [&::-webkit-slider-thumb]:mobileLg:h-[22px] [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:bg-stamp-purple-dark [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:cursor-pointer [&::-moz-range-thumb]:w-[18px] [&::-moz-range-thumb]:h-[18px] [&::-moz-range-thumb]:mobileLg:w-[22px] [&::-moz-range-thumb]:mobileLg:h-[22px] [&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:bg-stamp-purple-dark [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:cursor-pointer"
/>
<div
className={`${tooltipFloat} ${isTooltipVisible ? "block" : "hidden"}`}
style={{
left: `${tooltipPosition.x}px`,
top: `${tooltipPosition.y - 6}px`,
transform: "translate(-50%, -100%)",
}}
>
SELECT FEE
</div>
</div>
</div>
);

// Estimate details component
const renderDetails = () => {
const detailsTitleClassName = "text-stamp-grey-darker font-light";
const detailsTextClassName =
const detailsTitle = "text-stamp-grey-darker font-light";
const detailsText =
"text-xs mobileLg:text-sm font-medium text-stamp-grey-light";

return (
<div className={`${visible ? "visible" : "invisible"} gap-1 mt-1.5`}>
{/* File Type - Only show for stamp type */}
{type === "stamp" && fileType && (
<p className={detailsTextClassName}>
<span className={detailsTitleClassName}>FILE</span>{" "}
{fileType.toUpperCase()}
<p className={detailsText}>
<span className={detailsTitle}>FILE</span> {fileType.toUpperCase()}
</p>
)}

{/* Editions - Only show for stamp type */}
{type === "stamp" && issuance && (
<p className={detailsTextClassName}>
<span className={detailsTitleClassName}>EDITIONS</span> {issuance}
<p className={detailsText}>
<span className={detailsTitle}>EDITIONS</span> {issuance}
</p>
)}

{/* File Size */}
{fileSize && (
<p className={detailsTextClassName}>
<span className={detailsTitleClassName}>SIZE</span> {fileSize}{" "}
<p className={detailsText}>
<span className={detailsTitle}>SIZE</span> {fileSize}{" "}
<span className="font-light">BYTES</span>
</p>
)}

{/* Sats Per Byte */}
<p className={detailsTextClassName}>
<span className={detailsTitleClassName}>SATS PER BYTE</span> {fee}
<p className={detailsText}>
<span className={detailsTitle}>SATS PER BYTE</span> {fee}
</p>

{/* Miner Fee */}
{feeDetails?.minerFee && (
<p className={detailsTextClassName}>
<span className={detailsTitleClassName}>MINER FEE</span>{" "}
{coinType === "BTC"
<p className={detailsText}>
<span className={detailsTitle}>MINER FEE</span> {coinType === "BTC"
? formatSatoshisToBTC(feeDetails.minerFee, {
includeSymbol: false,
})
Expand All @@ -136,8 +178,8 @@ export function FeeCalculatorBase({

{/* Service Fee */}
{serviceFee && serviceFee > 0 && (
<p className={detailsTextClassName}>
<span className={detailsTitleClassName}>
<p className={detailsText}>
<span className={detailsTitle}>
{isModal ? "SERVICE FEE" : "MINTING FEE"}
</span>{" "}
{coinType === "BTC"
Expand All @@ -159,9 +201,8 @@ export function FeeCalculatorBase({

{/* Dust Value */}
{feeDetails?.dustValue && feeDetails.dustValue > 0 && (
<p className={detailsTextClassName}>
<span className={detailsTitleClassName}>DUST</span>{" "}
{coinType === "BTC"
<p className={detailsText}>
<span className={detailsTitle}>DUST</span> {coinType === "BTC"
? formatSatoshisToBTC(feeDetails.dustValue, {
includeSymbol: false,
})
Expand All @@ -172,9 +213,8 @@ export function FeeCalculatorBase({

{/* Total */}
{feeDetails?.totalValue && (
<p className={detailsTextClassName}>
<span className={detailsTitleClassName}>TOTAL</span>{" "}
{coinType === "BTC"
<p className={detailsText}>
<span className={detailsTitle}>TOTAL</span> {coinType === "BTC"
? formatSatoshisToBTC(feeDetails.totalValue, {
includeSymbol: false,
})
Expand All @@ -186,11 +226,21 @@ export function FeeCalculatorBase({
);
};

const buttonPurpleOutlineClassName =
const tooltipFloat =
"fixed text-stamp-grey-light text-[10px] mobileLg:text-xs mb-1 bg-black px-2 py-1 rounded-sm whitespace-nowrap pointer-events-none z-50";
const buttonPurpleOutline =
"inline-flex items-center justify-center border-2 border-stamp-purple rounded-md text-sm mobileLg:text-base font-extrabold text-stamp-purple tracking-[0.05em] h-[42px] mobileLg:h-[48px] px-4 mobileLg:px-5 hover:border-stamp-purple-highlight hover:text-stamp-purple-highlight transition-colors";
const buttonPurpleFlatClassName =
const buttonPurpleFlat =
"inline-flex items-center justify-center bg-stamp-purple border-2 border-stamp-purple rounded-md text-sm mobileLg:text-base font-extrabold text-black tracking-[0.05em] h-[42px] mobileLg:h-[48px] px-4 mobileLg:px-5 hover:border-stamp-purple-highlight hover:bg-stamp-purple-highlight transition-colors";

useEffect(() => {
return () => {
if (tooltipTimeoutRef.current) {
window.clearTimeout(tooltipTimeoutRef.current);
}
};
}, []);

return (
<div className={`text-[#999999] ${className}`}>
<div className="flex">
Expand All @@ -202,11 +252,11 @@ export function FeeCalculatorBase({
}`}
>
<button
className="w-12 h-6 rounded-full bg-stamp-grey flex items-center transition duration-300 focus:outline-none shadow"
className="min-w-[42px] h-[21px] mobileLg:min-w-12 mobileLg:h-6 rounded-full bg-stamp-grey flex items-center transition duration-300 focus:outline-none shadow"
onClick={handleCoinToggle}
>
<div
className={`w-6 h-6 relative rounded-full transition duration-500 transform bg-stamp-grey text-white ${
className={`w-[21px] h-[21px] mobileLg:w-6 mobileLg:h-6 relative rounded-full transition duration-500 transform flex justify-center items-center bg-stamp-grey ${
coinType === "BTC" ? "translate-x-full" : ""
}`}
>
Expand Down Expand Up @@ -318,7 +368,7 @@ export function FeeCalculatorBase({
<div className="flex justify-end gap-6">
{isModal && onCancel && (
<button
className={`${buttonPurpleOutlineClassName} ${
className={`${buttonPurpleOutline} ${
(disabled || isSubmitting || (!isModal && !tosAgreed))
? "opacity-50 cursor-not-allowed"
: ""
Expand All @@ -330,7 +380,7 @@ export function FeeCalculatorBase({
</button>
)}
<button
className={`${buttonPurpleFlatClassName} ${
className={`${buttonPurpleFlat} ${
(disabled || isSubmitting || (!isModal && !tosAgreed))
? "opacity-50 cursor-not-allowed"
: ""
Expand All @@ -350,8 +400,7 @@ export function FeeCalculatorBase({
const btcIcon = (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
className="w-[20px] h-[20px] mobileLg:w-6 mobileLg:h-6"
viewBox="0 0 24 24"
>
<path
Expand All @@ -364,8 +413,7 @@ const btcIcon = (
const usdIcon = (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
className="w-[20px] h-[20px] mobileLg:w-6 mobileLg:h-6"
style={{ padding: "1px" }}
viewBox="0 0 32 32"
>
Expand Down
68 changes: 62 additions & 6 deletions islands/Wallet/details/WalletReceiveModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "preact/hooks";
import { useEffect, useRef, useState } from "preact/hooks";
import QRCode from "qrcode";
import { ModalLayout } from "$components/shared/modal/ModalLayout.tsx";

Expand All @@ -7,9 +7,16 @@ interface Props {
address: string;
}

const tooltipIcon =
"absolute left-1/2 -translate-x-1/2 bg-[#000000BF] px-2 py-1 rounded-sm bottom-full text-[10px] mobileLg:text-xs text-stamp-grey-light whitespace-nowrap";

function WalletReceiveModal({ onClose, address }: Props) {
const [qrCodeDataUrl, setQrCodeDataUrl] = useState<string>("");
const [showCopied, setShowCopied] = useState(false);
const [isTooltipVisible, setIsTooltipVisible] = useState(false);
const [allowTooltip, setAllowTooltip] = useState(true);
const copyButtonRef = useRef<HTMLDivElement>(null);
const tooltipTimeoutRef = useRef<number | null>(null);

useEffect(() => {
const bitcoinUri = `bitcoin:${address}`;
Expand All @@ -25,18 +32,58 @@ function WalletReceiveModal({ onClose, address }: Props) {
.catch((err: Error) => console.error(err));
}, [address]);

const handleCopyMouseEnter = () => {
if (allowTooltip) {
const buttonRect = copyButtonRef.current?.getBoundingClientRect();
if (buttonRect) {
setIsTooltipVisible(true);
}

if (tooltipTimeoutRef.current) {
window.clearTimeout(tooltipTimeoutRef.current);
}

tooltipTimeoutRef.current = window.setTimeout(() => {
setIsTooltipVisible(false);
}, 1500);
}
};

const handleCopyMouseLeave = () => {
if (tooltipTimeoutRef.current) {
window.clearTimeout(tooltipTimeoutRef.current);
}
setIsTooltipVisible(false);
setShowCopied(false);
setAllowTooltip(true);
};

const handleCopy = async () => {
try {
await navigator.clipboard.writeText(address);
setShowCopied(true);
setTimeout(() => setShowCopied(false), 2000);
setIsTooltipVisible(false);
setAllowTooltip(false);

if (tooltipTimeoutRef.current) {
window.clearTimeout(tooltipTimeoutRef.current);
}

tooltipTimeoutRef.current = window.setTimeout(() => {
setShowCopied(false);
}, 1500);
} catch (err) {
console.error("Failed to copy:", err);
}
};

const tooltip =
"absolute left-1/2 -translate-x-1/2 bottom-full text-stamp-grey-light text-xs mb-1 hidden group-hover:block whitespace-nowrap";
useEffect(() => {
return () => {
if (tooltipTimeoutRef.current) {
window.clearTimeout(tooltipTimeoutRef.current);
}
};
}, []);

return (
<ModalLayout onClose={onClose} title="RECEIVE">
Expand All @@ -53,7 +100,12 @@ function WalletReceiveModal({ onClose, address }: Props) {
</p>
</div>
<div class="flex flex-col items-center pt-3 mobileLg:pt-6">
<div class="relative group">
<div
ref={copyButtonRef}
class="relative"
onMouseEnter={handleCopyMouseEnter}
onMouseLeave={handleCopyMouseLeave}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
Expand All @@ -66,7 +118,11 @@ function WalletReceiveModal({ onClose, address }: Props) {
>
<path d="M27 4H11C10.7348 4 10.4804 4.10536 10.2929 4.29289C10.1054 4.48043 10 4.73478 10 5V10H5C4.73478 10 4.48043 10.1054 4.29289 10.2929C4.10536 10.4804 4 10.7348 4 11V27C4 27.2652 4.10536 27.5196 4.29289 27.7071C4.48043 27.8946 4.73478 28 5 28H21C21.2652 28 21.5196 27.8946 21.7071 27.7071C21.8946 27.5196 22 27.2652 22 27V22H27C27.2652 22 27.5196 21.8946 27.7071 21.7071C27.8946 21.5196 28 21.2652 28 21V5C28 4.73478 27.8946 4.48043 27.7071 4.29289C27.5196 4.10536 27.2652 4 27 4ZM20 26H6V12H20V26ZM26 20H22V11C22 10.7348 21.8946 10.4804 21.7071 10.2929C21.5196 10.1054 21.2652 10 21 10H12V6H26V20Z" />
</svg>
<div class={tooltip}>
<div
class={`${tooltipIcon} ${
(isTooltipVisible || showCopied) ? "block" : "hidden"
}`}
>
{showCopied ? "COPIED" : "COPY"}
</div>
</div>
Expand Down
8 changes: 3 additions & 5 deletions islands/Wallet/details/WalletSendModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,8 @@ function WalletSendModal({ fee: initialFee, handleChangeFee, onClose }: Props) {
}
};

const inputField1colClassName =
"h-12 px-3 rounded-md bg-stamp-grey text-stamp-grey-darkest placeholder:text-stamp-grey-darkest placeholder:uppercase placeholder:font-light text-sm mobileLg:text-base font-medium w-full outline-none focus:bg-stamp-grey-light";
const inputField2colClassName =
"flex flex-col mobileMd:flex-row gap-3 mobileMd:gap-6 w-full";
const inputField =
"h-[42px] mobileLg:h-12 px-3 rounded-md bg-stamp-grey text-stamp-grey-darkest placeholder:text-stamp-grey-darkest placeholder:uppercase placeholder:font-light text-sm mobileLg:text-base font-medium w-full outline-none focus:bg-stamp-grey-light";

return (
<ModalLayout onClose={onClose} title="SEND">
Expand Down Expand Up @@ -219,7 +217,7 @@ function WalletSendModal({ fee: initialFee, handleChangeFee, onClose }: Props) {
recipientAddress: (e.target as HTMLInputElement).value,
})}
placeholder="Recipient address"
class={inputField1colClassName}
class={inputField}
/>
</div>

Expand Down
Loading

0 comments on commit 8285995

Please sign in to comment.