From cbd95bb0c5a4031f8b2107654f15e828b3b9a00c Mon Sep 17 00:00:00 2001 From: Tania Markina Date: Fri, 2 Oct 2026 13:48:40 +0200 Subject: [PATCH 1/9] single-row navbar with in-bar search and cluster dropdown --- app/components/Navbar.tsx | 152 +++++----- app/components/navbar/NavGroup.tsx | 123 ++++++++ app/components/navbar/NavSearch.tsx | 200 +++++++++++++ app/components/navbar/shared.tsx | 74 +++++ app/components/shared/focus-rule.ts | 27 ++ app/components/shared/ui/dialog.tsx | 6 +- app/entities/cluster/index.ts | 1 + app/features/cluster-switcher/client.ts | 6 + app/features/cluster-switcher/index.ts | 2 +- .../lib/__tests__/cluster-storage.spec.ts | 158 ++++++++-- .../cluster-switcher/lib/cluster-storage.ts | 68 ++--- .../lib/endpoint-provenance.ts | 20 ++ .../__tests__/use-custom-url-draft.spec.tsx | 73 ++++- .../model/use-custom-url-draft.ts | 65 ++--- .../model/use-saved-clusters.ts | 24 ++ .../cluster-switcher/ui/ClusterDropdown.tsx | 102 +++++++ .../cluster-switcher/ui/ClusterMenu.tsx | 270 ++++++++++++++++++ .../ui/ClusterStatusButton.tsx | 87 ------ .../ui/CustomUrlConsentDialog.tsx | 184 +++++++----- .../cluster-switcher/ui/EndpointForm.tsx | 87 ++++++ .../cluster-switcher/ui/SavedClusterList.tsx | 43 +-- .../cluster-switcher/ui/SavedEndpointRow.tsx | 193 +++++++++++++ ...ClusterStatusButton.responsive.stories.tsx | 33 --- .../ClusterStatusButton.stories.tsx | 99 ------- .../ui/__tests__/ClusterModal.spec.tsx | 6 +- .../ui/__tests__/ClusterStatusButton.spec.tsx | 106 ------- .../__tests__/CustomUrlConsentDialog.spec.tsx | 6 +- .../PendingCustomUrlConsent.spec.tsx | 4 +- .../ui/cluster-row-classes.ts | 15 + .../cluster-switcher/ui/provenance-mark.tsx | 36 +++ app/features/search/ui/BaseSearch.tsx | 48 ++-- app/features/search/ui/SearchFilters.tsx | 14 +- app/features/search/ui/SearchGroupHeading.tsx | 6 +- app/features/search/ui/SearchResultItem.tsx | 10 +- app/layout.tsx | 11 +- app/shared/lib/scrollToTop.ts | 4 + tailwind.config.ts | 5 +- 37 files changed, 1700 insertions(+), 668 deletions(-) create mode 100644 app/components/navbar/NavGroup.tsx create mode 100644 app/components/navbar/NavSearch.tsx create mode 100644 app/components/navbar/shared.tsx create mode 100644 app/components/shared/focus-rule.ts create mode 100644 app/features/cluster-switcher/client.ts create mode 100644 app/features/cluster-switcher/lib/endpoint-provenance.ts create mode 100644 app/features/cluster-switcher/model/use-saved-clusters.ts create mode 100644 app/features/cluster-switcher/ui/ClusterDropdown.tsx create mode 100644 app/features/cluster-switcher/ui/ClusterMenu.tsx delete mode 100644 app/features/cluster-switcher/ui/ClusterStatusButton.tsx create mode 100644 app/features/cluster-switcher/ui/EndpointForm.tsx create mode 100644 app/features/cluster-switcher/ui/SavedEndpointRow.tsx delete mode 100644 app/features/cluster-switcher/ui/__stories__/ClusterStatusButton.responsive.stories.tsx delete mode 100644 app/features/cluster-switcher/ui/__stories__/ClusterStatusButton.stories.tsx delete mode 100644 app/features/cluster-switcher/ui/__tests__/ClusterStatusButton.spec.tsx create mode 100644 app/features/cluster-switcher/ui/cluster-row-classes.ts create mode 100644 app/features/cluster-switcher/ui/provenance-mark.tsx create mode 100644 app/shared/lib/scrollToTop.ts diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx index c3867c922..dab2faa74 100644 --- a/app/components/Navbar.tsx +++ b/app/components/Navbar.tsx @@ -1,107 +1,87 @@ 'use client'; import { cn } from '@components/shared/utils'; -import { ClusterStatusButton } from '@features/cluster-switcher'; -import Logo from '@img/logos-solana/dark-explorer-logo.svg'; -import { useDisclosure } from '@mantine/hooks'; -import { useClusterPath } from '@utils/url'; -import Image from 'next/image'; -import Link from 'next/link'; -import { useSelectedLayoutSegment, useSelectedLayoutSegments } from 'next/navigation'; -import React, { ReactNode } from 'react'; -import { Menu } from 'react-feather'; +import { ClusterDropdown } from '@features/cluster-switcher'; +import React, { type ReactNode, useRef, useState } from 'react'; -import { ExternalLink } from '@/app/components/shared/ui/external-link'; -import { NavbarItem, NavbarLink, NavbarList } from '@/app/shared/ui/Navbar'; +import { NavLinks, NavMenu } from './navbar/NavGroup'; +import { NavSearch } from './navbar/NavSearch'; +import { BAR_CLASSES, BrandLockup, GUTTER_CLASSES, useNavRoutes } from './navbar/shared'; export interface INavbarProps { children?: ReactNode; } export function Navbar({ children }: INavbarProps) { - const [navOpened, navHandlers] = useDisclosure(false); - const homePath = useClusterPath({ pathname: '/' }); - const featureGatesPath = useClusterPath({ pathname: '/feature-gates' }); - const inspectorPath = useClusterPath({ pathname: '/tx/inspector' }); - const selectedLayoutSegment = useSelectedLayoutSegment(); - const selectedLayoutSegments = useSelectedLayoutSegments(); + const [searchOpened, setSearchOpened] = useState(false); + const [clusterOpened, setClusterOpened] = useState(false); + const [menuOpened, setMenuOpened] = useState(false); + const routes = useNavRoutes(); + const slotRef = useRef(null); + + const onSearchOpenChange = (open: boolean) => { + if (open) { + setMenuOpened(false); + setClusterOpened(false); + } + setSearchOpened(open); + }; + const onClusterOpenChange = (open: boolean) => { + if (open) { + setMenuOpened(false); + setSearchOpened(false); + } + setClusterOpened(open); + }; + const onMenuOpenChange = (open: boolean) => { + if (open) { + setClusterOpened(false); + setSearchOpened(false); + } + setMenuOpened(open); + }; return ( -