feat(tender-details): improve scrolling behavior and header offset management
- Introduced a new constant for the tender section scroll offset to enhance scrolling behavior when navigating to sections. - Updated the scroll-to-section logic to account for the sticky header and added a small gap for better visual alignment. - Refactored the useTenderSectionScrollSpy hook to utilize the new scroll offset, ensuring consistent behavior across sections. - Improved code organization by separating scroll offset calculations into a dedicated function.
This commit is contained in:
@@ -13,7 +13,11 @@ export const TENDER_SECTION_IDS = {
|
|||||||
aiSummary: "tender-section-ai-summary",
|
aiSummary: "tender-section-ai-summary",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const HEADER_SCROLL_MARGIN = "scroll-mt-[5.5rem]";
|
/** Sticky app header clearance — keep in sync with `getTenderSectionScrollOffset`. */
|
||||||
|
export const TENDER_SECTION_SCROLL_OFFSET_PX = 96;
|
||||||
|
|
||||||
|
/** For hash / native scroll-into-view (matches offset above). */
|
||||||
|
export const HEADER_SCROLL_MARGIN = "scroll-mt-24";
|
||||||
|
|
||||||
/** Body area only: center content in the card; ShowcaseSection `title` is unchanged */
|
/** Body area only: center content in the card; ShowcaseSection `title` is unchanged */
|
||||||
export const SHOWCASE_CENTERED_BODY = {
|
export const SHOWCASE_CENTERED_BODY = {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { getTenderSectionScrollOffset } from "../scroll-to-section";
|
||||||
|
|
||||||
export function useTenderSectionScrollSpy(sectionIds: string[]) {
|
export function useTenderSectionScrollSpy(sectionIds: string[]) {
|
||||||
const [activeId, setActiveId] = useState(sectionIds[0] ?? "");
|
const [activeId, setActiveId] = useState(sectionIds[0] ?? "");
|
||||||
@@ -17,7 +18,7 @@ export function useTenderSectionScrollSpy(sectionIds: string[]) {
|
|||||||
if (sectionIds.length === 0) return;
|
if (sectionIds.length === 0) return;
|
||||||
|
|
||||||
const pickActive = () => {
|
const pickActive = () => {
|
||||||
const offset = 96;
|
const offset = getTenderSectionScrollOffset();
|
||||||
const scrollY = window.scrollY;
|
const scrollY = window.scrollY;
|
||||||
const viewportHeight = window.innerHeight;
|
const viewportHeight = window.innerHeight;
|
||||||
const documentHeight = Math.max(
|
const documentHeight = Math.max(
|
||||||
|
|||||||
@@ -1,11 +1,34 @@
|
|||||||
|
import { TENDER_SECTION_SCROLL_OFFSET_PX } from "./constants";
|
||||||
|
|
||||||
|
const SECTION_SCROLL_GAP_PX = 8;
|
||||||
|
|
||||||
|
/** Pixels to leave above the section top (sticky header + small gap). */
|
||||||
|
export function getTenderSectionScrollOffset(): number {
|
||||||
|
if (typeof document === "undefined") {
|
||||||
|
return TENDER_SECTION_SCROLL_OFFSET_PX + SECTION_SCROLL_GAP_PX;
|
||||||
|
}
|
||||||
|
const header = document.querySelector("header");
|
||||||
|
const headerHeight = header?.getBoundingClientRect().height ?? 0;
|
||||||
|
return (
|
||||||
|
Math.max(TENDER_SECTION_SCROLL_OFFSET_PX, headerHeight) +
|
||||||
|
SECTION_SCROLL_GAP_PX
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function scrollToTenderSection(id: string) {
|
export function scrollToTenderSection(id: string) {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
|
|
||||||
const reduceMotion =
|
const reduceMotion =
|
||||||
typeof window !== "undefined" &&
|
typeof window !== "undefined" &&
|
||||||
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||||
el.scrollIntoView({
|
|
||||||
|
const offset = getTenderSectionScrollOffset();
|
||||||
|
const top =
|
||||||
|
el.getBoundingClientRect().top + window.scrollY - offset;
|
||||||
|
|
||||||
|
window.scrollTo({
|
||||||
|
top: Math.max(0, top),
|
||||||
behavior: reduceMotion ? "auto" : "smooth",
|
behavior: reduceMotion ? "auto" : "smooth",
|
||||||
block: "start",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user