9 Commits

Author SHA1 Message Date
e0257ff6fa Update src/app/page.tsx 2026-03-25 23:46:42 +00:00
3149358d53 Switch to version 6: modified src/components/button/useButtonClick.ts 2026-03-25 23:44:09 +00:00
d7569122bb Switch to version 6: modified src/components/button/ButtonBounceEffect/ButtonBounceEffect.tsx 2026-03-25 23:44:08 +00:00
8b6b156d74 Switch to version 6: modified src/app/page.tsx 2026-03-25 23:44:08 +00:00
3b9bdb232f Merge version_7 into main
Merge version_7 into main
2026-03-25 23:39:06 +00:00
6747ba4d83 Merge version_7 into main
Merge version_7 into main
2026-03-25 23:38:19 +00:00
861015f03f Merge version_7 into main
Merge version_7 into main
2026-03-25 23:37:29 +00:00
301c6d04bb Merge version_7 into main
Merge version_7 into main
2026-03-25 23:36:55 +00:00
b741446e29 Merge version_7 into main
Merge version_7 into main
2026-03-25 23:34:06 +00:00
3 changed files with 134 additions and 75 deletions

View File

@@ -1,6 +1,7 @@
"use client"; "use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider"; import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import ReactLenis from "lenis/react";
import ContactCTA from '@/components/sections/contact/ContactCTA'; import ContactCTA from '@/components/sections/contact/ContactCTA';
import FaqDouble from '@/components/sections/faq/FaqDouble'; import FaqDouble from '@/components/sections/faq/FaqDouble';
import FeatureCardTwentyThree from '@/components/sections/feature/FeatureCardTwentyThree'; import FeatureCardTwentyThree from '@/components/sections/feature/FeatureCardTwentyThree';
@@ -25,7 +26,7 @@ export default function LandingPage() {
secondaryButtonStyle="layered" secondaryButtonStyle="layered"
headingFontWeight="bold" headingFontWeight="bold"
> >
<ReactLenis root>
<div id="nav" data-section="nav"> <div id="nav" data-section="nav">
<NavbarLayoutFloatingInline <NavbarLayoutFloatingInline
navItems={[ navItems={[
@@ -55,7 +56,7 @@ export default function LandingPage() {
{ {
text: "Get My Free Roof Inspection", href: "#quote"}, text: "Get My Free Roof Inspection", href: "#quote"},
{ {
text: "See Why Dallas Trusts Us", href: "#reviews"}, text: "Call Now", href: "tel:+12145550199"},
]} ]}
layoutOrder="default" layoutOrder="default"
imageSrc="http://img.b2bpic.net/free-photo/chisinau-arena-sunset-moldova_1268-16015.jpg" imageSrc="http://img.b2bpic.net/free-photo/chisinau-arena-sunset-moldova_1268-16015.jpg"
@@ -172,7 +173,7 @@ export default function LandingPage() {
/> />
</div> </div>
<div id="contact-cta" data-section="contact-cta"> <div id="quote" data-section="quote">
<ContactCTA <ContactCTA
useInvertedBackground={false} useInvertedBackground={false}
background={{ background={{
@@ -230,7 +231,7 @@ export default function LandingPage() {
copyrightText="© 2024 | Results Roofing. All rights reserved." copyrightText="© 2024 | Results Roofing. All rights reserved."
/> />
</div> </div>
</ReactLenis>
</ThemeProvider> </ThemeProvider>
); );
} }

View File

@@ -1,57 +1,74 @@
import React, { forwardRef } from "react"; "use client";
import { cn } from "@/lib/utils";
import { useButtonClick } from "@/components/button/useButtonClick";
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { import { useRef, memo } from "react";
import { useCharAnimation } from "../useCharAnimation";
import { useButtonClick } from "../useButtonClick";
import { cls } from "@/lib/utils";
import "./BounceButton.css";
interface ButtonBounceEffectProps {
text: string; text: string;
onClick?: () => void;
href?: string; href?: string;
className?: string;
bgClassName?: string; bgClassName?: string;
textClassName?: string; textClassName?: string;
iconClassName?: string; disabled?: boolean;
newTab?: boolean; ariaLabel?: string;
type?: "button" | "submit" | "reset";
scrollToSection?: boolean;
} }
export const ButtonBounceEffect = forwardRef<HTMLButtonElement, ButtonProps>( const ButtonBounceEffect = ({
( text,
{ onClick,
text, href,
onClick, className = "",
href, bgClassName = "",
className, textClassName = "",
bgClassName, disabled = false,
textClassName, ariaLabel,
iconClassName, type = "button",
disabled = false, scrollToSection,
ariaLabel, }: ButtonBounceEffectProps) => {
type = "button", newTab, const buttonRef = useRef<HTMLButtonElement>(null);
...props const handleClick = useButtonClick(href, onClick, scrollToSection);
},
ref
) => {
const clickHandler = useButtonClick(href, onClick, newTab);
return ( useCharAnimation(buttonRef, text);
<button
className={cn( return (
"group relative flex h-12 w-full items-center justify-center rounded-lg bg-primary-cta p-3 text-sm font-medium text-primary-cta-foreground transition-all duration-300 ease-out active:scale-95", className <button
ref={buttonRef}
type={type}
onClick={handleClick}
data-href={href}
disabled={disabled}
aria-label={ariaLabel || text}
className={cls(
"bounce-button relative cursor-pointer flex items-center justify-center bg-transparent border-none leading-none no-underline h-9 px-6 min-w-0 w-fit max-w-full rounded-theme text-primary-cta-text",
"disabled:cursor-not-allowed disabled:opacity-50",
className
)}
>
<div
className={cls(
"bounce-button-bg absolute! inset-0 rounded-theme primary-button",
bgClassName
)}
></div>
<span
data-button-animate-chars=""
className={cls(
"bounce-button-text relative text-sm inline-block overflow-hidden truncate whitespace-nowrap",
textClassName
)} )}
onClick={clickHandler}
aria-label={ariaLabel}
disabled={disabled}
type={type}
ref={ref}
{...props}
> >
<span {text}
className={cn( </span>
"relative flex items-center gap-2 translate-y-0 group-hover:-translate-y-1 group-active:translate-y-0 transition-transform duration-300 ease-out", textClassName </button>
)} );
> };
{text}
</span>
</button>
);
}
);
ButtonBounceEffect.displayName = "ButtonBounceEffect"; ButtonBounceEffect.displayName = "ButtonBounceEffect";
export default memo(ButtonBounceEffect);

View File

@@ -1,33 +1,74 @@
"use client"; "use client";
import { useCallback } from "react"; import { useLenis } from "lenis/react";
// No import for 'lenis' or 'lenis/react' import { useRouter, usePathname } from "next/navigation";
import { useEffect } from "react";
/** export const useButtonClick = (
* A hook to provide a consistent click handler for buttons, especially for internal hash navigation. href?: string,
* It ensures smooth scrolling without relying on external libraries like Lenis or problematic querySelector patterns. onClick?: () => void,
*/ scrollToSection?: boolean
export function useButtonClick() { ) => {
const handleButtonClick = useCallback((event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>, href?: string) => { const lenis = useLenis();
if (href && href.startsWith("#")) { const router = useRouter();
const id = href.substring(1); // Remove the '#' const pathname = usePathname();
// Use getElementById directly to avoid querySelector issues with complex IDs (like '##quote')
const targetElement = document.getElementById(id);
if (targetElement) { const scrollToElement = (sectionId: string, delay: number = 100) => {
event.preventDefault(); // Prevent default browser jump setTimeout(() => {
targetElement.scrollIntoView({ behavior: "smooth" }); if (lenis) {
// Optionally update the URL hash without a full page reload or additional scroll lenis.scrollTo(`#${sectionId}`, { offset: 0 });
// history.pushState(null, '', href);
} else { } else {
console.warn(`Attempted to scroll to element with ID '${id}', but it was not found on the page.`); const element = document.getElementById(sectionId);
// If the element is not found, we let the default behavior happen (which might lead to a page refresh to the root) if (element) {
// or prevent it and do nothing. Given it's a "fix" for a bug, a silent failure with a console warning is safer than unexpected navigation. element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}
}, delay);
};
const handleClick = () => {
if (href) {
const isExternalLink = /^(https?:\/\/|www\.)/.test(href);
if (isExternalLink) {
window.open(
href.startsWith("www.") ? `https://${href}` : href,
"_blank",
"noopener,noreferrer"
);
} else if (href.startsWith("/")) {
const [path, hash] = href.split("#");
if (path !== pathname) {
router.push(path);
if (hash) {
setTimeout(() => {
window.location.hash = hash;
scrollToElement(hash, 100);
}, 100);
}
} else {
if (hash) {
window.location.hash = hash;
scrollToElement(hash, 50);
} else if (scrollToSection) {
const sectionId = path.replace(/^\//, "").replace(/\//g, "-");
scrollToElement(sectionId, 50);
}
}
} else {
scrollToElement(href, 50);
} }
} }
// For non-hash hrefs (external links, full paths), let the browser handle it onClick?.();
// unless there's a specific programmatic override needed (which isn't requested here). };
}, []);
return { handleButtonClick }; useEffect(() => {
} if (typeof window !== "undefined" && window.location.hash) {
const hash = window.location.hash.replace("#", "");
scrollToElement(hash, 300);
}
}, [pathname]);
return handleClick;
};