Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 34 additions & 16 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import CategoriesSidebar from './components/Sidebar/Categories';
import CommentsSidebar from './components/Sidebar/Comments';
import PagesSidebar from './components/Sidebar/Pages';
import PostsSidebar from './components/Sidebar/Posts';
import SuperMenu from './components/SuperMenu';
import SuperOverlay from './components/Super';
import { RenderPlugins } from './plugins';

import './App.css';
Expand All @@ -36,6 +36,7 @@ class App extends Component {
isShowingWritePost: false,
showChanges: false,
};
this.headerRef = React.createRef();
}
onLogOut() {
window.location.href = '/wp-login.php?action=logout';
Expand Down Expand Up @@ -71,6 +72,19 @@ class App extends Component {
}
}

onToggleSuper = () => {
if ( this.props.showingSuper ) {
this.props.onHideSuperSidebar();
} else {
this.props.onShowSuperSidebar();
}
}

onShowChanges = () => {
this.setState( { showChanges: true } );
this.props.onHideSuperSidebar();
}

onClickWritePost() {
this.setState( { isShowingWritePost: true } );
}
Expand Down Expand Up @@ -144,21 +158,25 @@ class App extends Component {
return (
<div className="App">
<OverlayContainer />
<SuperMenu
visible={ this.props.showingSuper }
onClose={ this.props.onHideSuperSidebar }
onSearch={ search => this.onSearch( search ) }
onShowChanges={ () => {
this.setState( { showChanges: true } );
this.props.onHideSuperSidebar();
} }
/>
<Header
onLogOut={ () => this.onLogOut() }
onWritePost={ () => this.onClickWritePost() }
onSearch={ search => this.onSearch( search ) }
onShowSuper={ this.props.onShowSuperSidebar }
/>
{ /* Keep the header above the overlay, so its search input stays usable. */ }
<div
ref={ this.headerRef }
className={ this.props.showingSuper ? 'relative z-30' : undefined }
>
<Header
superActive={ this.props.showingSuper }
onLogOut={ () => this.onLogOut() }
onWritePost={ () => this.onClickWritePost() }
onSearch={ search => this.onSearch( search ) }
onShowSuper={ this.onToggleSuper }
/>
</div>
{ this.props.showingSuper && (
<SuperOverlay
headerRef={ this.headerRef }
onShowChanges={ this.onShowChanges }
/>
) }
<SessionExpiredWarning />
<div className="Outer grid grid-cols-[min(30%,360px)_auto] gap-12">
{ this.renderSidebar() }
Expand Down
2 changes: 1 addition & 1 deletion src/_print.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
.Sidebar-Container,
.Header,
.Overlay,
.SuperMenu,
.Super,
.Post__actions,
.Post-Summary-actions button,
.Post-Summary-people,
Expand Down
12 changes: 12 additions & 0 deletions src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const SHOW_SIDEBAR_POSTS = 'SHOW_SIDEBAR_POSTS';
export const HIDE_SIDEBAR = 'HIDE_SIDEBAR';
export const SHOW_SUPER_SIDEBAR = 'SHOW_SUPER_SIDEBAR';
export const HIDE_SUPER_SIDEBAR = 'HIDE_SUPER_SIDEBAR';
export const SET_SUPER_SEARCH = 'SET_SUPER_SEARCH';
export const ENABLE_BETA_FEATURE = 'ENABLE_BETA_FEATURE';
export const DISABLE_BETA_FEATURE = 'DISABLE_BETA_FEATURE';
export const SET_DEFAULT_POST_VIEW = 'SET_DEFAULT_POST_VIEW';
Expand Down Expand Up @@ -39,6 +40,17 @@ export const showMetaSidebar = () => ( { type: SHOW_META_SIDEBAR } );
export const showSuperSidebar = () => ( { type: SHOW_SUPER_SIDEBAR } );
export const hideSuperSidebar = () => ( { type: HIDE_SUPER_SIDEBAR } );

/**
* Set the term for network-wide search in the super overlay.
*
* @param {string} search Search term.
* @returns {object} Action.
*/
export const setSuperSearch = search => ( {
type: SET_SUPER_SEARCH,
search,
} );

export const enableBetaFeature = feature => ( {
type: ENABLE_BETA_FEATURE,
feature,
Expand Down
15 changes: 15 additions & 0 deletions src/changelog.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,21 @@ export const changes = [
</React.Fragment>
),
},
{
date: '2026-09-03',
title: 'Switch Sites & Search the Network',
content: () => (
<React.Fragment>
<p>The button in the top left now opens an overview of
every site on your network, so you can jump between
them with a single click.</p>
<p>While it's open, the search bar searches posts and
comments across all of your sites at once. Narrow the
results down by site, type or date, and sort them by
relevance or by date.</p>
</React.Fragment>
),
},
];

export function getChanges( lastView ) {
Expand Down
58 changes: 48 additions & 10 deletions src/components/Header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,69 @@ import { Slot } from 'react-slot-fill';

import { withCurrentUser } from '../../hocs';
import SearchInput from '../SearchInput';
import NetworkSearchInput from '../Super/SearchInput';
import { getNetwork } from '../Super/util';

import CurrentUser from './CurrentUser';
import HeaderButton from './HeaderButton';
import Logo from './Logo';

const SUPER_BUTTON_CLASSES = [
'w-[min(30%,360px)] text-white p-0 pr-5 flex items-center border-none font-inherit text-base cursor-pointer',
'hover:border-none hover:text-white/60 focus:border-none focus:text-white/60',
'[&:hover_.hm-logo]:opacity-60 [&:focus_.hm-logo]:opacity-60',
].join( ' ' );

export class Header extends Component {
render() {
const { superActive } = this.props;
const network = getNetwork();

// Tint the header with the brand colour while the search is network-wide.
// The tint sits on white, as the header needs to stay opaque over the overlay.
const networkSearch = superActive && !! network;

return (
<div className="bg-hm-light-grey flex-1">
<div className="flex flex-row pr-5 max-[600px]:pr-1.5">
<div
className={ [
'Header flex-1 transition-colors duration-150',
networkSearch ? 'bg-white' : 'bg-hm-light-grey',
].join( ' ' ) }
>
<div
className={ [
'flex flex-row pr-5 max-[600px]:pr-1.5 transition-colors duration-150',
networkSearch ? 'bg-brand/5' : '',
].filter( Boolean ).join( ' ' ) }
>
<button
className="bg-brand w-[min(30%,360px)] text-white p-0 pr-5 flex items-center border-none font-inherit text-base cursor-pointer hover:border-none hover:text-white/60 focus:border-none focus:text-white/60 [&:hover_.hm-logo]:opacity-60 [&:focus_.hm-logo]:opacity-60"
aria-expanded={ superActive }
className={ [
SUPER_BUTTON_CLASSES,
superActive ? 'bg-brand-dark' : 'bg-brand',
].join( ' ' ) }
title={ network ? 'Switch site or search the network' : 'Site overview' }
type="button"
onClick={ this.props.onShowSuper }
>
<Logo />

{ window.H2Data.network ? window.H2Data.network.name : window.H2Data.site.name }
{ network ? network.name : window.H2Data.site.name }
</button>

<Slot name="Header.buttons" />

<SearchInput
className="hidden sm:block"
value={ this.props.searchValue }
onSearch={ this.props.onSearch }
/>
{ networkSearch ? (
<NetworkSearchInput
autoFocus
className="hidden sm:block"
/>
) : (
<SearchInput
className="hidden sm:block"
value={ this.props.searchValue }
onSearch={ this.props.onSearch }
/>
) }

<Slot name="Header.secondary_buttons" />

Expand All @@ -53,13 +88,16 @@ export class Header extends Component {

Header.defaultProps = {
searchValue: '',
superActive: false,
};

Header.propTypes = {
searchValue: PropTypes.string,
superActive: PropTypes.bool,
onLogOut: PropTypes.func.isRequired,
onWritePost: PropTypes.func.isRequired,
onSearch: PropTypes.func.isRequired,
onShowSuper: PropTypes.func.isRequired,
};

export default withCurrentUser( Header );
20 changes: 20 additions & 0 deletions src/components/Header/index.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,23 @@ export const Default = () => (
{ ...defaultProps }
/>
);

export const SuperActive = () => (
<Header
{ ...defaultProps }
superActive
/>
);

export const OnNetwork = () => {
window.H2Data.network = {
name: 'Storybook Network',
};

return (
<Header
{ ...defaultProps }
superActive
/>
);
};
30 changes: 21 additions & 9 deletions src/components/SearchInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ import { decodeEntities } from '../util';

import Link from './Link';

export const FORM_CLASSES = 'SearchInput flex-1 relative self-stretch';
export const INPUT_CLASSES = 'w-full text-lg border-2 border-hm-border-color rounded-sm px-2 py-1 md:py-2 md:leading-5 placeholder:italic';

/**
* Get the classes for the wrapper around the search input.
*
* The background is left to the caller: the site search needs an opaque one
* to cover its results dropdown, while the network search shows the header's.
*
* @param {boolean} small Whether the input is in a small (unpadded) context.
* @returns {string} Class names.
*/
export const getWrapClasses = small => [
'SearchInput__wrap',
'flex items-center h-full relative z-11',
small ? '' : 'px-5',
].filter( Boolean ).join( ' ' );

export class Results extends React.Component {
state = {
selected: -1,
Expand Down Expand Up @@ -204,18 +222,12 @@ class SearchInput extends React.Component {

return (
<form
className={ `SearchInput flex-1 relative self-stretch ${ this.props.className || '' }` }
className={ `${ FORM_CLASSES } ${ this.props.className || '' }` }
onSubmit={ this.onSubmit }
>
<div
className={ [
'SearchInput__wrap',
'flex items-center h-full bg-hm-light-grey relative z-11',
this.props.small ? '' : 'px-5',
].filter( Boolean ).join( ' ' ) }
>
<div className={ `${ getWrapClasses( this.props.small ) } bg-hm-light-grey` }>
<input
className="w-full text-lg border-2 border-hm-border-color rounded-sm px-2 py-1 md:py-2 md:leading-5 placeholder:italic"
className={ INPUT_CLASSES }
type="search"
placeholder="Search..."
ref={ ref => this.inputEl = ref }
Expand Down
Loading
Loading