UNPKG

denwa-web-shared

Version:

A shared library for Next.js App Router projects containing reusable components, hooks, schemas, and utilities.

202 lines (201 loc) 5.35 kB
import { useEffect, useRef, useState } from "react"; import { useIntersection, useMedia, useWindowScroll } from "react-use"; import { useInView } from "motion/react"; import { clsx } from "clsx"; import { twMerge } from "tailwind-merge"; import { jsx, jsxs } from "react/jsx-runtime"; //#region src/client/hooks/use-disable-scroll.ts var useDisableScroll = (isDisableScroll) => { useEffect(() => { if (!isDisableScroll) return; const scrollPosition = window.scrollY; return () => { window.scrollTo(0, scrollPosition); }; }, []); }; //#endregion //#region src/client/hooks/use-is-client.ts var useIsClient = () => { const [isClient, setIsClient] = useState(false); useEffect(() => { setIsClient(true); }, []); return isClient; }; //#endregion //#region src/server/constants/index.ts var THEME = { VIEW_PORT: { EXTRA_SMALL: 280, SMALL: 320, SMALL_MOBILE: 380, MOBILE: 450, MEDIUM: 500, EXTRA_MEDIUM: 600, TABLET: 768, LAPTOP: 1024, LAPTOP_BIG: 1200, BIG: 1440, VERY_BIG: 1920 }, OFFSET: { 1: 8, 2: 16, 3: 24, 4: 32, 5: 40, 6: 48, 7: 56, 8: 64 } }; //#endregion //#region src/client/hooks/use-view-port.ts var useLaptopBigViewPort = () => { return { isLaptopBigMinWidth: useMedia(`(min-width: ${THEME.VIEW_PORT.LAPTOP_BIG}px)`), isLaptopBigMaxWidth: useMedia(`(max-width: ${THEME.VIEW_PORT.LAPTOP_BIG}px)`) }; }; var useLaptopViewPort = () => { return { isLaptopMinWidth: useMedia(`(min-width: ${THEME.VIEW_PORT.LAPTOP}px)`), isLaptopMaxWidth: useMedia(`(max-width: ${THEME.VIEW_PORT.LAPTOP}px)`) }; }; var useTabletViewPort = () => { return { isTabletMinWidth: useMedia(`(min-width: ${THEME.VIEW_PORT.TABLET}px)`), isTabletMaxWidth: useMedia(`(max-width: ${THEME.VIEW_PORT.TABLET}px)`) }; }; var useExtraMediumViewPort = () => { return { isExtraMediumMinWidth: useMedia(`(min-width: ${THEME.VIEW_PORT.EXTRA_MEDIUM}px)`), isExtraMediumMaxWidth: useMedia(`(max-width: ${THEME.VIEW_PORT.EXTRA_MEDIUM}px)`) }; }; var useMobileViewPort = () => { return { isMobileMinWidth: useMedia(`(min-width: ${THEME.VIEW_PORT.MOBILE}px)`), isMobileMaxWidth: useMedia(`(max-width: ${THEME.VIEW_PORT.MOBILE}px)`) }; }; var useSmallViewPort = () => { return { isSmallMinWidth: useMedia(`(min-width: ${THEME.VIEW_PORT.SMALL}px)`), isSmallMaxWidth: useMedia(`(max-width: ${THEME.VIEW_PORT.SMALL}px)`) }; }; //#endregion //#region src/client/hooks/use-appear-animation.ts var useAppearAnimation = ({ once = true, margin = "-100px", delay = .1, direction = "up", animationDistance = 50, duration = .8, ease = "easeOut" } = {}) => { const ref = useRef(null); const isInView = useInView(ref, { once, margin }); const getInitialState = () => { switch (direction) { case "up": return { opacity: 0, y: animationDistance }; case "down": return { opacity: 0, y: -animationDistance }; case "left": return { opacity: 0, x: animationDistance }; case "right": return { opacity: 0, x: -animationDistance }; case "scale": return { opacity: 0, scale: .8 }; case "fade": return { opacity: 0 }; default: return { opacity: 0, y: animationDistance }; } }; const getAnimateState = () => { switch (direction) { case "up": case "down": return { opacity: 1, y: 0 }; case "left": case "right": return { opacity: 1, x: 0 }; case "scale": return { opacity: 1, scale: 1 }; case "fade": return { opacity: 1 }; default: return { opacity: 1, y: 0 }; } }; return { ref, isInView, motionProps: { initial: getInitialState(), animate: isInView ? getAnimateState() : void 0, transition: { duration, ease, delay } } }; }; //#endregion //#region src/server/lib/css.ts function cn(...inputs) { return twMerge(clsx(inputs)); } //#endregion //#region src/client/ui/header-wrapper.tsx var HeaderWrapper = ({ children, className, hideClassName, scrollPosition = 100, hideBackgroundOnScroll = true }) => { const { y } = useWindowScroll(); const isClient = useIsClient(); const isHiddenBackground = hideBackgroundOnScroll && y > scrollPosition && isClient; return /* @__PURE__ */ jsx("header", { className: cn("fixed z-30 w-full transition bg-background", className, { "bg-transparent": isHiddenBackground }, { hideClassName: isHiddenBackground && hideClassName }), children }); }; //#endregion //#region src/client/ui/infinity-list.tsx var InfinityList = ({ children, intersectionElementClassName, isNext, intersectionElement, onIntersection }) => { const intersectionRef = useRef({}); const intersection = useIntersection(intersectionRef, { root: null, rootMargin: "0px", threshold: 1 }); useEffect(() => { if (!intersection?.isIntersecting) return; onIntersection(); }, [intersection?.isIntersecting]); return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", { children }), /* @__PURE__ */ jsx("div", { className: cn(intersectionElementClassName, { hidden: !isNext }), ref: intersectionRef, children: intersectionElement() })] }); }; //#endregion export { HeaderWrapper, InfinityList, useAppearAnimation, useDisableScroll, useExtraMediumViewPort, useIsClient, useLaptopBigViewPort, useLaptopViewPort, useMobileViewPort, useSmallViewPort, useTabletViewPort };