From 2cd098b4eea8cd43c5322f3265b4126488406bd2 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Dec 2025 05:00:18 +0000 Subject: [PATCH] feat: Expand search bar to full width on focus with collapsible header When the search input is focused on mobile: - The search bar expands to take full width - Logo and right section buttons are hidden - Ellipsis menu appears with all actions (Create Climb, Login/Logout) - Close button replaces the search filter button to collapse back Changes: - Add focus/blur handlers to SearchClimbNameInput - Track expanded state in header - Add CSS classes for mobile-only visibility toggling --- .../components/board-page/header.module.css | 16 +++++ .../web/app/components/board-page/header.tsx | 62 +++++++++++++------ .../search-drawer/search-climb-name-input.tsx | 17 ++++- 3 files changed, 74 insertions(+), 21 deletions(-) diff --git a/packages/web/app/components/board-page/header.module.css b/packages/web/app/components/board-page/header.module.css index f0c346b8..e336390e 100644 --- a/packages/web/app/components/board-page/header.module.css +++ b/packages/web/app/components/board-page/header.module.css @@ -10,6 +10,14 @@ display: none; } +.hideOnMobileExpanded { + display: flex; +} + +.showOnMobileExpanded { + display: none; +} + .header { touch-action: manipulation; } @@ -32,4 +40,12 @@ .mobileMenuButton { display: flex; } + + .hideOnMobileExpanded { + display: none; + } + + .showOnMobileExpanded { + display: flex; + } } diff --git a/packages/web/app/components/board-page/header.tsx b/packages/web/app/components/board-page/header.tsx index 769bacb5..2f9b3d20 100644 --- a/packages/web/app/components/board-page/header.tsx +++ b/packages/web/app/components/board-page/header.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; -import { Flex, Button, Dropdown, MenuProps } from 'antd'; +import React, { useState, useRef } from 'react'; +import { Flex, Button, Dropdown, MenuProps, InputRef } from 'antd'; import { Header } from 'antd/es/layout/layout'; import { useSession, signIn, signOut } from 'next-auth/react'; import SearchButton from '../search-drawer/search-button'; @@ -12,7 +12,7 @@ import { generateLayoutSlug, generateSizeSlug, generateSetSlug } from '@/app/lib import { ShareBoardButton } from './share-button'; import { useBoardProvider } from '../board-provider/board-provider-context'; import { useQueueContext } from '../graphql-queue'; -import { UserOutlined, LogoutOutlined, LoginOutlined, PlusOutlined, MoreOutlined } from '@ant-design/icons'; +import { UserOutlined, LogoutOutlined, LoginOutlined, PlusOutlined, MoreOutlined, CloseOutlined } from '@ant-design/icons'; import AngleSelector from './angle-selector'; import Logo from '../brand/logo'; import styles from './header.module.css'; @@ -26,12 +26,23 @@ export default function BoardSeshHeader({ boardDetails, angle }: BoardSeshHeader const { data: session } = useSession(); const { logout } = useBoardProvider(); const { currentClimb } = useQueueContext(); + const [isSearchExpanded, setIsSearchExpanded] = useState(false); + const searchInputRef = useRef(null); const handleSignOut = () => { signOut(); logout(); // Also logout from board provider }; + const handleSearchFocus = () => { + setIsSearchExpanded(true); + }; + + const handleSearchCollapse = () => { + setIsSearchExpanded(false); + searchInputRef.current?.blur(); + }; + const userMenuItems: MenuProps['items'] = [ { key: 'logout', @@ -57,6 +68,12 @@ export default function BoardSeshHeader({ boardDetails, angle }: BoardSeshHeader label: 'Login', onClick: () => signIn(), }] : []), + ...(session?.user ? [{ + key: 'logout', + icon: , + label: 'Logout', + onClick: handleSignOut, + }] : []), ]; return (
- {/* Logo - Fixed to left */} - + {/* Logo - Fixed to left, hidden on mobile when search is expanded */} + {/* Center Section - Mobile only */}
- +
- + {isSearchExpanded ? ( +
- {/* Right Section */} - + {/* Right Section - hidden on mobile when search is expanded */} + {angle !== undefined && } {/* Desktop: show Create Climb button */} @@ -120,16 +148,14 @@ export default function BoardSeshHeader({ boardDetails, angle }: BoardSeshHeader )} - - {/* Mobile: meatball menu for Create Climb and Login */} - {mobileMenuItems.length > 0 && ( -
- -
- )}
+ + {/* Mobile: meatball menu - always show when search is expanded, otherwise only when there are items */} +
+ +
diff --git a/packages/web/app/components/search-drawer/search-climb-name-input.tsx b/packages/web/app/components/search-drawer/search-climb-name-input.tsx index 29cd267d..7b70a05d 100644 --- a/packages/web/app/components/search-drawer/search-climb-name-input.tsx +++ b/packages/web/app/components/search-drawer/search-climb-name-input.tsx @@ -1,15 +1,24 @@ 'use client'; -import React from 'react'; -import { Input } from 'antd'; +import React, { useRef } from 'react'; +import { Input, InputRef } from 'antd'; import { SearchOutlined } from '@ant-design/icons'; import { useUISearchParams } from '../queue-control/ui-searchparams-provider'; -const SearchClimbNameInput = () => { +interface SearchClimbNameInputProps { + onFocus?: () => void; + onBlur?: () => void; + inputRef?: React.RefObject; +} + +const SearchClimbNameInput = ({ onFocus, onBlur, inputRef }: SearchClimbNameInputProps) => { const { uiSearchParams, updateFilters } = useUISearchParams(); + const localRef = useRef(null); + const ref = inputRef || localRef; return ( } allowClear @@ -19,6 +28,8 @@ const SearchClimbNameInput = () => { }); }} value={uiSearchParams.name} + onFocus={onFocus} + onBlur={onBlur} /> ); };