I wish you a good day.
I want to adapt the header behavior in Medium.com, savee.it, Instagram tabbar to my own site.
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> )}