mirror of
https://github.com/p-stream/p-stream.git
synced 2026-05-03 14:29:16 +00:00
refactor and simplify search bar positioning
This commit is contained in:
parent
544fe97c5e
commit
b6a8028eff
2 changed files with 22 additions and 57 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useAsyncFn, useWindowSize } from "react-use";
|
import { useAsyncFn } from "react-use";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
base64ToBuffer,
|
base64ToBuffer,
|
||||||
|
|
@ -33,6 +33,7 @@ import { RegisterCalloutPart } from "@/pages/parts/settings/RegisterCalloutPart"
|
||||||
import { SidebarPart } from "@/pages/parts/settings/SidebarPart";
|
import { SidebarPart } from "@/pages/parts/settings/SidebarPart";
|
||||||
import { PageTitle } from "@/pages/parts/util/PageTitle";
|
import { PageTitle } from "@/pages/parts/util/PageTitle";
|
||||||
import { AccountWithToken, useAuthStore } from "@/stores/auth";
|
import { AccountWithToken, useAuthStore } from "@/stores/auth";
|
||||||
|
import { useBannerSize } from "@/stores/banner";
|
||||||
import { useLanguageStore } from "@/stores/language";
|
import { useLanguageStore } from "@/stores/language";
|
||||||
import { usePreferencesStore } from "@/stores/preferences";
|
import { usePreferencesStore } from "@/stores/preferences";
|
||||||
import { useSubtitleStore } from "@/stores/subtitles";
|
import { useSubtitleStore } from "@/stores/subtitles";
|
||||||
|
|
@ -56,30 +57,13 @@ function SettingsLayout(props: {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isMobile } = useIsMobile();
|
const { isMobile } = useIsMobile();
|
||||||
const searchRef = useRef<HTMLInputElement>(null);
|
const searchRef = useRef<HTMLInputElement>(null);
|
||||||
const { width: windowWidth, height: windowHeight } = useWindowSize();
|
const bannerSize = useBannerSize();
|
||||||
|
|
||||||
// Dynamic offset calculation like HeroPart
|
// Navbar height is 80px (h-20)
|
||||||
const topSpacing = 16; // Base spacing
|
const navbarHeight = 80;
|
||||||
const [stickyOffset, setStickyOffset] = useState(topSpacing);
|
// On desktop: inline with navbar (same top position + 14px adjustment)
|
||||||
|
// On mobile: below navbar (navbar height + banner)
|
||||||
// Detect if running as a PWA on iOS
|
const topOffset = isMobile ? navbarHeight + bannerSize : bannerSize + 14;
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WideContainer ultraWide classNames="overflow-visible">
|
<WideContainer ultraWide classNames="overflow-visible">
|
||||||
|
|
@ -87,7 +71,7 @@ function SettingsLayout(props: {
|
||||||
<div
|
<div
|
||||||
className="fixed left-0 right-0 z-50"
|
className="fixed left-0 right-0 z-50"
|
||||||
style={{
|
style={{
|
||||||
top: `${stickyOffset}px`,
|
top: `${topOffset}px`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ThinContainer>
|
<ThinContainer>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useRef, useState } from "react";
|
||||||
import Sticky from "react-sticky-el";
|
import Sticky from "react-sticky-el";
|
||||||
import { useWindowSize } from "react-use";
|
|
||||||
|
|
||||||
import { SearchBarInput } from "@/components/form/SearchBar";
|
import { SearchBarInput } from "@/components/form/SearchBar";
|
||||||
import { ThinContainer } from "@/components/layout/ThinContainer";
|
import { ThinContainer } from "@/components/layout/ThinContainer";
|
||||||
import { useSlashFocus } from "@/components/player/hooks/useSlashFocus";
|
import { useSlashFocus } from "@/components/player/hooks/useSlashFocus";
|
||||||
import { HeroTitle } from "@/components/text/HeroTitle";
|
import { HeroTitle } from "@/components/text/HeroTitle";
|
||||||
|
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||||
import { useIsTV } from "@/hooks/useIsTv";
|
import { useIsTV } from "@/hooks/useIsTv";
|
||||||
import { useRandomTranslation } from "@/hooks/useRandomTranslation";
|
import { useRandomTranslation } from "@/hooks/useRandomTranslation";
|
||||||
import { useSearchQuery } from "@/hooks/useSearchQuery";
|
import { useSearchQuery } from "@/hooks/useSearchQuery";
|
||||||
|
|
@ -44,41 +44,22 @@ export function HeroPart({
|
||||||
const [search, setSearch, setSearchUnFocus] = searchParams;
|
const [search, setSearch, setSearchUnFocus] = searchParams;
|
||||||
const [showBg, setShowBg] = useState(false);
|
const [showBg, setShowBg] = useState(false);
|
||||||
const bannerSize = useBannerSize();
|
const bannerSize = useBannerSize();
|
||||||
|
const { isMobile } = useIsMobile();
|
||||||
|
const { isTV } = useIsTV();
|
||||||
|
|
||||||
const stickStateChanged = useCallback(
|
const stickStateChanged = useCallback(
|
||||||
(isFixed: boolean) => {
|
(isFixed: boolean) => {
|
||||||
setShowBg(isFixed);
|
setShowBg(isFixed);
|
||||||
setIsSticky(isFixed);
|
setIsSticky(isFixed);
|
||||||
},
|
},
|
||||||
[setShowBg, setIsSticky],
|
[setIsSticky],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { width: windowWidth, height: windowHeight } = useWindowSize();
|
// Navbar height is 80px (h-20)
|
||||||
|
const navbarHeight = 80;
|
||||||
const { isTV } = useIsTV();
|
// On desktop: inline with navbar (same top position)
|
||||||
|
// On mobile: below navbar (navbar height + banner)
|
||||||
// Detect if running as a PWA on iOS
|
const topOffset = isMobile ? navbarHeight + bannerSize : bannerSize + 14;
|
||||||
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]);
|
|
||||||
|
|
||||||
const time = getTimeOfDay(new Date());
|
const time = getTimeOfDay(new Date());
|
||||||
const title = randomT(`home.titles.${time}`);
|
const title = randomT(`home.titles.${time}`);
|
||||||
|
|
@ -91,7 +72,7 @@ export function HeroPart({
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"space-y-16 text-center",
|
"space-y-16 text-center",
|
||||||
showTitle ? "mt-44" : `mt-4`,
|
showTitle ? "mt-44" : "mt-4",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{showTitle && (!isTV || search.length === 0) ? (
|
{showTitle && (!isTV || search.length === 0) ? (
|
||||||
|
|
@ -102,9 +83,9 @@ export function HeroPart({
|
||||||
|
|
||||||
<div className="relative h-20 z-30">
|
<div className="relative h-20 z-30">
|
||||||
<Sticky
|
<Sticky
|
||||||
topOffset={stickyOffset * -1 + bannerSize}
|
topOffset={-topOffset}
|
||||||
stickyStyle={{
|
stickyStyle={{
|
||||||
paddingTop: `${stickyOffset + bannerSize}px`,
|
paddingTop: `${topOffset}px`,
|
||||||
}}
|
}}
|
||||||
onFixedToggle={stickStateChanged}
|
onFixedToggle={stickStateChanged}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue