fix(ecommerce): saca la barra flotante de "Añadir al carrito" en mobile
Pedido del usuario con capturas reales: la barra fija seguía superponiéndose sobre contenido de abajo al scrollear (ej. sobre la tarjeta de "También te puede interesar"), incluso después de varias vueltas ajustando cuándo debía aparecer/ocultarse. En vez de seguir parchando esa lógica, se saca la barra flotante por completo -- queda un solo botón "Añadir al carrito", visible en su lugar normal dentro de la página también en mobile (sin duplicar), igual que la mayoría de tiendas reales. Se borra components/product/mobile-add-to-cart.tsx (ya sin uso) y el id="product-buy-button"/id="recommendations-section" que solo existían para el IntersectionObserver de ese componente. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV
This commit is contained in:
1 parent
972b11772a
commit
5c79cc3345
3 files changed
+6
-105
No files matched your search
@@ -1,93 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { ShoppingBag } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { Product } from "@/lib/headless/types";
|
||||
import { useCartStore } from "@/store/cart-store";
|
||||
|
||||
const SCROLL_THRESHOLD_PX = 80;
|
||||
|
||||
// Antes esta barra quedaba fija desde el primer render en mobile,
|
||||
// tapando permanentemente parte de la imagen del producto solo para
|
||||
// duplicar un boton que ya está visible más abajo. Ahora combina tres
|
||||
// condiciones, igual que el "Buy Now" sticky de Amazon:
|
||||
// - scrolled: recien aparece despues de que el usuario empieza a
|
||||
// scrollear (nunca sobre la vista inicial de la imagen/hero).
|
||||
// - !buttonVisible: el boton real (#product-buy-button en
|
||||
// ProductPurchase, visible tambien en mobile) no esta en pantalla --
|
||||
// ya sea porque todavia no se llego a el, o porque se scrolleo mas
|
||||
// alla.
|
||||
// - !pastRecommendations: una vez que "También te puede gustar"
|
||||
// (#recommendations-section en app/product/[slug]/page.tsx) empieza a
|
||||
// entrar en pantalla, la barra se oculta y sigue oculta el resto del
|
||||
// scroll (footer incluido) -- pedido explicito del usuario. OJO:
|
||||
// condicionar esto a que el borde superior de la seccion cruce el
|
||||
// TOPE del viewport (top <= 0) rompe en paginas cortas -- si no hay
|
||||
// suficiente contenido/footer debajo, nunca se puede scrollear tan
|
||||
// lejos y el bug original (barra pegada hasta el fondo) segunia
|
||||
// reproduciendose. En cambio, se compara contra la ALTURA del
|
||||
// viewport (top < innerHeight): se oculta apenas la seccion empieza a
|
||||
// asomar desde abajo, se mantiene oculta al seguir scrolleando mas
|
||||
// alla (top se vuelve cada vez mas negativo, sigue siendo <
|
||||
// innerHeight), y solo reaparece si se vuelve a scrollear hacia
|
||||
// arriba lo suficiente como para que la seccion salga de pantalla per
|
||||
// completo otra vez.
|
||||
export function MobileAddToCart({ product }: { product: Product }) {
|
||||
const addItem = useCartStore((state) => state.addItem);
|
||||
const [buttonVisible, setButtonVisible] = useState(true);
|
||||
const [pastRecommendations, setPastRecommendations] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const outOfStock = product.variants[0]?.available === false;
|
||||
|
||||
useEffect(() => {
|
||||
const target = document.getElementById("product-buy-button");
|
||||
if (!target) return;
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (entry) setButtonVisible(entry.isIntersecting);
|
||||
}, { threshold: 0 });
|
||||
observer.observe(target);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const target = document.getElementById("recommendations-section");
|
||||
if (!target) return;
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (entry) setPastRecommendations(entry.boundingClientRect.top < window.innerHeight);
|
||||
}, { threshold: 0 });
|
||||
observer.observe(target);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > SCROLL_THRESHOLD_PX);
|
||||
onScroll();
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
const visible = scrolled && !buttonVisible && !pastRecommendations;
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-hidden={!visible}
|
||||
className={`fixed inset-x-0 bottom-0 z-40 border-t border-slate-200 bg-white/95 p-3 shadow-2xl backdrop-blur transition-transform duration-200 lg:hidden ${
|
||||
visible ? "translate-y-0" : "pointer-events-none translate-y-full"
|
||||
}`}
|
||||
>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
className="w-full"
|
||||
disabled={outOfStock}
|
||||
onClick={() => addItem(product, product.variants[0]?.id)}
|
||||
>
|
||||
<ShoppingBag />
|
||||
{outOfStock ? "Agotado" : "Añadir al carrito"}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -41,14 +41,11 @@ export function ProductPurchase({ product }: { product: Product }) {
|
||||
<strong className="text-3xl font-black">{formatMoney(product.price, product.currency)}</strong>
|
||||
{product.compareAtPrice && <del className="text-slate-400">{formatMoney(product.compareAtPrice, product.currency)}</del>}
|
||||
</div>
|
||||
{/* Visible tambien en mobile (antes quedaba oculto <lg porque
|
||||
MobileAddToCart quedaba fijo abajo desde el primer render,
|
||||
tapando parte de la imagen sin necesidad). id usado por
|
||||
MobileAddToCart para saber cuando este boton real sale del
|
||||
viewport y recien ahi mostrar su barra sticky -- mismo patron
|
||||
que el "Buy Now" sticky de Amazon en mobile. */}
|
||||
{/* Un solo botón, visible también en mobile -- sin barra flotante
|
||||
aparte (se probó, pero terminaba tapando contenido de abajo al
|
||||
scrollear, ej. "También te puede interesar"; el usuario pidió
|
||||
sacarla y dejar el botón normal como en la mayoría de tiendas). */}
|
||||
<Button
|
||||
id="product-buy-button"
|
||||
variant="primary"
|
||||
size="lg"
|
||||
className="w-full"
|
||||
|
||||
Reference in new issue
Block a user