Content was drawing under the iOS status bar and home indicator because the app uses viewport-fit=cover + black-translucent but the fixed/sticky chrome didn't reserve the safe areas. - bottom nav (MyLayout): move the 50px row into an inner box and let the bar extend by env(safe-area-inset-bottom) so icons sit above the home indicator (previously the fixed height clipped the inset padding). - top headers gain env(safe-area-inset-top): HomePageTopBar, ExploreTopBar, SearchTopBar, CartHeader, ChatHeader, and wrapper-padded HeaderWithSupport / ProfilePagesHeader (so their absolutely-positioned buttons shift too). - chat input pads bottom by the inset; chat scroll area height accounts for the taller header/input. - _index.tsx no longer overrides the viewport meta (it was dropping viewport-fit=cover on the home page); the global one in root.tsx wins. env(safe-area-inset-*) is 0 on non-notch devices, so desktop/Android render identically — only iOS gains the padding. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
import { useNavigate } from "@remix-run/react";
|
|
import { memo, useCallback } from "react";
|
|
import AppInput from "../AppInput";
|
|
import { CircleX, Search } from "lucide-react";
|
|
import { onSearch } from "~/utils/helpers";
|
|
import { Button } from "../ui/button";
|
|
|
|
interface ExploreTopBarProps {
|
|
setIsActiveSearchBar: (value: boolean) => void;
|
|
searchValue: string;
|
|
setSearchValue: (value: string) => void;
|
|
}
|
|
|
|
const ExploreTopBar = memo(function ExploreTopBar({
|
|
setIsActiveSearchBar,
|
|
searchValue,
|
|
setSearchValue,
|
|
}: ExploreTopBarProps) {
|
|
const navigate = useNavigate();
|
|
|
|
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setSearchValue(e.target.value);
|
|
}, [setSearchValue]);
|
|
|
|
const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (e.key === "Enter") {
|
|
onSearch(searchValue, navigate);
|
|
}
|
|
}, [searchValue, navigate]);
|
|
|
|
const handleFocus = useCallback(() => {
|
|
setIsActiveSearchBar(true);
|
|
}, [setIsActiveSearchBar]);
|
|
|
|
const handleSearchClick = useCallback(() => {
|
|
onSearch(searchValue, navigate);
|
|
}, [searchValue, navigate]);
|
|
|
|
const handleClearClick = useCallback(() => {
|
|
setSearchValue("");
|
|
}, [setSearchValue]);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-4 pb-4 pt-[calc(1rem_+_env(safe-area-inset-top))] px-4 w-full sticky top-0 z-30 bg-WHITE">
|
|
<div className="flex items-center justify-center gap-2 rounded-[10px] w-full relative">
|
|
<AppInput
|
|
inputIcon={Search}
|
|
inputDir="rtl"
|
|
inputTitle=""
|
|
className="bg-WHITE2 px-12 focus:border-blue-500"
|
|
inputValue={searchValue}
|
|
inputPlaceholder="جستجو..."
|
|
inputOnChange={handleChange}
|
|
onKeyDown={handleKeyDown}
|
|
inputOnFocus={handleFocus}
|
|
/>
|
|
{searchValue && (
|
|
<>
|
|
<Button
|
|
className="absolute transition-all duration-300 w-9 h-9 ease-in-out right-2 top-2 flex items-center justify-center cursor-pointer bg-blue-500 hover:bg-blue-200 rounded-lg"
|
|
variant="primary"
|
|
size="icon"
|
|
onClick={handleSearchClick}
|
|
>
|
|
<Search className="w-4 h-4 text-WHITE" />
|
|
</Button>
|
|
<div
|
|
onClick={handleClearClick}
|
|
onKeyDown={(e) => e.key === "Enter" && handleClearClick()}
|
|
role="button"
|
|
tabIndex={0}
|
|
className="absolute transition-all duration-300 ease-in-out left-2 top-3 w-6 h-6 flex items-center justify-center cursor-pointer hover:bg-gray-100 rounded-full p-1"
|
|
>
|
|
<CircleX className="w-4 h-4 text-gray-500" />
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
export default ExploreTopBar;
|