import { getClassNames } from '@websolutespa/bom-core'; import { useEventListener, useIsomorphicLayoutEffect, useScrollTo } from '@websolutespa/bom-mixer-hooks'; import React, { useState } from 'react'; import styled, { css } from 'styled-components'; import { Box } from '../box/box'; import { Button } from '../button/button'; import { Container } from '../container/container'; import { Flex } from '../flex/flex'; import { Nav } from '../nav/nav'; import { UIComponentProps } from '../types'; type ContainerProps = { fixed?: boolean; sticky?: boolean; scrolled?: boolean; }; export type PageNavContainerProps = UIComponentProps; const PageNavContainer = styled.div` `; const PageNavInner = styled.div` display: flex; align-items: center; min-height: 80px; position: fixed; top: 0; width: 100%; z-index: 1100; background: var(--color-neutral-900); color: var(--color-neutral-100); transition: ease-in-out 350ms; transition-property: background-color, transform; ${props => css` background: ${props.scrolled ? 'var(--color-neutral-900)' : 'var(--color-neutral-100)'}; color: ${props.scrolled ? 'var(--color-neutral-100)' : 'var(--color-neutral-900)'}; border-bottom: 1px solid ${props.scrolled ? 'var(--color-neutral-900)' : 'var(--color-neutral-300)'}; transform: translateY(${props.scrolled ? 0 : '-100%'}); `} `; type PageNavItem = { href: string, title: string }; type Props = { items: PageNavItem[]; // !!! todo fixed / sticky boolean }; export type PageNavProps = UIComponentProps; export const PageNav: React.FC = (props: PageNavProps) => { const innerRef = React.createRef(); const [id, setId] = useState(null); const [scrolled, setScrolled] = useState(false); const innerProps: PageNavContainerProps = { ...props, scrolled }; const scrollTo = useScrollTo(); const onScroll = () => { if (innerRef.current) { const scrollTop = window.scrollY; const ids = props.items.map(x => x.href); const anchorTargets: HTMLElement[] = ids.map(id => document.querySelector(id)).filter(x => x) as HTMLElement[]; const activeTarget = anchorTargets.reduce((p: HTMLElement | null, c: HTMLElement, i: number) => { // const previousTop = p ? (p.offsetTop - window.scrollY) : Number.NEGATIVE_INFINITY; const top = c.offsetTop - window.scrollY; // if (top < window.innerHeight && previousTop < 0) { if (top < window.innerHeight / 2) { return c; } else { return p; } }, null); // console.log('activeTarget', activeTarget?.id); setId(activeTarget?.id || null); const top = innerRef.current.offsetTop - scrollTop; const lastTarget = anchorTargets.length ? anchorTargets[anchorTargets.length - 1] : null; const lastTargetBottom = lastTarget ? (lastTarget.offsetTop + lastTarget.offsetHeight - window.scrollY) : Number.NEGATIVE_INFINITY; const isScrolled = top < 0 && lastTargetBottom > 0; setScrolled(isScrolled); } }; useEventListener('scroll', onScroll); // Set size at the first client-side load useIsomorphicLayoutEffect(() => { onScroll(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const classNames = getClassNames('page-nav'); return ( <> {props.items && ( {props.items.map((item, i) => ( ))} )} {props.children} ); };