Quantcast
Channel: Active questions tagged header - Stack Overflow
Viewing all articles
Browse latest Browse all 647

Making a Collapsible Sticky Header — With React

$
0
0

I wish you a good day.

I want to adapt the header behavior in Medium.com, savee.it, Instagram tabbar to my own site.

Faraz Khan example on medium

I would like to ask for an example made with react native in the link. and below is sample code from Savee.it.

I want to do this with next js, can anyone explain how it works?

import type SVNav from '@apps/www/src/www/components/SVNav';import { getWindowScrollTop } from '@pkgs/shared-client/helpers/dom';import { unit } from '@pkgs/shared-client/styles/mixins';import clsx from 'clsx';import documentReady from 'document-ready';import prefixAll from 'inline-style-prefixer/static';import React, { forwardRef } from 'react';import setStyles from 'set-styles';import { twMerge } from 'tailwind-merge';const POSITIONS = {    TOP: 'top',    BOTTOM: 'bottom',} as const;const _DefaultTopSpacer = forwardRef<HTMLDivElement>((props, ref) => (<div ref={ref} className="h-0" {...props} />));const defaultProps: {    position: ValueOf<typeof POSITIONS>;    TopSpacerComponent: typeof _DefaultTopSpacer | typeof SVNav.TopSpacer;} = {    position: POSITIONS.TOP,    TopSpacerComponent: _DefaultTopSpacer,};type Props = Partial<typeof defaultProps> & {    className?: string;    render: (state: State) => JSX.Element;};type State = {    isHidden: boolean;    isAtTop: boolean;};class SVStickyBar extends React.Component<Props, State> {    static POSITIONS = POSITIONS;    static defaultProps = defaultProps;    elementRef = React.createRef<HTMLElement>();    spacerRef = React.createRef<HTMLDivElement>();    y = 0;    top = 0;    height = 0;    state: State = {        isHidden: false,        isAtTop: true,    };    componentDidMount() {        this.resize();        documentReady(this.resize);        window.addEventListener('resize', this.resize);        window.addEventListener('scroll', this.update);    }    componentWillUnmount() {        window.removeEventListener('resize', this.resize);        window.removeEventListener('scroll', this.update);    }    resize = () => {        const element = this.elementRef.current;        if (!element) {            setTimeout(this.resize, 100);            return;        }        const computedStyle = getComputedStyle(element);        this.height = parseInt(computedStyle.height) + parseInt(computedStyle.paddingBottom);        const spacer = this.spacerRef.current;        if (spacer) {            // spacer.style.height = unit(parseInt(computedStyle.height) + 16);            spacer.style.height = unit(parseInt(computedStyle.height));        }        this.update();    };    update = () => {        const { isHidden, isAtTop } = this.state;        const isBottom = this.props.position === POSITIONS.BOTTOM;        const scrollY = Math.max(0, getWindowScrollTop());        const min = Math.min(this.y, scrollY);        const t = Math.min(0, Math.max(this.height * -1, min - scrollY));        this.y = scrollY + t;        if (this.top != t) {            this.top = t;            if (this.elementRef.current) {                setStyles(                    this.elementRef.current,                    prefixAll({                        transform: `translateY(${t * (isBottom ? -1 : 1)}px)`,                    }),                );            }            if (t <= this.height * -1) {                if (!isHidden) {                    this.setState({                        isHidden: true,                    });                }            } else if (isHidden) {                this.setState({                    isHidden: false,                });            }        }        if (this.y <= 0 || scrollY <= this.height) {            if (!isAtTop) {                this.setState({                    isAtTop: true,                });            }        } else if (isAtTop) {            this.setState({                isAtTop: false,            });        }    };    render() {        const { render, className, TopSpacerComponent, position, ...otherProps } = this.props; // eslint-disable-line no-unused-vars        const { isHidden } = this.state;        return (<>                {position === POSITIONS.TOP && TopSpacerComponent && (<TopSpacerComponent ref={this.spacerRef} />                )}<div                    ref={this.elementRef as React.RefObject<HTMLDivElement>}                    className={twMerge(                        clsx('prevent-grid-select fixed top-0 left-0 right-0',                            isHidden && 'pointer-events-none',                            position === POSITIONS.TOP && 'z-index-nav',                            position === POSITIONS.BOTTOM && 'z-index-nav-small bottom-0 top-auto',                        ),                        className,                    )}                    {...otherProps}>                    {render(this.state)}</div></>        );    }}export default SVStickyBar;

I want to hide the title when going down and show that the given value decreases to zero when going up.

The code below is working smoothly now.

import { useEffect, useRef, useState } from "react";interface Props {  isHidden: boolean;  isAtTop: boolean;  align?: 'left' | 'center' | 'right';  className?: string;  children: React.ReactNode;}const positions = {  top: 'top',  bottom: 'bottom'}const defaultProps = {  position: positions.top}function Toolbar({ className, children }: Props) {  const elementRef = useRef<HTMLElement>(null);  const [say, setSay] = useState(0)  let y = 0;  let top = 0;  let height = 48;  let isHidden: false;  let isAtTop: true;  useEffect(() => {    const handleScroll = () => {      const isBottom = defaultProps.position === positions.bottom;      const scrollY = Math.max(0, window.scrollY);      const min = Math.min(y, scrollY);      const t = Math.min(0, Math.max(height * -1, min - scrollY));      y = scrollY + t;      if (top != t) {        top = t        if (elementRef.current) {          elementRef.current.style.transform = `translateY(${t * (isBottom ? -1 : 1)}px)`;        }        if (t <= height * -1) {          if (!isHidden) {            isHidden: true;          } else if (isHidden) {            isHidden: false;          }        }      }      if (y <= 0 || scrollY <= height) {        if (!isAtTop) {          isAtTop: true;        }      } else if (isAtTop) {        isAtTop: false;      }    };    // Scroll olaylarını dinleme    window.addEventListener('scroll', handleScroll);    // Temizlik fonksiyonu    return () => {      window.removeEventListener('scroll', handleScroll);    };  }, [elementRef]);  return (<header ref={elementRef}      id="__toolbar"      className={`        fixed        top-0        w-full        bg-white        dark:bg-black        z-50        ${className}      `}><div className="        w-full        mx-auto        px-4        flex        justify-stretch        h-12">        {children}</div></header>  )}

Viewing all articles
Browse latest Browse all 647

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>