// Copyright 2019-2026 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 import { faArrowLeft, faCog, faPlusCircle, faSearch } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import logo from '../assets/pjs.svg'; import { ActionContext } from '../components/index.js'; import InputFilter from '../components/InputFilter.js'; import Link from '../components/Link.js'; import { useOutsideClick, useTranslation } from '../hooks/index.js'; import { getConnectedTabsUrl } from '../messaging.js'; import { styled } from '../styled.js'; import MenuAdd from './MenuAdd.js'; import MenuSettings from './MenuSettings.js'; interface Props { children?: React.ReactNode; className?: string; onFilter?: (filter: string) => void; showAdd?: boolean; showBackArrow?: boolean; showConnectedAccounts?: boolean; showSearch?: boolean; showSettings?: boolean; smallMargin?: boolean; text?: React.ReactNode; } function Header ({ children, className = '', onFilter, showAdd, showBackArrow, showConnectedAccounts, showSearch, showSettings, smallMargin = false, text }: Props): React.ReactElement { const [isAddOpen, setShowAdd] = useState(false); const [isSettingsOpen, setShowSettings] = useState(false); const [isSearchOpen, setShowSearch] = useState(false); const [filter, setFilter] = useState(''); const [connectedTabsUrl, setConnectedTabsUrl] = useState([]); const { t } = useTranslation(); const addIconRef = useRef(null); const addMenuRef = useRef(null); const setIconRef = useRef(null); const setMenuRef = useRef(null); const isConnected = useMemo(() => connectedTabsUrl.length >= 1 , [connectedTabsUrl]); const onAction = useContext(ActionContext); useEffect(() => { if (!showConnectedAccounts) { return; } getConnectedTabsUrl() .then((tabsUrl) => setConnectedTabsUrl(tabsUrl)) .catch(console.error); }, [showConnectedAccounts]); useOutsideClick([addIconRef, addMenuRef], (): void => { isAddOpen && setShowAdd(!isAddOpen); }); useOutsideClick([setIconRef, setMenuRef], (): void => { isSettingsOpen && setShowSettings(!isSettingsOpen); }); const _toggleAdd = useCallback( () => setShowAdd((isAddOpen) => !isAddOpen), [] ); const _toggleSettings = useCallback( () => setShowSettings((isSettingsOpen) => !isSettingsOpen), [] ); const _onChangeFilter = useCallback( (filter: string) => { setFilter(filter); onFilter && onFilter(filter); }, [onFilter] ); const _toggleSearch = useCallback( (): void => { if (isSearchOpen) { _onChangeFilter(''); } setShowSearch((isSearchOpen) => !isSearchOpen); }, [_onChangeFilter, isSearchOpen] ); const _onBackArrowClick = useCallback( () => onAction('../index.js') , [onAction]); return (
{showBackArrow ? ( ) : ( ) } {text || 'polkadot{.js}'}
{showSearch && (
{showConnectedAccounts && !!isConnected && !isSearchOpen && (
•Connect Accounts
)} {isSearchOpen && ( )}
)}
{showAdd && (
)} {showSettings && (
)}
{isAddOpen && ( )} {isSettingsOpen && ( )} {children}
); } export default React.memo(styled(Header)` max-width: 100%; box-sizing: border-box; font-weight: normal; margin: 0; position: relative; margin-bottom: 25px; && { padding: 0 0 0; } > .container { display: flex; justify-content: space-between; width: 100%; border-bottom: 1px solid var(--inputBorderColor); min-height: 70px; .branding { display: flex; justify-content: center; align-items: center; color: var(--labelColor); font-family: var(--fontFamily); text-align: center; margin-left: 24px; .logo { height: 28px; width: 28px; margin: 8px 12px 12px 0; } .logoText { color: var(--textColor); font-family: var(--fontFamily); font-size: 20px; line-height: 27px; } } .popupMenus, .searchBarWrapper { align-self: center; } .connectedAccountsWrapper { flex: 1; display: flex; justify-content: center; align-items: center; } .connectedAccounts { border: 1px solid var(--inputBorderColor); border-radius: 4px; padding: 0 0.5rem; .greenDot { margin-right: 0.3rem; font-size: 1.5rem; color: var(--connectedDotColor); padding-bottom: 0.2rem; } } .searchBarWrapper { flex: 1; display: flex; justify-content: end; align-items: center; .searchIcon { margin-right: 8px; &:hover { cursor: pointer; } } } .popupToggle { display: inline-block; vertical-align: middle; &:last-child { margin-right: 24px; } &:hover { cursor: pointer; } } .inputFilter { width: 100% } .popupToggle+.popupToggle { margin-left: 8px; } } .plusIcon, .cogIcon, .searchIcon { color: var(--iconNeutralColor); &.selected { color: var(--primaryColor); } } .arrowLeftIcon { color: var(--labelColor); margin-right: 1rem; cursor: pointer; } &.smallMargin { margin-bottom: 15px; } `);