SPB Git forge

spb/immo-ka

Public

Immo-Ka — agrégateur des propriétés à vendre au Québec (73 connecteurs, ~40 000 annonces, React+FastAPI)

112commits 1branches 0releases
125.4 MBsize
maindefault branch
13 days agolast push
Python 47.5% HTML 27.9% TypeScript 15.5% CSS 7.2% JavaScript 2%
2.1 KB · 47 lines tsx
Raw Blame History
1// -----------------------------------------------------------------------------2// Immo-Ka — Agrégateur de propriétés à vendre (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// fiche/StickyListingCTA.tsx : barre CTA compacte du bas (mobile/tablette) —5//   n'apparaît qu'une fois la rangée d'actions du héro sortie de l'écran,6//   respecte la safe-area iOS, ne masque aucun contenu (padding de page).7// -----------------------------------------------------------------------------8import { useEffect, useState } from "react";9import { Listing, fmtPrice, sourceName } from "../api";10import { Ico } from "../components/Icons";11import { ComparaisonPrix, estLocation } from "./synthese";12import { NBSP } from "./ui";1314/** true dès que l'élément suivi est passé au-dessus du viewport (défilement). */15export function usePastElement(watch: React.RefObject<HTMLElement>): boolean {16  const [past, setPast] = useState(false);17  useEffect(() => {18    const check = () => {19      const el = watch.current;20      if (!el) return;21      setPast(el.getBoundingClientRect().bottom < 0);22    };23    check();24    window.addEventListener("scroll", check, { passive: true });25    window.addEventListener("resize", check);26    return () => { window.removeEventListener("scroll", check); window.removeEventListener("resize", check); };27  }, [watch]);28  return past;29}3031export default function StickyListingCTA({ l, cmp, show }: {32  l: Listing; cmp: ComparaisonPrix | null; show: boolean;33}) {34  return (35    <div className={`ik-cta-bar ${show ? "show" : ""}`} aria-hidden={!show}>36      <div className="ik-cta-txt">37        <div className="ik-cta-price">{fmtPrice(l.price, l.price_label)}{estLocation(l) && l.price != null && <small>{NBSP}/ mois</small>}</div>38        {cmp && <div className={`ik-cta-sub ${cmp.tone === "good" ? "good" : ""}`}>{cmp.court}</div>}39      </div>40      <a className="ik-btn ik-btn-primary" href={l.url} target="_blank" rel="noopener noreferrer" tabIndex={show ? 0 : -1}>41        Voir l'annonce <Ico name="external" size={15} />42        <span className="visually-hidden"> chez {sourceName(l.source)}</span>43      </a>44    </div>45  );46}47