refactor and simplify search bar positioning

This commit is contained in:
Pas 2025-11-08 12:39:28 -07:00
parent 544fe97c5e
commit b6a8028eff
2 changed files with 22 additions and 57 deletions

View file

@ -1,7 +1,7 @@
import classNames from "classnames";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAsyncFn, useWindowSize } from "react-use";
import { useAsyncFn } from "react-use";
import {
base64ToBuffer,
@ -33,6 +33,7 @@ import { RegisterCalloutPart } from "@/pages/parts/settings/RegisterCalloutPart"
import { SidebarPart } from "@/pages/parts/settings/SidebarPart";
import { PageTitle } from "@/pages/parts/util/PageTitle";
import { AccountWithToken, useAuthStore } from "@/stores/auth";
import { useBannerSize } from "@/stores/banner";
import { useLanguageStore } from "@/stores/language";
import { usePreferencesStore } from "@/stores/preferences";
import { useSubtitleStore } from "@/stores/subtitles";
@ -56,30 +57,13 @@ function SettingsLayout(props: {
const { t } = useTranslation();
const { isMobile } = useIsMobile();
const searchRef = useRef<HTMLInputElement>(null);
const { width: windowWidth, height: windowHeight } = useWindowSize();
const bannerSize = useBannerSize();
// Dynamic offset calculation like HeroPart
const topSpacing = 16; // Base spacing
const [stickyOffset, setStickyOffset] = useState(topSpacing);
// Detect if running as a PWA on iOS
const isIOSPWA =
/iPad|iPhone|iPod/i.test(navigator.userAgent) &&
window.matchMedia("(display-mode: standalone)").matches;
const adjustedTopSpacing = isIOSPWA ? 60 : topSpacing;
const isLandscape = windowHeight < windowWidth && isIOSPWA;
const adjustedOffset = isLandscape ? -40 : 0;
useEffect(() => {
if (windowWidth > 1280) {
// On large screens the bar goes inline with the nav elements
setStickyOffset(adjustedTopSpacing);
} else {
// On smaller screens the bar goes below the nav elements
setStickyOffset(adjustedTopSpacing + 60 + adjustedOffset);
}
}, [adjustedOffset, adjustedTopSpacing, windowWidth]);
// Navbar height is 80px (h-20)
const navbarHeight = 80;
// On desktop: inline with navbar (same top position + 14px adjustment)
// On mobile: below navbar (navbar height + banner)
const topOffset = isMobile ? navbarHeight + bannerSize : bannerSize + 14;
return (
<WideContainer ultraWide classNames="overflow-visible">
@ -87,7 +71,7 @@ function SettingsLayout(props: {
<div
className="fixed left-0 right-0 z-50"
style={{
top: `${stickyOffset}px`,
top: `${topOffset}px`,
}}
>
<ThinContainer>

View file

@ -1,12 +1,12 @@
import classNames from "classnames";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useRef, useState } from "react";
import Sticky from "react-sticky-el";
import { useWindowSize } from "react-use";
import { SearchBarInput } from "@/components/form/SearchBar";
import { ThinContainer } from "@/components/layout/ThinContainer";
import { useSlashFocus } from "@/components/player/hooks/useSlashFocus";
import { HeroTitle } from "@/components/text/HeroTitle";
import { useIsMobile } from "@/hooks/useIsMobile";
import { useIsTV } from "@/hooks/useIsTv";
import { useRandomTranslation } from "@/hooks/useRandomTranslation";
import { useSearchQuery } from "@/hooks/useSearchQuery";
@ -44,41 +44,22 @@ export function HeroPart({
const [search, setSearch, setSearchUnFocus] = searchParams;
const [showBg, setShowBg] = useState(false);
const bannerSize = useBannerSize();
const { isMobile } = useIsMobile();
const { isTV } = useIsTV();
const stickStateChanged = useCallback(
(isFixed: boolean) => {
setShowBg(isFixed);
setIsSticky(isFixed);
},
[setShowBg, setIsSticky],
[setIsSticky],
);
const { width: windowWidth, height: windowHeight } = useWindowSize();
const { isTV } = useIsTV();
// Detect if running as a PWA on iOS
const isIOSPWA =
/iPad|iPhone|iPod/i.test(navigator.userAgent) &&
window.matchMedia("(display-mode: standalone)").matches;
const topSpacing = isIOSPWA ? 60 : 16;
const [stickyOffset, setStickyOffset] = useState(topSpacing);
const isLandscape = windowHeight < windowWidth && isIOSPWA;
const adjustedOffset = isLandscape
? -40 // landscape
: 0; // portrait
useEffect(() => {
if (windowWidth > 1280) {
// On large screens the bar goes inline with the nav elements
setStickyOffset(topSpacing);
} else {
// On smaller screens the bar goes below the nav elements
setStickyOffset(topSpacing + 60 + adjustedOffset);
}
}, [adjustedOffset, topSpacing, windowWidth]);
// Navbar height is 80px (h-20)
const navbarHeight = 80;
// On desktop: inline with navbar (same top position)
// On mobile: below navbar (navbar height + banner)
const topOffset = isMobile ? navbarHeight + bannerSize : bannerSize + 14;
const time = getTimeOfDay(new Date());
const title = randomT(`home.titles.${time}`);
@ -91,7 +72,7 @@ export function HeroPart({
<div
className={classNames(
"space-y-16 text-center",
showTitle ? "mt-44" : `mt-4`,
showTitle ? "mt-44" : "mt-4",
)}
>
{showTitle && (!isTV || search.length === 0) ? (
@ -102,9 +83,9 @@ export function HeroPart({
<div className="relative h-20 z-30">
<Sticky
topOffset={stickyOffset * -1 + bannerSize}
topOffset={-topOffset}
stickyStyle={{
paddingTop: `${stickyOffset + bannerSize}px`,
paddingTop: `${topOffset}px`,
}}
onFixedToggle={stickStateChanged}
>